美文网首页
MyBatis collection 集合嵌套查询树形节点

MyBatis collection 集合嵌套查询树形节点

作者: 程序员泥瓦匠 | 来源:发表于2018-01-30 19:01 被阅读648次

    MyBatis collection 集合

    MyBatis 是数据持久层框架,支持定制化 SQL、存储过程以及高级映射。尤其强大在于它的映射语句,比如高级映射中的 collection 集合。

    collection 集合,集合常用的两个场景是集合的嵌套查询、集合的嵌套结果。集合的嵌套结果就是查询结果对应嵌套子对象。这里就是利用 collection 集合嵌套查询树形节点。下面来一一实现。

    查询树形节点 Web 案例

    创建数据库表

    节点表:

    CREATE TABLE `node` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(32) NOT NULL,
      `parent_id` int(11) unsigned NOT NULL,
      PRIMARY KEY (`id`),
      KEY `parent_id` (`parent_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COMMENT='节点表'
    

    简单的节点父子关系设计,下面插入几条数据:

    INSERT INTO node (name, parent_id) VALUES ('一级节点A', 0);
    INSERT INTO node (name, parent_id) VALUES ('一级节点B', 0);
    INSERT INTO node (name, parent_id) VALUES ('一级节点C', 0);
    INSERT INTO node (name, parent_id) VALUES ('二级节点AA', 1);
    INSERT INTO node (name, parent_id) VALUES ('二级节点aa', 1);
    INSERT INTO node (name, parent_id) VALUES ('二级节点BB', 2);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点AAA', 4);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点aaa', 4);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点BBB', 6);
    

    mybatis-collection-tree 工程

    pom.xml 添加 mybatis 依赖:

        <modelVersion>4.0.0</modelVersion>
    
        <groupId>springboot</groupId>
        <artifactId>mybatis-collection-tree</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>MyBatis :: collection 集合嵌套查询树形节点</name>
    
        <!-- Spring Boot 启动父依赖 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.1.RELEASE</version>
        </parent>
    
        <properties>
            <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
            <mysql-connector>5.1.39</mysql-connector>
        </properties>
    
        <dependencies>
    
            <!-- Spring Boot Web 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- Spring Boot Test 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!-- Spring Boot Mybatis 依赖 -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis-spring-boot}</version>
            </dependency>
    
            <!-- MySQL 连接驱动依赖 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-connector}</version>
            </dependency>
    
            <!-- Junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    </project>
    

    在 application.properties 应用配置文件,增加 Mybatis 相关配置:

    ## 数据源配置
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    ## Mybatis 配置
    mybatis.typeAliasesPackage=org.mybatis.domain
    mybatis.mapperLocations=classpath:mapper/*.xml
    

    mybatis.typeAliasesPackage 配置为 org.mybatis.domain,指向实体类包路径。mybatis.mapperLocations 配置为 classpath 路径下 mapper 包下,* 代表会扫描所有 xml 文件。

    重要的还是看 collection 在 xml 的映射实现,NodeMapper.xml 代码如下:

    <mapper namespace="org.mybatis.dao.NodeDao">
    
      <resultMap id="BaseTreeResultMap" type="org.mybatis.domain.Node">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
      </resultMap>
    
      <resultMap id="NextTreeResultMap" type="org.mybatis.domain.Node">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
      </resultMap>
    
      <sql id="Base_Column_List">
            id, name
        </sql>
    
      <select id="getNextNodeTree" resultMap="NextTreeResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM node
        WHERE parent_id = #{id}
      </select>
    
      <select id="getNodeTree" resultMap="BaseTreeResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM node
        WHERE parent_id = 0
      </select>
    
    </mapper>
    

    在 dao 层,我们只调用 getNodeTree 方法,parent_id = 0 代表顶级节点。然后通过 collection 节点继续调用 getNextNodeTree 方法进行循环调用。

    <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
    

    以下是关键的知识点:

    • column 代表会拿父节点 id ,作为参数获取 next 对象
    • javaType 代表 next 对象是个列表,其实可以省略不写
    • ofType 用来区分 JavaBean 属性类型和集合包含的类型
    • select 是用来执行循环哪个 SQL

    工程代码地址:https://github.com/JeffLi1993/myabtis-learning-example
    工程演示后的结果如图所示:

    image.png

    思考小结

    这样的实现原理,嵌套 SQL 执行,这里就存在一个性能上的问题。比如 10 万条数据,需要执行 10 万次 SELECT 语句。所以不推荐数据量级大的树形结构。

    如果结构不经常改变,数量级还行,可以考虑加缓存。这样,读取的数据库的次数大大减少,比如省市区。

    还有一种常用的树形节点实现是,读取几次,内存处理。这样的好处就是减少对数据库查询次数,内存处理速度很快,性能大大提升。

    参考文献:
    http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

    相关文章

      网友评论

          本文标题:MyBatis collection 集合嵌套查询树形节点

          本文链接:https://www.haomeiwen.com/subject/vefpaxtx.html