Mybatis学习

Mybatis入门

搭建环境

  1. 创建maven工程

     Groupid:com.xuexi
     ArtifactId:mybatis01
     Packing:jar
  2. 添加坐标

     
         org.mybatis
         mybatis
         3.4.5
     
     
         junit
         junit
         4.10
         test
     
     
         mysql
         mysql-connector-java
         5.1.6
         runtime
     
     
         log4j
         log4j
         1.2.12
     
  3. 编写User实体类

  4. 编写持久层接口IUserDao

     public interface IUserDao {
         /**
          * 查询所有用户
          * @return
          */
         List findAll();
     }
  5. 编写resources中持久层接口的映射文件IUserDao.xml

    要求:

    • 创建位置:必须和持久层接口在相同的包中
    • 名称:必须以持久层接口名称命名文件夹,扩展名是.xml

        
        
        
            
            
        
  6. 编写SqlMapConfig.xml配置文件

     
     
     
         
         
             
             
                 
                 
                 
                 
                 
                 
                 
                 
                 
             
         
         
         
             
         
     
  7. 编写测试类

     public class MybatisTest {
         public static void main(String[] args)throws Exception {
             //1.读取配置文件
             InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
             //2.创建 SqlSessionFactory 的构建者对象
             SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
             //3.使用构建者创建工厂对象 SqlSessionFactory
             SqlSessionFactory factory = builder.build(in);
             //4.使用 SqlSessionFactory 生产 SqlSession 对象
             SqlSession session = factory.openSession();
             //5.使用 SqlSession 创建 dao 接口的代理对象
             IUserDao userDao = session.getMapper(IUserDao.class);
             //6.使用代理对象执行查询所有方法
             List users = userDao.findAll();
             for(User user : users) {
             System.out.println(user);
             }
             //7.释放资源
             session.close();
             in.close();
         }
     }

基于注解的mybatis使用

  1. 在持久层接口中添加注解

     public interface IUserDao {
         /**
          * 查询所有用户
          * @return
          */
         @Select("select * from user")
         List findAll();
     }
  2. 修改SqlMapConfig.xml

     
     
         
     
  3. 注意事项

    在使用基于注解的Mybatis配置时,请移除xml的映射配置(IUserDao.xml)

基于代理Dao实现CRUD操作

使用要求:

  1. 持久层接口和持久层接口的映射配置必须在相同的包下

  2. 持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名

  3. SQL 语句的配置标签 select * from user where id = #{uid}

    说明:

    1. resultType属性:用于指定结果集的类型
    2. parameterType属性:用于指定传入参数的类型
    3. sql语句中使用 #{} 字符:代表占位符,相当于原来jdbc部分的 ? ,都是用于执行语句时替换实际的数据。具体的数据是由 #{} 里面的内容决定的
    4. #{}中内容的写法:由于数据类型是基本类型,所以此处可以随意写
  4. 在测试类添加测试

     public class MybastisCRUDTest {
         private InputStream in ;
         private SqlSessionFactory factory;
         private SqlSession session;
         private IUserDao userDao;
    
         @Test
         public void testFindOne() {
             //6.执行操作
             User user = userDao.findById(41);
             System.out.println(user);
         }
    
         @Before//在测试方法执行之前执行
         public void init()throws Exception {
             //1.读取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
             //2.创建构建者对象
             SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
             //3.创建 SqlSession 工厂对象
             factory = builder.build(in);
             //4.创建 SqlSession 对象
             session = factory.openSession();
             //5.创建 Dao 的代理对象
             userDao = session.getMapper(IUserDao.class);
         }
    
         @After//在测试方法执行完成之后执行
         public void destroy() throws Exception{
             session.commit();
             //7.释放资源
             session.close();
             in.close();
         }
     }

