CREATE TABLE `t_districts` (
`adcode` bigint NOT NULL COMMENT '主键(区域编码)\r\n',
`pid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '父级区域编码',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '行政单位名称',
`citycode` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '城市编码',
`level` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '行政单位类别 省 市 区/县 街道/乡镇',
`center` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '经度,纬度',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`adcode`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ehe.core.pojo.domain.StringTenantEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;
import javax.persistence.Column;
import javax.persistence.Transient;
import java.util.LinkedList;
import java.util.List;
@ApiModel(value = "行政区域", description = "行政区域")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@Accessors(chain = true)
@TableName(value = "t_districts")
public class Districts{
/** 城市编码 */
@ApiModelProperty(value = "城市编码", position = 10, required = true)
@Column(length = 255)
private String citycode;
/** 区域编码 */
@ApiModelProperty(value = "区域编码", position = 11, required = true)
@Column(length = 255)
private String adcode;
/** 城市编码 */
@ApiModelProperty(value = "城市名称", position = 12, required = true)
@Column(length = 255)
private String name;
/** 区域中心点 */
@ApiModelProperty(value = "区域中心点", position = 13, required = true)
@Column(length = 255)
private String center;
/** 行政区划级别 */
@ApiModelProperty(value = "行政区划级别", position = 14, required = true)
@Column(length = 60)
private String level;
/** 上级区域编码 */
@ApiModelProperty(value = "上级区域编码", position = 15, required = true)
@Column(length = 60)
private String pid;
@Transient
@TableField(exist = false)
private boolean hasChildren;
@TableField(exist = false)
List<Districts> childrenList = new LinkedList<>();
}
public List<DistrictsVO> getAreaTree(String pid) {
List<Districts> districtsList = districtsService.selectAll();
districtsList.stream().filter(districts -> "0".equals(districts.getPid()))
.peek(districts -> addChildren(districts,districtsList))
.collect(Collectors.toList());
return BeanConverts.convert(districtsList,DistrictsVO.class);
return BeanConverts.convert(districtsList,DistrictsVO.class);
}
private void addChildren(Districts districts, List<Districts> districtsList) {
districts.setChildrenList(
districtsList.stream().filter(vo -> districts.getAdcode().equals(vo.getPid()))
.peek(children -> addChildren(children,districtsList)).collect(Collectors.toList())
);
}
import com.baomidou.mybatisplus.annotation.TableField;
import com.ehe.core.pojo.vo.StringVO;
import com.ehe.elder.domain.Districts;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
/**
* 行政区域 VO
* @author rambo
*/
@ApiModel(value = "行政区域 VO", description = "行政区域 VO")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@Accessors(chain = true)
public class DistrictsVO{
/** 城市编码 */
@ApiModelProperty(value = "城市编码", position = 10, required = true)
private String citycode;
/** 区域编码 */
@ApiModelProperty(value = "区域编码", position = 11, required = true)
private String adcode;
/** 城市名称 */
@ApiModelProperty(value = "城市名称", position = 12, required = true)
private String name;
/** 区域中心点 */
@ApiModelProperty(value = "区域中心点", position = 13, required = true)
private String center;
/** 行政区划级别 */
@ApiModelProperty(value = "行政区划级别", position = 14, required = true)
private String level;
/** 上级区域编码 */
@ApiModelProperty(value = "上级区域编码", position = 15, required = true)
private String pid;
@TableField(exist = false)
List<Districts> childrenList = new LinkedList<>();
}
二、工具类
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import javax.annotation.Nonnull;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* bean 转换 工具类
* @author jerry
*/
@Slf4j
public final class BeanConverts {
private BeanConverts() {
}
/**
* BeanUtils.copyProperties 增强
* 拷贝时,source 值为空则不拷贝
* @param source 源数据
* @param target 目标数据
* @param ignoreProperties 忽略属性
*/
public static void copyPropertiesIgnoreNull(@Nonnull Object source, @Nonnull Object target, String... ignoreProperties) {
Set<String> ignoreSet = new HashSet<>();
if (ignoreProperties != null) {
ignoreSet.addAll(Arrays.asList(ignoreProperties));
}
ignoreSet.addAll(getObjectNullFieldName(source));
BeanUtils.copyProperties(source, target, ignoreSet.toArray(new String[0]));
}
/**
* 获取对象字段值为null 的字段名称集合
* @param object 源数据
* @return 字段名称集合
*/
public static List<String> getObjectNullFieldName(@Nonnull Object object) {
BeanMap beanMap = BeanMap.create(object);
List<String> fieldNameList = new ArrayList<>();
for (Object key : beanMap.keySet()) {
if (beanMap.get(key) == null) {
fieldNameList.add(key.toString());
}
}
return fieldNameList;
}
/**
* 将对象转换为map
* 不转换空值字段
* @param obj 对象
* @return 键值对
*/
public static Map<String, Object> objToMapIgnoreNull(Object obj) {
return objToMap(obj, true);
}
/**
* 将对象转换为map
* @param obj 对象
* @return 键值对
*/
public static Map<String, Object> objToMap(Object obj) {
return objToMap(obj, false);
}
/**
* 将对象转换为map
* @param obj 对象
* @param ignoreNull 忽略空值字段
* @return 键值对
*/
private static Map<String, Object> objToMap(Object obj, boolean ignoreNull) {
Map<String, Object> map = new HashMap<>(10);
if (obj != null) {
BeanMap beanMap = BeanMap.create(obj);
for (Object key : beanMap.keySet()) {
Object value = beanMap.get(key);
if (ignoreNull && value == null) {
continue;
}
map.put(key.toString(), value);
}
}
return map;
}
/**
* bean 转换
* @param obj 源对象
* @param clazz 目标类型
* @param 泛型
* @return 目标对象
*/
public static <T> T convert(Object obj, @Nonnull Class<T> clazz) {
if (obj == null) {
return null;
}
T t = BeanUtils.instantiateClass(clazz);
BeanUtils.copyProperties(obj, t);
return t;
}
/**
* bean 转换
* @param optional 源对象
* @param clazz 目标类型
* @param 泛型
* @return 目标对象
*/
public static <T> Optional<T> convert(@Nonnull Optional<?> optional, @Nonnull Class<T> clazz) {
return optional.map(obj -> convert(obj, clazz));
}
/**
* bean 集合转换
* @param collection 源对象集合
* @param clazz 目标类型
* @param 泛型
* @return 目标对象集合
*/
@Nonnull
public static <T> List<T> convert(@Nonnull Collection<?> collection, @Nonnull Class<T> clazz) {
return collection.stream().map(item -> convert(item, clazz)).collect(Collectors.toList());
}
/**
* bean 集合转换
* @param iterable 实体集合
* @param clazz 目标类型
* @param 泛型
* @return 目标对象集合
*/
@Nonnull
public static <T> List<T> convert(@Nonnull Iterable<?> iterable, @Nonnull Class<T> clazz) {
return convert(StreamSupport.stream(iterable.spliterator(), false), clazz);
}
/**
* bean 集合转换
* @param stream 实体集合流
* @param clazz 目标类型
* @param 泛型
* @return 目标对象集合
*/
@Nonnull
public static <T> List<T> convert(@Nonnull Stream<?> stream, @Nonnull Class<T> clazz) {
return stream.map(entity -> convert(entity, clazz)).collect(Collectors.toList());
}
/**
* 实体分页转换
* @param page 实体分页
* @param clazz 目标对象
* @param 泛型
* @return 目标分页
*/
@Nonnull
public static <T> Page<T> convert(@Nonnull Page<?> page, @Nonnull Class<T> clazz) {
List<T> list = convert(page.getContent(), clazz);
return new PageImpl<>(list, page.getPageable(), page.getTotalElements());
}
/**
* map 转对象
* @param clazz 目标类型
* @param collection 键值对集合
* @param 泛型
* @return 目标对象集合
*/
@Nonnull
public static <T> List<T> mapToObj(@Nonnull Class<T> clazz, @Nonnull Collection<Map<String, Object>> collection) {
return collection.stream().map(map -> BeanConverts.mapToObj(clazz, map)).collect(Collectors.toList());
}
/**
* map 转对象
* @param clazz 目标类型
* @param map 键值对
* @param 泛型
* @return 目标对象
*/
public static <T> T mapToObj(@Nonnull Class<T> clazz, @Nonnull Map<String, Object> map) {
T d = BeanUtils.instantiateClass(clazz);
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(d.getClass());
} catch (IntrospectionException e) {
log.warn(e.getMessage(), e);
return null;
}
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : propertyDescriptors) {
Method setter = property.getWriteMethod();
if (setter == null) {
continue;
}
Object value = map.get(property.getName());
if (value == null) {
continue;
}
try {
setter.invoke(d, value);
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
}
return d;
}
/**
* 对象集合中取出两个字段,组成map
* @param collection 对象集合
* @param keyProperty key属性值
* @param valueProperty value属性值
* @param 泛型
* @return 属性值map
*/
@Nonnull
public static <E> Map<String, E> propertyToMap(@Nonnull Collection<?> collection, @Nonnull String keyProperty, @Nonnull String valueProperty) {
Map<String, E> map = new LinkedHashMap<>();
if (collection.isEmpty()) {
return map;
}
Object obj = collection.iterator().next();
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(obj.getClass());
} catch (IntrospectionException e) {
log.warn(e.getMessage(), e);
return map;
}
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
Method keyGetter = null;
Method valueGetter = null;
for (PropertyDescriptor property : propertyDescriptors) {
if (keyProperty.equals(property.getName()) && property.getReadMethod() != null) {
keyGetter = property.getReadMethod();
} else if (valueProperty.equals(property.getName()) && property.getReadMethod() != null) {
valueGetter = property.getReadMethod();
}
}
if (keyGetter == null || valueGetter == null) {
return map;
}
for (Object item : collection) {
try {
map.put(keyGetter.invoke(item).toString(), (E) valueGetter.invoke(item));
} catch (IllegalAccessException | InvocationTargetException e) {
log.warn(e.getMessage(), e);
}
}
return map;
}
}
官方示例:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
/**
* Returns a stream consisting of the elements of this stream, additionally
* performing the provided action on each element as elements are consumed
* from the resulting stream.
*
* This is an intermediate
* operation.
*
*
For parallel stream pipelines, the action may be called at
* whatever time and in whatever thread the element is made available by the
* upstream operation. If the action modifies shared state,
* it is responsible for providing the required synchronization.
*
* @apiNote This method exists mainly to support debugging, where you want
* to see the elements as they flow past a certain point in a pipeline:
*
{@code
* Stream.of("one", "two", "three", "four")
* .filter(e -> e.length() > 3)
* .peek(e -> System.out.println("Filtered value: " + e))
* .map(String::toUpperCase)
* .peek(e -> System.out.println("Mapped value: " + e))
* .collect(Collectors.toList());
* }
*
* In cases where the stream implementation is able to optimize away the
* production of some or all the elements (such as with short-circuiting
* operations like {@code findFirst}, or in the example described in
* {@link #count}), the action will not be invoked for those elements.
*
* @param action a
* non-interfering action to perform on the elements as
* they are consumed from the stream
* @return the new stream
*/
Stream<T> peek(Consumer<? super T> action);
}
eg:
Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
java 8的stream是由三部分组成:
1、数据源,
2、零个或一个或多个中间操作,
3、一个或零个终止操作
中间操作是对数据的加工,注意,中间操作是lazy操作,并不会立马启动,需要等待终止操作才会执行。终止操作是stream的启动操作,只有加上终止操作,stream才会真正的开始执行。所以,问题解决了,比如:collect(Collectors.toList()) 就是终止操作;peek是一个中间操作,文档说明peek主要被用在debug用途,上面例子输出
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
peek接收的是一个Consumer类型的参数,Consumer是没有返回值的,只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
所以我们试一试peek对stream中基本数据类型的操作,看一下peek中对基本数据类型的操作会不会影响结果的输出:可以看到stream中的元素并没有被转换成大写格式
@GetMapping("/test04")
public void test04(){
Stream.of("one", "two", "three", "four")
.peek(e -> e.toUpperCase())
.forEach(System.out::println);
}
one
two
three
four
可以看到map是真正的对元素进行了转换
@GetMapping("/test04")
public void test04(){
Stream.of("one", "two", "three", "four")
.map(e -> e.toUpperCase())
.forEach(System.out::println);
}
ONE
TWO
THREE
FOUR
peek也有例外,假如我们Stream里面是一个对象会怎么样?
@GetMapping("/test04")
public void test04(){
Stream.of(Payment.builder().name("a").build(), Payment.builder().name("b").build(), Payment.builder().name("c").build())
.peek(payment -> payment.setName(payment.getName().toUpperCase()))
.forEach(System.out::println);
}
Payment(typeId=null, name=A, orderId=null)
Payment(typeId=null, name=B, orderId=null)
Payment(typeId=null, name=C, orderId=null)
如果是对象的话,实际的结果会被改变。
peek接收一个Consumer,而map接收一个Function。
Consumer是没有返回值的,
它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
而Function是有返回值的,
这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。