Java学习笔记分享之MyBatis篇(中)

    科技2022-07-11  102

    官网: 打开官网

    MyBatis发展到现在的3.x版本,已经具备了很完善的开发和扩展机制,可以应付不同的场景。

    开发模式:XML 和 注解详细的XML配置信息缓存机制插件机制动态SQL

    MyBatis开发套路:

    1)添加MyBatis的GAV坐标

    2)创建数据库表(user)

    3)创建实体类(user)

    4)编写映射文件(UserMapper.xml)

    5)编写核心文件SqlMapConfig.xml

    6)测试功能是否正常

    坐标导入

    <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!--junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>

    数据库Sql

    /* Navicat Premium Data Transfer Source Server : 127.0.0.1(3306) Source Server Type : MySQL Source Server Version : 50717 Source Host : localhost:3306 Source Schema : lagou_mybatis Target Server Type : MySQL Target Server Version : 50717 File Encoding : 65001 Date: 16/09/2020 11:38:29 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `birthday` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'lucy', '123', '2019-12-12'); INSERT INTO `user` VALUES (2, 'tom', '123', '2019-12-12'); INSERT INTO `user` VALUES (3, 'jack', '123456', '2020-12-02'); SET FOREIGN_KEY_CHECKS = 1;

    实体类

    public class User implements Serializable { private int id; private String username; private String password; private String birthday; // getter setter }

    MyBatis开发的前面三个步骤已经完成了,那么接下来就分别从XML和注解来对MyBatis进行讲解。

    1.1 MyBatis快速入门

    UserMapper.xml映射文件

    ​ Mapper.xml映射文件的作用主要是映射实体(User)和数据库表(user)的关系。同时定义对数据库操作的sql语句信息。

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:命名空间,必须保持唯一 namespace在实际开发中是需要和接口全限定类名保持一致的,这个后面解释这样的好处 --> <mapper namespace="userMapper"> <resultMap id="ResultMap" type="com.zyj.pojo.User"> <id property="id" column="id" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="birthday" column="birthday" /> </resultMap> <!-- 定义查询sql语句 id:同一个mapper.xml文件中需要唯一 resultMap:返回的结果类型映射,可以是resultMap或者resutlType。 --> <select id="findAll" resultMap="ResultMap"> select * from User </select> </mapper>

    SqlMapConfig.xml核心配置文件

    ​ SqlMapConfig.xml核心配置文件主要是配置数据库相关的信息,例如数据库的url路径、数据库驱动、账号、密码等。同时还有一个非常重要的任务就是关联Mapper.xml配置文件。这样MyBatis只需要解析这个配置文件就可以解读到所有需要的信息了。

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 应用启动,解析配置文件生成Configuration配置对象 environments:default属性选择项目当前的数据库源值就是environment的id environment数据库源,可以配置多个,例如开发环境dev/测试环境test/生产环境prod等 --> <environments default="developer"> <environment id="developer"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/lagou_mybatis?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 引入Mapper映射配置文件 --> <mappers> <mapper resource="UserMapper.xml" /> </mappers> </configuration>

    代码测试

    // 加载核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 获取sqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); // 获取sqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行sql语句,参数为:mapper.xml配置文件的namesapce.方法id List<User> userList = sqlSession.selectList("userMapper.findAll"); // 输出结果 System.out.println(userList); // 关闭资源 sqlSession.close();

    1.2 配置文件深入解析

    ​ 由于MyBatis的sql整体开发都是基于XML配置文件。所以,在开始正式的学习Mybatis操作数据库之前,我们需要学习的是MyBatis的相关文件配置标签和它的作用。MyBatis涉及到的配置文件有2类SqlMapConfig.xml和Mapper.xml。那么下面我们就分别讲解2个文件的标签。

    1.2.1 SqlMapConfig.xml

    属性(properties)

    ​ 这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:

    <properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="dev_password"/> </properties>

    设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:

    <dataSource type="POOLED"> <!-- 这里配置的属性读取上面定义的 --> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>

    ​ 这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。

    如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

    首先读取在 properties 元素体内指定的属性。然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

    因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

    从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。例如:

    <dataSource type="POOLED"> <!-- ... --> <property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' --> </dataSource>

    类型别名(typeAliases)

    ​ 在编写XML的时候,经常需要写类的全限定类名,这样的类名非常的长。因此,MyBatis提供了优化方案。类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如

    <!-- 定义类型别名,单独指定 --> <typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> <typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases> <!-- 定义类型别名,扫描指定的包,这样扫描的别名默认是类名首字母小写,如果需要单独指定可以使用@Alias注解,使用方式如下: --> <typeAliases> <package name="domain.blog"/> </typeAliases> // 扫描包方式自定义别名 @Alias("author") public class Author { ... }

    ​ 下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

    别名映射的类型_bytebyte_longlong_shortshort_intint_integerint_doubledouble_floatfloat_booleanbooleanstringStringbyteBytelongLongshortShortintIntegerintegerIntegerdoubleDoublefloatFloatbooleanBooleandateDatedecimalBigDecimalbigdecimalBigDecimalobjectObjectmapMaphashmapHashMaplistListarraylistArrayListcollectionCollectioniteratorIterator

    环境配置(environments)

    ​ MyBatis可以配置成适应多种环境,这样有利于将Sql映射到多种数据库中。这在实际开发中非常有用。例如:开发、测试、生产环境的数据库资源配置都是不一样的。例如下面的配置:

    <environments default="developer"><!-- 使用开发环境 --> <!-- 开发环境 --> <environment id="developer"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value=""/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> <!-- 测试环境 --> <environment id="testA"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value=""/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments>

    ​ 在创建SqlSessionFactory的时候,可以将需要使用的环境变量传递。如果忽略了environment参数,那么就会使用配置的默认值。

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

    事务管理器(transactionManager)

    在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

    JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如: <transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>

    数据源(dataSource)

    dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

    UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。

    POOLED(推荐)– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。

    JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

    映射器(mappers)

    ​ mappers标签目的是在SqlMapConfig配置文件中关联Mapper配置文件。同时也提供了相对比较灵活方式的配置。

    <!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> <!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers> <!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers> <!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>

    ​ 这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要重点讨论的。

    1.2.2 Mapper.xml

    动态sql

    ​ MyBatis强大并且受到众多开发者青睐的原因之一就是强大的动态Sql能力。如果我们开发中使用的Sql语句都是非常简单的,那么没有问题。但是遇到复杂的业务场景,需要随时根据不同的条件执行不同的查询语句,这个时候就不应该写多个sql语句。而是应该编写一个随时根据不同条件调整的sql语句。

    动态sql标签

    ifchoose (when, otherwise)trim (where, set)foreach

    if标签

    使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

    <select id="findActiveBlogWithTitleLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <!-- 如果title为null,那么就不包含 AND title like #{title} 查询条件--> <if test="title != null"> AND title like #{title} </if> </select>

    同一个if标签多个判断条件

    <select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test="title != null"> AND title like #{title} </if> <!-- 如果一个if标签需要判断多个条件,那么使用and表示'与',or表示'或' --> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </select>

    choose、when、otherwise标签

    ​ 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。 choose的条件判断和if标签的写法一样。

    <select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <!-- 如果上面的条件都不满足,就拼接otherwise标签的条件 --> <otherwise> AND featured = 1 </otherwise> </choose> </select>

    trim、where、set标签

    ​ 前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,如果我们将 “state = ‘ACTIVE’” 设置成动态条件,那么我们的sql写法就如下:

    <select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE <!-- state条件也是动态的 --> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </select>

    如果上诉的test="state != null"、test="title != null"、test="author != null and author.name != null" 条件都不成立,那么这条动态Sql最终的结果为:

    SELECT * FROM BLOG WHERE -- 查询失败,where后面没有条件

    如果上诉的第二个条件test="title != null"成立,那么这条动态Sql最终的结果为:

    SELECT * FROM BLOG WHERE AND title like 'someTitle -- 查询失败,where后面多个一个and关键字

    where标签引入

    ​ 由于查询条件的不同,关键字where和and都没有实现动态。这个时候我们需要引入新的标签实现where和and的动态生成。

    <select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG <!-- 使用where标签包裹if查询条件,如果没有任何一个if条件满足,where就不会拼接。而且where会将第一个条件的and或者or关键字去掉 --> <where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </where> </select>

    set标签引入

    ​ set标签用于update语句中,update语句可以根据if判断条件决定是否更新某一个属性值。set原理和where标签一样,都是根据if条件是否成立拼接set关键字。

    <update id="updateAuthorIfNecessary"> update Author <!-- 如果if条件一个都不成立,那么set关键字也不会拼接。如果有成立的,那么第一个属性后面的‘逗号’也会自动删除掉 --> <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>

    foreach标签

    动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

    <select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <!-- foreach属性解读 collection:需要遍历的集合名词 index:遍历的索引,一般不是使用 item:collection遍历出来的值临时存储在item中 open:整个遍历过程开始符号 separator:遍历一次就拼接的符号 close:整个遍历过程结束符号 --> <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select> -- 上述动态sql的遍历结果如下: SELECT * FROM POST P WHERE ID in (id1, id2)

    1.3 XML模式复杂映射

    1.3.1 一对一查询

    案例:用户表和订单表,从订单的角度来看,一个订单只属于一个用户。

    要求:查询一个订单,并且查询这个订单所关联的用户信息。

    -- 创建订单表并插入记录 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for orders -- ---------------------------- DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `ordertime` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `total` double NULL DEFAULT NULL, `uid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, INDEX `uid`(`uid`) USING BTREE, CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of orders -- ---------------------------- INSERT INTO `orders` VALUES (1, '2019-12-12', 3000, 1); INSERT INTO `orders` VALUES (2, '2019-12-12', 4000, 1); INSERT INTO `orders` VALUES (3, '2019-12-12', 5000, 2); SET FOREIGN_KEY_CHECKS = 1; -- 查询数据sql select * from orders o inner join user u on o.uid = u.id;

    查询结果:

    创建orders实体 user的实体已经在上面创建过了

    public class Order { private int id; private String ordertime; private double total; // 存储用户信息 private User user; // getter setter toString }

    创建OrderMapper接口

    public interface OrderMapper { List<Order> findOrderAndUser(); }

    创建OrderMapper.xml

    注意:association 注解是映射一的关系,配置user的信息,内部配置user的属性值。由于orders和user表都有id字段,为了避免冲突,所以association 的id封装字段为orders表的uid。

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zyj.dao.OrderMapper"> <resultMap id="orderAndUserResultMap" type="com.zyj.pojo.Order"> <id property="id" column="id" /> <result property="ordertime" column="ordertime" /> <result property="total" column="total" /> <!-- user映射 --> <association property="user" javaType="com.zyj.pojo.User"> <result property="id" column="uid" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="birthday" column="birthday" /> </association> </resultMap> <!--一对一查询--> <select id="findOrderAndUser" resultMap="orderAndUserResultMap"> select * from orders o inner join user u on o.uid = u.id </select> </mapper>

    测试过程

    @Test public void test1() { OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<Order> orderList = orderMapper.findOrderAndUser(); for (Order order : orderList) { System.out.println(order); } }
    1.3.2 一对多查询

    案例:用户表和订单表,从用户的角度来看,一个用户有多个订单。

    要求:查询一个用户,并且查询这个用户所关联的订单信息。

    -- 查询数据sql select * from user u left join orders o on u.id = o.uid;

    查询结果:

    更新user实体

    public class user { // 新增订单属性,使用结合存储查询到的订单信息 private List<Order> orderList; // getter setter toString }

    更新UserMapper接口

    public interface UserMapper { List<User> findUserAndOrder(); }

    更新UserMapper.xml

    **注意:**collection 注解是映射多的关系,配置order的信息,内部配置order的属性值。由于orders和user表都有id字段,为了避免冲突,所以resultMap 的id封装字段为orders表的uid。

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zyj.dao.UserMapper"> <resultMap id="userAndOrderResultMap" type="com.zyj.pojo.User"> <!-- 注意这里的user id属性值 --> <id property="id" column="uid" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="birthday" column="birthday" /> <collection property="orderList" ofType="com.zyj.pojo.Order"> <result property="id" column="id" /> <result property="ordertime" column="ordertime" /> <result property="total" column="total" /> </collection> </resultMap> <!-- 一对多查询 statement : namespacer.id = com.zyj.dao.UserMapper.findUserAndOrder --> <select id="findUserAndOrder" resultMap="userAndOrderResultMap"> select * from user u left join orders o on u.id = o.uid </select> </mapper>

    测试过程

    @Test public void test2() { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userAndOrder = userMapper.findUserAndOrder(); for (User user : userAndOrder) { System.out.println(user); } }
    1.3.3多对多查询

    案例:用户表和角色表,一个用户可以有多个角色,一个角色可以被多个用户使用。

    要求:查询一个用户,并且查询这个用户所关联的角色信息。

    -- 创建角色表 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `roleDesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES (1, 'CTO', 'CTO'); INSERT INTO `sys_role` VALUES (2, 'CEO', 'CEO'); SET FOREIGN_KEY_CHECKS = 1; -- ------------------------------------------------- -- 创建用户角色中间表 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `userid` int(11) NOT NULL, `roleid` int(11) NOT NULL, PRIMARY KEY (`userid`, `roleid`) USING BTREE, INDEX `roleid`(`roleid`) USING BTREE, CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES (1, 1); INSERT INTO `sys_user_role` VALUES (2, 1); INSERT INTO `sys_user_role` VALUES (1, 2); INSERT INTO `sys_user_role` VALUES (2, 2); SET FOREIGN_KEY_CHECKS = 1; -- 查询数据sql,对于多对多关系,都是通过中间表关联 select * from user u left join sys_user_role ur on u.id = ur.userid inner join sys_role r on ur.roleid = r.id;

    查询结果:

    创建rose实体

    public class Role { private int id; private String rolename; private String roleDesc; // getter setter }

    更新user实体

    public class user { // 新增角色属性,使用结合存储查询到的角色信息 private List<Role> roleList; // getter setter toString }

    更新UserMapper接口

    public interface UserMapper { List<User> findUserAndRole(); }

    更新UserMapper.xml

    注意collection 注解是映射多的关系,配置role的信息,内部配置role的属性值。对于多对多的关系可以拆分为双向的一对多。所以多对多查询和一对多查询一致,只是需要多关联中间表。

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zyj.dao.UserMapper"> <resultMap id="userAndRoleResultMap" type="com.zyj.pojo.User"> <id property="id" column="id" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="birthday" column="birthday" /> <collection property="roleList" ofType="com.zyj.pojo.Role"> <result property="id" column="roleid" /> <result property="rolename" column="rolename" /> <result property="roleDesc" column="roleDesc" /> </collection> </resultMap> <select id="findUserAndRole" resultMap="userAndRoleResultMap"> select * from user u left join sys_user_role ur on u.id = ur.userid inner join sys_role r on ur.roleid = r.id </select> </mapper>

    测试过程

    @Test public void test3() { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userAndOrder = userMapper.findUserAndRole(); for (User user : userAndOrder) { System.out.println(user); } } 知春秋 认证博客专家 博客专家 Java高级研发 不忘初心,方得始终。初心易得,始终难守。
    Processed: 0.009, SQL: 8