保存操作

  1. 在持久层接口中添加新增方法

     int saveUser(User user);
  2. 在用户的映射配置文件中配置

     
     
         insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
     

    说明:

    1. parameterType属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称
    2. sql语句中使用 #{} 字符:代表占位符,相当于原来jdbc部分的 ? ,都是用于执行语句时替换实际的数据。具体的数据是由 #{} 里面的内容决定的
    3. #{} 中内容的写法:由于保存方法的参数是一个 User 对象,此处要写 User 对象中的属性名称。使用的是 ognl 表达式
    4. ognl 表达式:它是apache提供的一种表达式语言,全称是:Object Graphic Navigation Language 对象图导航语言。按照一定的语法格式来获取数据的,语法格式就是使用 #{对象.对象} 的方式。 #{user.username} 它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用 getUsername() 方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user. 而直接写 username
  3. 添加测试方法

     @Test
     public void testSave(){
         User user = new User();
         user.setUsername("modify User property");
         user.setAddress("北京市顺义区");
         user.setSex("男");
         user.setBirthday(new Date());
         System.out.println("保存操作之前: "+user);
         //5.执行保存方法
         userDao.saveUser(user);
         System.out.println("保存操作之后: "+user);
     }

    打开 Mysql 数据库发现并没有添加任何记录,原因是什么?
    这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务
    提交呢?
    可以使用: session.commit(); 来实现事务提交。 加入事务提交后的代码如下:

     @After//在测试方法执行完成之后执行
     public void destroy() throws Exception{
         session.commit();
         //7.释放资源
         session.close();
         in.close();
     }
  4. 扩展:新增用户id的返回值

    新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回

     
         
         
             select last_insert_id();
         
         insert into user(username,birthday,sex,address)
         values(#{username},#{birthday},#{sex},#{address})
     

用户更新

  1. 在持久层接口中添加更新方法

     int updateUser(User user);
  2. 在用户的映射配置文件中配置

     
     
         update user set username=#{username},birthday=#{birthday},sex=#{sex}, address=#{address} where id=#{id}
     
  3. 添加测试方法

     @Test
     public void testUpdateUser()throws Exception{
         //1.根据 id 查询
         User user = userDao.findById(52);
         //2.更新操作
         user.setAddress("北京市顺义区");
         int res = userDao.updateUser(user);
         System.out.println(res);
     }

用户删除

  1. 在持久层接口中添加删除方法

     int deleteUser(Integer userId);
  2. 在用户的映射配置文件中配置

     
     
         delete from user where id = #{uid}
     
  3. 添加测试方法

     @Test
     public void testDeleteUser() throws Exception {
         //6.执行操作
         int res = userDao.deleteUser(52);
         System.out.println(res);
     }

用户模糊查询

  1. 在持久层接口中添加模糊查询方法

     List findByName(String username);
  2. 在用户的映射配置文件中配置

     
     
  3. 添加测试方法

     @Test
     public void testFindByName(){
         //5.执行查询一个方法
         List users = userDao.findByName("%王%");
         for(User user : users){
         System.out.println(user);
     }
  4. 模糊查询的另一种配置方式

    1. 修改SQL语句的配置,如下

       
       

      将原来的 #{} 占位符,改成了 ${value} 。注意如果用模糊查询的这种写法,那么 ${value} 的写法就是固定的,不能写成其它名字。

    2. 测试

       @Test
       public void testFindByName(){
           //5.执行查询一个方法
           List users = userDao.findByName("王");
           for(User user : users){
           System.out.println(user);
           }
       } 
  5. #{}${} 的区别

    • #{} 表示一个占位符号

      通过 #{} 可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
      #{}可以有效防止 sql 注入。 #{} 可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类
      型值,#{} 括号中可以是 value 或其它名称。
    • ${} 表示拼接 sql 串

      通过 ${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${} 可以接收简
      单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, ${} 括号中只能是 value

查询使用聚合函数

  1. 在持久层接口中添加模糊查询方法

     int findTotal();
  2. 在用户的映射配置文件中配置

     
     
  3. 添加测试方法

     @Test
     public void testFindTotal() throws Exception {
         //6.执行操作
         int res = userDao.findTotal();
         System.out.println(res);
     }

Mybatis与JDBC编程的比较

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

    解决:
    在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。
  2. Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大, sql 变动需要改变 java 代码。

    解决:
    将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。

  3. 向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。

    解决:
    Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的
    类型。

  4. 对结果集解析麻烦, sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对
    象解析比较方便。

    解决:
    Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的
    类型。

Mybatis的参数深入

parameterType配置参数

  1. SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类

  2. 注意

    基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:
    java.lang.String。

    实体类类型,目前我们只能使用全限定类名。

    究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,
    而我们的是实体类并没有注册别名,所以必须写全限定类名。

    参考 TypeAliasRegistery.class 的源码

传递 pojo 包装对象

开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

Pojo 类中包含 pojo。

需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中

  1. 编写 QueryVo

     public class QueryVo implements Serializable {
         private User user;
         public User getUser() {
             return user;
         }
         public void setUser(User user) {
             this.user = user;
         }
     }
  2. 编写持久层接口

     public interface IUserDao {
         /**
          * 根据 QueryVo 中的条件查询用户
          * @param vo
          * @return
          */
         List findByVo(QueryVo vo);
     }
  3. 持久层接口的映射文件

     
     
  4. 测试包装类作为参数

     @Test
     public void testFindByQueryVo() {
         QueryVo vo = new QueryVo();
         User user = new User();
         user.setUserName("%王%");
         vo.setUser(user);
         List users = userDao.findByVo(vo);
         for(User u : users) {
             System.out.println(u);
         }
     }

Mybatis的输出结果封装

resultType配置结果类型

resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。

需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须
使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)

同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装

  1. 基本类型示例

    Dao接口

     /**
      * 查询总记录条数
      * @return
      */
     int findTotal();

    映射配置

     
     
  2. 实体类类型示例

    Dao接口

     /**
      * 查询所有用户
      * @return
      */
     List findAll();

    映射配置

     
     
  3. 特殊情况示例

    实体类属性和数据库表的列名不一致

    修改映射配置

    使用别名查询

     
     

    如果查询很多,都使用别名的话写起来很麻烦

resultMap结果类型

resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。

在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询

  1. 定义 resultMap

     
     
         
         
         
         
         
     
    
     说明:
     id 标签:用于指定主键字段
     result 标签:用于指定非主键字段
     column 属性:用于指定数据库列名
     property 属性:用于指定实体类属性名称
  2. 映射配置

     
     

SqlMapConfig.xml配置文件

配置内容

  1. SqlMapConfig.xml中配置的内容和顺序

     -properties(属性)
         --property
     -settings(全局配置参数)
         --setting
     -typeAliases(类型别名)
         --typeAliase
         --package
     -typeHandlers(类型处理器)
     -objectFactory(对象工厂)
     -plugins(插件)
     -environments(环境集合属性对象)
         --environment(环境子属性对象)
             ---transactionManager(事务管理)
             ---dataSource(数据源)
     -mappers(映射器)
         --mapper
         --package

properties(属性)

在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。

  1. 第一种

     
         
         
         
         
     
  2. 第二种

    1. 在classpath下定义db.properties文件

       jdbc.driver=com.mysql.jdbc.Driver
       jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
       jdbc.username=root
       jdbc.password=mysql
    2. properties标签配置

       
       
    3. 此时我们的dataSource标签就编程了引用上面的配置

       
           
           
           
           
       

typeAliases(类型别名)

采用自定义别名方式

  1. 自定义别名

    在 SqlMapConfig.xml 中配置:

     
         
         
         
         
         
     

mappers(映射器)

  1. 使用相对于类路径的资源,如:

     
  2. 使用mapper接口类路径,如:

     

    注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在用一个目录中。

  3. 注册指定包下的所有mapper接口,如:

     

    注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中

Mybatis连接池与事务深入

Mybatis的连接池技术

SqlMapConfig.xml 配置文件中, 通过 来实现 Mybatis 中连接池的配置

  1. Mybatis连接池的分类

    Mybatis将数据源分为三类:

    • UNPOOLED 不使用连接池的数据源
    • POOLED 使用连接池的数据源
    • JNDI 使用 JNDI 实现的数据源
  2. Mybatis中数据源的配置

    SqlMapConfig.xml

     
     
         
         
         
         
     

    MyBatis 在初始化时, 根据 的 type 属性来创建相应类型的的数据源 DataSource,即:

    type=”POOLED”: MyBatis 会创建 PooledDataSource 实例

    type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例

    type=”JNDI”: MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用

  3. Mybatis中DataSource的存取

    MyBatis是通过工厂模式来创建数据源DataSource对象的,MyBatis定义了抽象的工厂接口: org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源 DataSource

  4. Mybatis中连接的获取过程分析

    当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建java.sql.Connection对象。也就是说, java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。

     @Test
     public void testSql() throws Exception {
         InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
         SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
         SqlSession sqlSession = factory.openSession();
         List list = sqlSession.selectList("findUserById",41);
         System.out.println(list.size());
     }

    只有当第 4 句 sqlSession.selectList("findUserById"),才会触发 MyBatis 在底层执行下面这个方法来创建 java.sql.Connection 对象

Mybatis的事务控制

  1. JDBC中的事务控制

    在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以调整。

  2. Mybatis中事务提交方式

    Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。

  3. Mybatis 自动提交事务的设置

     //创建 SqlSession 对象
     session = factory.openSession(true);

Mybatis的动态SQL语句

标签

根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同空时还要加入用户名作为条件。这种情况在多条件组合查询中经常会碰到。

  1. 持久层Dao接口

     List findByUser(User user);
  2. 持久层Dao映射配置

     

    注意: 标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
    另外要注意 where 1=1 的作用

  3. 测试

     @Test
     public void testFindByUser() {
         User u = new User();
         u.setUsername("%王%");
         u.setAddress("%顺义%");
         //6.执行操作
         List users = userDao.findByUser(u);
         for(User user : users) {
             System.out.println(user);
         }
     }

标签

为了简化上面 where 1=1 的条件拼装,我们可以采用 标签来简化开发

  1. 持久层Dao映射配置

     
     

    标签

  2. 需求

    传入多个 id 查询用户信息,用下边两个 sql 实现:

     SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
     SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16)

    这样在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。

    1. 在 QueryVo 中加入一个 List 集合用于封装参数

       public class QueryVo implements Serializable {
           private List ids;
           public List getIds() {
               return ids;
           }
           public void setIds(List ids) {
               this.ids = ids;
           }
       }
  3. 持久层接口

     List findInIds(QueryVo vo);
  4. 持久层Dao映射配置

     
     

    SQL语句:

     select 字段 from user where id in (?)

    标签用于遍历集合,它的属性:

     collection:代表要遍历的集合元素,注意编写时不要写#{}
     open:代表语句的开始部分
     close:代表结束部分
     item:代表遍历集合的每个元素,生成的变量名
     sperator:代表分隔符

Mybatis中简化编写的SQL片段

Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

  1. 定义代码片段

     
     
         select * from user
     
  2. 引用代码片段

     
     
    
     
     

Mybatis多表查询之 一对多

本次案例主要以最为简单的用户和账户的模型来分析 Mybatis 多表关系。用户为 User 表,账户为Account表。一个用户(User)可以有多个账户(Account)

一对一查询(多对一)

需求

查询所有账户信息,关联查询下单用户信息。

注意:

因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户
  1. 方式一

    1. 定义账户信息的实体类

       public class Account implements Serializable {
           private Integer id;
           private Integer uid;
           private Double money;
           public Integer getId() {
               return id;
           }
           public void setId(Integer id) {
               this.id = id;
           }
           public Integer getUid() {
               return uid;
           }
           public void setUid(Integer uid) {
               this.uid = uid;
           }
           public Double getMoney() {
               return money;
           }
           public void setMoney(Double money) {
               this.money = money;
           }
           @Override
           public String toString() {
               return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
           }
       }
    2. 编写sql语句

      实现查询账户信息时,也要查询账户所对应的用户信息

       SELECT
           account.*,
           user.username,
           user.address
       FROM
           account,
           user
       WHERE account.uid = user.id
    3. 定义 AccountUser 类

      为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 User 类。

       public class AccountUser extends Account implements Serializable {
           private String username;
           private String address;
           public String getUsername() {
               return username;
           }
           public void setUsername(String username) {
               this.username = username;
           }
           public String getAddress() {
               return address;
           }
           public void setAddress(String address) {
               this.address = address;
           }
           @Override
           public String toString() {
               return super.toString() + " AccountUser [username=" + username + ", address=" + address + "]";
           }
       }
    4. 定义账户的持久层Dao接口

       List findAll();
    5. 定义 AccountDao.xml 文件中的查询配置信息

       
       
       
           
           
       

      注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType 的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。

    6. 测试
    7. 小结

      定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

  2. 方式二

    使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。

    通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户是哪个用户的

    1. 修改Account类

      在 Account 类中加入 User 类的对象作为 Account 类的一个属性。

    2. 修改 AccountDao 接口中的方法

       List findAll();

      注意:第二种方式,将返回值改 为了 Account 类型。因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息

    3. 重新定义 AccountDao.xml 文件

      
      
      
          
          
              
              
              
              
              
                  
                  
                  
                  
                  
              
          
          
      
    4. 测试

一对多查询

需求:

查询所有用户信息及用户关联的账户信息。

分析:

用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适
  1. 编写SQL语句

     SELECT
         u.*, acc.id id,
         acc.uid,
         acc.money
     FROM
         user u
     LEFT JOIN account acc ON u.id = acc.uid
  2. User 实体类加入 List

  3. 用户持久层 Dao 接口中加入查询方法

     List findAll();
  4. 用户持久层 Dao 映射文件配置

     
     
     
         
             
             
             
             
             
             
             
                 
                 
                 
             
         
    
         
         
     
    
    
     说明:
     collection
         部分定义了用户关联的账户信息。表示关联查询结果集
     property="accList":
         关联查询的结果集存储在 User 对象的上哪个属性。
     ofType="account":
         指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。
  5. 测试

Mybatis多表查询之多对多

实现Role到User多对多

  1. 业务要求及实现 SQL

    需求:

     实现查询所有对象并且加载它所分配的用户信息。

    分析:

     查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。

    SQL

     SELECT
         r.*,u.id uid,
         u.username username,
         u.birthday birthday,
         u.sex sex,
         u.address address
     FROM
         ROLE r
     INNER JOIN
         USER_ROLE ur
     ON ( r.id = ur.rid)
     INNER JOIN
         USER u
     ON (ur.uid = u.id);
  2. 编写角色实体类

     public class Role implements Serializable {
         private Integer roleId;
         private String roleName;
         private String roleDesc;
         //多对多的关系映射:一个角色可以赋予多个用户
         private List users;
         public List getUsers() {
             return users;
         }
         public void setUsers(List users) {
             this.users = users;
         }
         public Integer getRoleId() {
             return roleId;
         }
         public void setRoleId(Integer roleId) {
             this.roleId = roleId;
         }
         public String getRoleName() {
             return roleName;
         }
         public void setRoleName(String roleName) {
             this.roleName = roleName;
         }
         public String getRoleDesc() {
             return roleDesc;
         }
         public void setRoleDesc(String roleDesc) {
             this.roleDesc = roleDesc;
         }
         @Override
         public String toString() {
             return "Role{" + "roleId=" + roleId + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}';
         }
     }
  3. 编写Role持久层接口

     List findAll();
  4. 编写映射文件

     
     
     
         
         
             
             
             
             
                 
                 
                 
                 
                 
             
         
    
         
         
     
  5. 测试

