深圳网站设计有名 乐云践新,茂名网站开发,电子元器件商城,wordpress配置多语言1. 树形结构
树形结构#xff0c;是指#xff1a;数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈 它具有以下特点#xff1a;
每个节点都只有有限个子节点或无子节点#xff1b;没有父节点的节点称为根节点#xff1b;每一个非根节点有且只有一个父节点是指数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈 它具有以下特点
每个节点都只有有限个子节点或无子节点没有父节点的节点称为根节点每一个非根节点有且只有一个父节点除了根节点外每个子节点可以分为多个不相交的子树树里面没有环路(cycle)
2. 常见问题 在实际开发中很多数据都是树形结构例如地区、页面上的菜单、上下级关系的组织等等这时就需要我们从数据源中读取到数据通过某些方式拼成树形结构 然后再给前端展示。对于一些不经常变化且使用频繁的数据可以考虑将拼好的树形结构数据放入缓存每次用的时候直接读取出来就可以使用。
3. 准备环境
springboot: 2.6.0 mysql: 5.7
CREATE TABLE t_region (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL,region_type varchar(255) DEFAULT NULL,parent_id bigint(20) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8;INSERT into t_region(name, region_type, parent_id) VALUES(山西省, province, 0);
INSERT into t_region(name, region_type, parent_id) VALUES(临汾市, city, 1);
INSERT into t_region(name, region_type, parent_id) VALUES(尧都区, district, 2);INSERT into t_region(name, region_type, parent_id) VALUES(北京, province, 0);
INSERT into t_region(name, region_type, parent_id) VALUES(北京市, city, 4);
INSERT into t_region(name, region_type, parent_id) VALUES(朝阳区, district, 5);INSERT into t_region(name, region_type, parent_id) VALUES(太原市, city, 1);
INSERT into t_region(name, region_type, parent_id) VALUES(小店区, district, 7);pom.xml
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdorg.example/groupIdartifactIdspring-test/artifactIdversion1.0-SNAPSHOT/versionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.6.0/versionrelativePath//parentpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactId/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.2/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion1.16.10/versionscopeprovided/scope/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.1.10/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency!-- druid依赖 --dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.0/version/dependency/dependencies
/projectspring:main:allow-circular-references: truedatasource: #定义数据源#127.0.0.1为本机测试的ip3306是mysql的端口号。serverTimezone是定义时区照抄就好mysql高版本需要定义这些东西#useSSL也是某些高版本mysql需要问有没有用SSL连接url: jdbc:mysql://192.168.1.141:3306/db_user?serverTimezoneGMT%2B8useSSLFALSEusername: root #数据库用户名root为管理员password: 123456 #该数据库用户的密码# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:# xml扫描多个目录用逗号或者分号分隔告诉 Mapper 所对应的 XML 文件位置mapper-locations: classpath:mapper/*.xml# 以下配置均有默认值,可以不设置global-config:db-config:#主键类型 AUTO:数据库ID自增 INPUT:用户输入ID,ID_WORKER:全局唯一ID (数字类型唯一ID), UUID:全局唯一ID UUID;id-type: auto#数据库类型db-type: MYSQLconfiguration:# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射map-underscore-to-camel-case: true# 如果查询结果中包含空值的列则 MyBatis 在映射的时候不会映射这个字段call-setters-on-nulls: true# 这个配置会将执行的sql打印出来在开发或测试的时候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl表对应的实体类
Data
TableName(value t_region)
public class Region {TableId(type IdType.AUTO)private Long id;/*** 名称*/private String name;/*** 类型*/private String regionType;/*** 父id*/private Long parentId;
}返回给前端的实体类
Data
public class RegionVO {private Long id;/*** 名称*/private String name;/*** 类型*/private String regionType;/*** 父id*/private Long parentId;private ListRegionVO children;
}4.实现方式
1.基于xml
RegionMapper.xml
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespaceorg.example.mapper.RegionMapperresultMap idregionMap typeorg.example.dto.RegionVOid propertyid columnid/idresult propertyname columnname/resultresult propertyregionType columnregion_type/resultresult propertyparentId columnparent_id/resultcollection propertychildren ofTypeorg.example.dto.RegionVO javaTypejava.util.Listcolumnid selectgetById/collection/resultMapselect idgetById resultMapregionMap parameterTypemapSELECT*FROMt_regionwhere parent_id#{id}/selectselect idgetAll resultMapregionMapSELECT*FROMt_region where parent_id 0/select/mapper
RegionMapper
Mapper
public interface RegionMapper extends BaseMapperRegion {ListRegionVO getAll();
}RegionService
public interface RegionService extends IServiceRegion {ListRegionVO getAll();
}RegionServiceImpl
Service
public class RegionServiceImpl extends ServiceImplRegionMapper, Region implements RegionService {Overridepublic ListRegionVO getAll(){return baseMapper.getAll();}
}这种方式按照上边添加的数据量(8条)共执行了9次查询 2.LambdaQueryWrapper
RegionServiceImpl添加如下代码 Overridepublic ListRegionVO getAllWrapper(){LambdaQueryWrapperRegion wrapper new LambdaQueryWrapper();wrapper.eq(Region::getParentId, 0);//查询根级ListRegion regions baseMapper.selectList(wrapper);ListRegionVO list regions.stream().map(p - {RegionVO obj new RegionVO();BeanUtils.copyProperties(p, obj);return obj;}).collect(Collectors.toList());list.forEach(this::getChildren);return list;}private void getChildren(RegionVO item){LambdaQueryWrapperRegion wrapper new LambdaQueryWrapper();wrapper.eq(Region::getParentId, item.getId());//根据parentId查询ListRegion list baseMapper.selectList(wrapper);ListRegionVO voList list.stream().map(p - {RegionVO vo new RegionVO();BeanUtils.copyProperties(p, vo);return vo;}).collect(Collectors.toList());//写入到childrenitem.setChildren(voList);//如果children不为空继续往下找if (!CollectionUtils.isEmpty(voList)) {voList.forEach(this::getChildren);}}这种方式按照上边添加的数据量(8条)共执行了9次查询 3.递归方法
RegionServiceImpl添加如下代码 Override
public ListRegionVO build(){//一次把所有的数据都查出来ListRegion regions baseMapper.selectList(null);ListRegionVO allList regions.stream().map(p - {RegionVO vo new RegionVO();BeanUtils.copyProperties(p, vo);return vo;}).collect(Collectors.toList());//指定根节点的parentIdreturn buildChildren(0L, allList);
}private ListRegionVO buildChildren(Long parentId, ListRegionVO allList){ListRegionVO voList new ArrayList();for (RegionVO item : allList) {//如果相等if (Objects.equals(item.getParentId(), parentId)) {//递归自己调自己item.setChildren(buildChildren(item.getId(), allList));voList.add(item);}}return voList;
}这种就不必说了一次查询所有数据出来一共执行一次查询 4.总结 查询方式有很多应该使用哪种需要猿们结合具体情况选择。
第一种情况当整体数据量特别大 层级不深 需要按照某个根节点查询时推荐使用第一、二种方式。 第二种情况当需要查询整个树时推荐使用第三种方式。