实现User到Role的多对多

从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。

Mybatis 延迟加载策略

何为延迟加载

  1. 延迟加载:

    就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

  2. 坏处:

    因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

实现需求

查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。

使用 assocation 实现延迟加载

  1. 账户的持久层DAO接口

     List findAll();
  2. 账户的持久层映射文件

     
     
     
         
         
             
             
             
             
             
             
         
         
     
    
     说明:
     select: 填写我们要调用的 select 映射的 id
     column : 填写我们要传递给 select 映射的参数
  3. 用户的持久层接口和映射文件

     public interface IUserDao {
     /**
      * 根据 id 查询
      * @param userId
      * @return
      */
     User findById(Integer userId);
     }
    
    
     
     
     
         
         
     
  4. 开启Mybatis的延迟加载策略

    在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。

     
     
         
         
     

使用 Collection 实现延迟加载

在一对多关系配置的结点中配置延迟加载策略。

结点中也有 select 属性, column 属性。

  1. 在 User 实体类中加入 List属性

  2. 编写用户和账户持久层接口的方法

     List findByUid(Integer uid);
  3. 编写用户持久层映射配置

     
         
         
         
         
         
         
         
         
     
    
     
     
    
    
     说明:
     标签:
     主要用于加载关联的集合对象
     select 属性:
     用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id
     column 属性:
     用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一个字段名了
  4. 编写账户持久层映射配置

     
     

Mybatis缓存

Mybatis一级缓存

一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

  1. 一级缓存的分析

    一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除, commit(), close()等方法时,就会清空一级缓存。

Mybatis二级缓存

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

  1. 二级缓存的开启与关闭

    1. 在 SqlMapConfig.xml 文件开启二级缓存

       
           
           
       

      因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
      false 代表不开启二级缓存。

    2. 配置相关的 Mapper 映射文件

      标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

       
       
       
           
           
       
    3. 配置 statement 上面的 useCache 属性

       
       

      将 UserDao.xml 映射文件中的