MyBatis框架学习_04

MyBatis延迟加载策略

延迟加载的定义

  • 延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
  • 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
  • 坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

实现需求

  • 需求: 查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
  • 在03实现多表操作时,我们使用了resultMap来实现一对一,一对多,多对多关系的操作。主要是通过association、collection实现一对一及一对多映射。association、collection具备延迟加载功能。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
─src
├─main
│ ├─java
│ │ └─wzc
│ │ ├─dao
│ │ │ IAccountDao.java
│ │ │ IUserDao.java
│ │ │
│ │ └─domain
│ │ Account.java
│ │ User.java
│ │
│ └─resources
│ │ jdbcConfig.properties
│ │ log4j.properties
│ │ SqlMapConfig.xml
│ │
│ └─wzc
│ └─dao
│ IAccountDao.xml
│ IUserDao.xml

└─test
└─java
└─wzc
└─test
AccountTest.java
UserTest.java

使用assocation实现延迟加载

  1. 账户持久层DAO接口的查询方法

    1
    2
    3
    4
    5
    /**
    * 查询所有账户,同时还要获取到当前账户的所属用户信息
    * @return
    */
    List<Account> findAll();
  1. 账户的持久层映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?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="wzc.dao.IAccountDao">
    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
    <id property="id" column="id"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 配置延迟加载
    select属性指定的内容:查询用户的唯一标识:
    column属性指定的内容:用户根据id查询时,所需要的参数的值
    -->
    <association property="user" column="uid" javaType="user" select="wzc.dao.IUserDao.findById"></association>

    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
    SELECT * FROM account
    </select>
    </mapper>
    • select: 填写我们要调用的 select 映射的 id
    • column : 填写我们要传递给 select 映射的参数
  2. 用户的持久层DAO接口的查询方法

    1
    2
    3
    4
    5
    6
    /**
    * 根据id查询用户信息
    * @param userId
    * @return
    */
    User findById(Integer userId);
  3. 用户的持久层映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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="wzc.dao.IUserDao">
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="int" resultType="user">
    SELECT * FROM user WHERE id = #{uid};
    </select>
    </mapper>
  1. SqlMapConfig.xml中开启Mybatis的延迟加载策略

    1
    2
    3
    4
    5
    6
    <!-- 配置参数 -->
    <settings>
    <!-- 开启Mybatis支持延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>
  1. 测试只查账户信息不查用户信息的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class AccountTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws Exception {
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //3.获取SqlSession对象
    sqlSession = factory.openSession();
    //4.获取dao的代理对象
    accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After//用于在测试方法执行之后执行
    public void destroy() throws Exception {
    //要手动提交事务(autocommit默认为false)
    sqlSession.commit();
    //6.释放资源
    sqlSession.close();
    in.close();
    }

    /**
    * 测试查询所有
    */
    @Test
    public void testFindAll(){
    List<Account> accounts = accountDao.findAll();
    }
    }

使用Collection实现延迟加载

  • 同样我们也可以在一对多关系配置的结点中配置延迟加载策略。 结点中也有select属性,column属性。
  • 需求: 完成加载用户对象时,查询该用户所拥有的账户信息。
  1. 在User实体类中加入List属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //一对多关系映射:主表实体应该包含从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
    return accounts;
    }

    public void setAccounts(List<Account> accounts) {
    this.accounts = accounts;
    }
  1. 用户持久层DAO接口方法

    1
    2
    3
    4
    5
    /**
    * 查询所有用户,同时获取到用户下所有账户的信息
    * @return
    */
    List<User> findAll();
  1. 账户持久层DAO接口方法

    1
    2
    3
    4
    5
    6
    /**
    * 根据用户id查询账户信息
    * @param uid
    * @return
    */
    List<Account> findAccountByUid(Integer uid);
  1. 用户持久层映射配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!-- 定义User的ResultMap -->
    <resultMap id="userAccountMap" type="user">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="address" column="address"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <!-- 配置user对象中accounts集合的映射 -->
    <collection property="accounts" ofType="account" select="wzc.dao.IAccountDao.findAccountByUid" column="id">
    </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
    SELECT * FROM user
    </select>
    • 标签: 主要用于加载关联的集合对象
    • select属性: 用于指定查询account列表的sql语句,所以填写的是该sql映射的id
    • column属性: 用于指定select属性的sql语句的参数来源,上面的参数来自于user的id列,所以就写成id这一个字段名了
  2. 账户持久层映射配置

    1
    2
    3
    4
    <!-- 根据用户id查询账户列表 -->
    <select id="findAccountByUid" resultType="account">
    SELECT * FROM account WHERE uid = #{uid}
    </select>
  1. 测试只加载用户信息的方法

    1
    2
    3
    4
    @Test
    public void testFindAll(){
    List<User> users = userDao.findAll();
    }

MyBatis缓存

  • 像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。

  • Mybatis中缓存分为一级缓存,二级缓存。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
─src
├─main
│ ├─java
│ │ └─wzc
│ │ ├─dao
│ │ │ IUserDao.java
│ │ │
│ │ └─domain
│ │ User.java
│ │
│ └─resources
│ │ jdbcConfig.properties
│ │ log4j.properties
│ │ SqlMapConfig.xml
│ │
│ └─wzc
│ └─dao
│ IUserDao.xml

└─test
└─java
└─wzc
└─test
SecondLevelCacheTest.java
UserTest.java

MyBatis一级缓存

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

    • 用户持久层DAO接口的方法

      1
      2
      3
      4
      5
      6
      /**
      * 根据id查询用户信息
      * @param userId
      * @return
      */
      User findById(Integer userId);
  • 用户持久层映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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="wzc.dao.IUserDao">
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="int" resultType="user" useCache="true">
    SELECT * FROM user WHERE id = #{uid};
    </select>
    </mapper>
  • 测试方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 测试一级缓存
    */
    @Test
    public void testFirstLevelCache(){
    User user1 = userDao.findById(41);
    System.out.println("第一次查询的用户:" + user1);

    User user2 = userDao.findById(41);
    System.out.println("第二次查询的用户:" + user2);

    System.out.println(user1 == user2);
    }
    • 测试结果: true. 对数据库进行了一次查询
    • 分析: 虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
  1. 一级缓存的分析

    • 一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
    • 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
    • 得到用户信息,将用户信息存储到一级缓存中。
    • 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
  1. 测试一级缓存的清空

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        @Test
    public void testFirstLevelCache(){
    User user1 = userDao.findById(41);
    System.out.println(user1);

    // sqlSession.close();

    //再次获取SqlSession对象
    // sqlSession = factory.openSession();
    sqlSession.clearCache();//此方法也可以清空缓存

    //再次获取dao代理对象
    userDao = sqlSession.getMapper(IUserDao.class);

    User user2 = userDao.findById(41);
    System.out.println(user2);

    System.out.println(user1 == user2);
    }
    • 测试结果: false. 对数据库进行了两次查询
    • 当执行sqlSession.close()后,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql 语句,从数据库第二次进行了查询操作。

MyBatis二级缓存

  • 二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
  1. 二级缓存结构
  • 首先开启mybatis的二级缓存。
  • sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
  • 如果SqlSession3去执行相同 mapper映射下sql,执行commit提交,将会清空该 mapper映射下的二级缓存区域的数据。
  • sqlSession2去查询与sqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

  • 第一步:在SqlMapConfig.xml文件开启二级缓存

    1
    2
    3
    <settings>
    <setting name="cacheEnabled" value="true"/>
    </settings>
  • 第二步:配置相关的Mapper映射文件

    1
    2
    3
    4
    <mapper namespace="wzc.dao.IUserDao">
    <!-- 开启user支持二级缓存 -->
    <cache/>
    </mapper>
    • 标签表示当前这个mapper映射将使用二级缓存,区分的标准就看mapper的namespace值。
  • 第三步:配置statement上面的useCache属性

    1
    2
    3
    4
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="int" resultType="user" useCache="true">
    SELECT * FROM user WHERE id = #{uid};
    </select>
  • 将映射文件中的select标签中设置useCache=”true”代表当前这个statement要使用二级缓存,如果不使用二级缓存可以设置为false。

  • 注意:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
  1. 二级缓存测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public class SecondLevelCacheTest {

    private InputStream in;
    private SqlSessionFactory factory;

    @Before//用于在测试方法执行之前执行
    public void init() throws Exception {
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    factory = new SqlSessionFactoryBuilder().build(in);

    }

    @After//用于在测试方法执行之后执行
    public void destroy() throws Exception {

    in.close();
    }

    /**
    * 测试一级缓存
    */
    @Test
    public void testFirstLevelCache(){
    SqlSession sqlSession1 = factory.openSession();
    IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
    User user1 = dao1.findById(41);
    System.out.println(user1);
    sqlSession1.close();//一级缓存消失

    SqlSession sqlSession2 = factory.openSession();
    IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
    User user2 = dao2.findById(41);
    System.out.println(user2);
    sqlSession2.close();

    System.out.println(user1 == user2);
    }
    }
    • 测试结果: false. 且只对数据库进行了一次查询
    • 执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出sql语句,所以此时的数据就只能是来自于二级缓存。
  2. 二级缓存注意事项

    • 当我们在使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,这种就可以使用序列化方式来保存对象。

MyBatis注解开发

  • 这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。

MyBatis的常用注解说明

  • @Insert:实现新增
  • @Update:实现更新
  • @Delete:实现删除
  • @Select:实现查询
  • @Result:实现结果集封装
  • @Results:可以与
  • @Result一起使用,封装多个结果集
  • @ResultMap:实现引用
  • @Results定义的封装
  • @One:实现一对一结果集封装
  • @Many:实现一对多结果集封装
  • @SelectProvider: 实现动态SQL映射
  • @CacheNamespace:实现注解二级缓存的使用

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
─src
├─main
│ ├─java
│ │ └─wzc
│ │ ├─dao
│ │ │ IUserDao.java
│ │ │
│ │ └─domain
│ │ User.java
│ │
│ └─resources
│ │ jdbcConfig.properties
│ │ log4j.properties
│ │ SqlMapConfig.xml
│ │
│ └─wzc
│ └─dao
└─test
└─java
└─wzc
└─test
AnnotationCRUDTest.java
MybatisAnnoTest.java



使用MyBatis注解实现基本CRUD

  1. 实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class User implements Serializable { 
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    public Integer getUserId() { return userId; }
    public void setUserId(Integer userId) { this.userId = userId; }
    public String getUserName() { return userName; }
    public void setUserName(String userName) { this.userName = userName; }
    public Date getUserBirthday() { return userBirthday; }
    public void setUserBirthday(Date userBirthday) { this.userBirthday = userBirthday; }
    public String getUserSex() { return userSex;}
    public void setUserSex(String userSex) { this.userSex = userSex; }
    public String getUserAddress() { return userAddress; }
    public void setUserAddress(String userAddress) { this.userAddress = userAddress; } @Override
    public String toString() {
    return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex=" + userSex + ", userAddress=" + userAddress + "]";
    }
    }
    • 注意: 此处故意和数据库表的列名不一致。
  2. 使用注解方式开发持久层DAO接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    public interface IUserDao {

    /**
    * 查询所有用户
    * @return
    */
    @Select("SELECT * FROM user")
    @Results(id="userMap",
    value={
    @Result(id=true,column="id",property="userId"),
    @Result(column="username",property="userName"),
    @Result(column="sex",property="userSex"),
    @Result(column="address",property="userAddress"),
    @Result(column="birthday",property="userBirthday")
    })
    List<User> findAll();

    /**
    * 保存用户
    * @param user
    */
    @Insert("INSERT INTO user(username, address, sex, birthday) VALUES (#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
    * 更新用户
    * @param user
    */
    @Update("UPDATE user SET username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} WHERE id=#{id}")
    void updateUser(User user);

    /**
    * 删除用户
    * @param userId
    */
    @Delete("DELETE FROM user WHERE id=#{id}")
    void deleteUser(Integer userId);

    /**
    * 根据id查询用户
    * @param userId
    * @return
    */
    @Select("SELECT * FROM user WHERE id=#{id}")
    User findById(Integer userId);

    /**
    * 根据用户名模糊查询
    * @param username
    * @return
    */
    // @Select("SELECT * FROM user WHERE username LIKE #{username}")
    @Select("SELECT * FROM user WHERE username LIKE '%${value}%'")
    List<User> findUserByName(String username);

    /**
    * 查询总用户数量
    * @return
    */
    @Select("SELECT COUNT(*) FROM user")
    int findTotalUser();
    }
    • 通过注解方式,就不需要再去编写UserDao.xml 映射文件了。
  3. SqlMapConfig 配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?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>
    <!-- 引入外部配置文件 -->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- 配置别名 -->
    <typeAliases>
    <package name="wzc.domain"></package>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
    <environment id="mysql">
    <transactionManager type="JDBC"></transactionManager>
    <dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
    </dataSource>
    </environment>
    </environments>
    <!-- 指定带有注解的dao接口所在位置 -->
    <mappers>
    <package name="wzc.dao"></package>
    </mappers>
    </configuration>
  1. 测试方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    public class AnnotationCRUDTest {

    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;

    @Before
    public void init() throws Exception {
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    factory = new SqlSessionFactoryBuilder().build(in);
    session = factory.openSession();
    userDao = session.getMapper(IUserDao.class);

    }

    @After
    public void destroy() throws Exception {
    session.commit();
    session.close();
    in.close();
    }

    @Test
    public void testSave(){
    User user = new User();
    user.setUsername("mybatis annotation");
    user.setAddress("南京市鼓楼区");

    userDao.saveUser(user);
    }

    @Test
    public void testUpdate(){
    User user = new User();
    user.setId(56);
    user.setUsername("mybatis annotation update");
    user.setAddress("南京市鼓楼区");
    user.setSex("男");
    user.setBirthday(new Date());

    userDao.updateUser(user);
    }


    @Test
    public void testDelete(){

    userDao.deleteUser(54);
    }

    @Test
    public void testFindOne(){

    User user = userDao.findById(56);
    System.out.println(user);
    }

    @Test
    public void testFindByName(){

    // List<User> users = userDao.findUserByName("%mybatis%");//占位符 SELECT * FROM user WHERE username LIKE ?
    List<User> users = userDao.findUserByName("mybatis");//字符串拼接方式 SELECT * FROM user WHERE username LIKE '%mybatis%'

    for (User user : users) {
    System.out.println(user);
    }

    }

    @Test
    public void testFindTotal(){
    int total = userDao.findTotalUser();
    System.out.println(total);
    }
    }

使用注解实现复杂关系映射开发

  • 实现复杂关系映射之前我们可以在映射文件中通过配置来实现,在使用注解开发时我们需要借助@Results注解,@Result注解,@One注解,@Many注解。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
─src
├─main
│ ├─java
│ │ └─wzc
│ │ ├─dao
│ │ │ IAccountDao.java
│ │ │ IUserDao.java
│ │ │
│ │ └─domain
│ │ Account.java
│ │ User.java
│ │
│ └─resources
│ jdbcConfig.properties
│ log4j.properties
│ SqlMapConfig.xml

└─test
└─java
└─wzc
└─test
AccountTest.java
AnnotationCRUDTest.java
SecondLevelCacheTest.java
  1. 复杂关系映射的注解说明

    • @Results注解

      代替的是标签

      该注解中可以使用单个@Result注解,也可以使用@Result集合

      @Results({@Result(),@Result()})或@Results(@Result())

    • @Result注解

      代替了 标签和标签

      @Result 中 属性介绍:

      ​ id 是否是主键字段

      ​ column 数据库的列名

      ​ property需要装配的属性名

      ​ one 需要使用的@One注解(@Result(one=@One)()))

      ​ many 需要使用的@Many注解(@Result(many=@many)()))

    • @One注解(一对一)

      代替了标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。

      @One注解属性介绍:

      ​ select 指定用来多表查询的sqlmapper

      ​ fetchType会覆盖全局的配置参数lazyLoadingEnabled。

      使用格式: @Result(column=” “,property=””,one=@One(select=””))

    • @Many注解(多对一) 代替了标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。

      注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;

      使用格式: @Result(property=””,column=””,many=@Many(select=””))

  2. 使用注解实现一对一复杂关系映射及延迟加载

  • User实体类及Account实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    /**
    * 用户实体类
    */
    public class User implements Serializable{

    private Integer userId;
    private String userName;
    private String userAddress;
    private String userSex;
    private Date userBirthday;
    public Integer getUserId() {
    return userId;
    }

    public void setUserId(Integer userId) {
    this.userId = userId;
    }

    public String getUserName() {
    return userName;
    }

    public void setUserName(String userName) {
    this.userName = userName;
    }

    public String getUserAddress() {
    return userAddress;
    }

    public void setUserAddress(String userAddress) {
    this.userAddress = userAddress;
    }

    public String getUserSex() {
    return userSex;
    }

    public void setUserSex(String userSex) {
    this.userSex = userSex;
    }

    public Date getUserBirthday() {
    return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
    this.userBirthday = userBirthday;
    }

    @Override
    public String toString() {
    return "User{" +
    "userId=" + userId +
    ", userName='" + userName + '\'' +
    ", userAddress='" + userAddress + '\'' +
    ", userSex='" + userSex + '\'' +
    ", userBirthday=" + userBirthday +
    '}';
    }
    }

    /**
    * 账户实体类
    */
    public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //多对一(mybatis中称之为一对一)的映射:一个账户只能属于一个用户
    private User user;

    public User getUser() {
    return user;
    }

    public void setUser(User user) {
    this.user = user;
    }

    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 +
    '}';
    }
    }
  • 账户的持久层DAO接口并使用注解配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public interface IAccountDao {

    /**
    * 查询所有账户,并且获取每个账户所属的用户信息
    * 注意:多对一@Result@One注解,fetchType = FetchType.EAGER
    * 一对多@Result@Many注解,fetchType = FetchType.LAZY
    * @return
    */

    @Select("SELECT * FROM account")
    @Results(id = "accountMap", value = {
    @Result(id = true,column = "id",property = "id"),//id=true表示该列为主键
    @Result(column = "uid",property = "uid"),
    @Result(column = "money",property = "money"),
    @Result(property = "user",
    column = "uid",
    one = @One(select = "wzc.dao.IUserDao.findById",fetchType = FetchType.EAGER)),
    })
    List<Account> findAll();
    }
  • 用户的持久层DAO接口并使用注解配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public interface IUserDao {

    /**
    * 查询所有用户
    * @return
    */
    @Select("SELECT * FROM user")
    @Results(id = "userMap", value = {
    @Result(id = true,column = "id",property = "userId"),
    @Result(column = "username",property = "userName"),
    @Result(column = "address",property = "userAddress"),
    @Result(column = "sex",property = "userSex"),
    @Result(column = "birthday",property = "userBirthday")
    })
    List<User> findAll();

    /**
    * 根据id查询用户
    * @param userId
    * @return
    */
    @Select("SELECT * FROM user WHERE id=#{id}")
    // @ResultMap(value = {"userMap"})//只有一个属性value可省略,数组中只有一个元素{}也可省略
    @ResultMap("userMap")
    User findById(Integer userId);
    }
  • 测试一对一关联及延迟加载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void testFindAll(){
    List<Account> accounts = acountDao.findAll();
    /*for (Account account : accounts) {
    System.out.println("----------每个账户的信息-----------");
    System.out.println(account);
    System.out.println(account.getUser());
    }*/
    }
  1. 使用注解实现一对多复杂关系映射
    • 需求: 查询用户信息时,也要查询他的账户列表。使用注解方式实现。
    • 分析: 一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
  • User实体类中加入List

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //一对多关系映射:一个用户对应多个账户
    private List<Account> accounts;

    public List<Account> getAccounts() {
    return accounts;
    }

    public void setAccounts(List<Account> accounts) {
    this.accounts = accounts;
    }
  • 用户的持久层DAO接口并使用注解配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public interface IUserDao {

    /**
    * 查询所有用户
    * @return
    */
    @Select("SELECT * FROM user")
    @Results(id = "userMap", value = {
    @Result(id = true,column = "id",property = "userId"),
    @Result(column = "username",property = "userName"),
    @Result(column = "address",property = "userAddress"),
    @Result(column = "sex",property = "userSex"),
    @Result(column = "birthday",property = "userBirthday"),
    @Result(property = "accounts",column = "id",
    many = @Many(select = "wzc.dao.IAccountDao.findAccountByUid",
    fetchType = FetchType.LAZY))
    })
    List<User> findAll();
    }
    • @Many: 相当于的配置
      • select属性:代表将要执行的sql语句
      • fetchType属性:代表加载方式,一般如果要延迟加载都设置为LAZY的值
  • 账户的持久层DAO接口并使用注解配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface IAccountDao {
    /**
    * 根据用户id查询账户信息
    * @param userId
    * @return
    */
    @Select("SELECT * FROM account WHERE uid = #{userId}")
    List<Account> findAccountByUid(Integer userId);
    }
  • 测试方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class AnnotationCRUDTest {

    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;

    @Before
    public void init() throws Exception {
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    factory = new SqlSessionFactoryBuilder().build(in);
    session = factory.openSession();
    userDao = session.getMapper(IUserDao.class);

    }

    @After
    public void destroy() throws Exception {
    session.commit();
    session.close();
    in.close();
    }

    @Test
    public void testFindAll() {
    List<User> users = userDao.findAll();
    for (User user : users) {
    System.out.println("------每个用户的信息--------");
    System.out.println(user);
    System.out.println(user.getAccounts());
    }
    }
    }

MyBatis基于注解的二级缓存

  1. 在SqlMapConfig中开启二级缓存支持

    1
    2
    3
    4
    <!-- 配置开启二级缓存 -->
    <settings>
    <setting name="cacheEnabled" value="true"/>
    </settings>
  1. 在持久层DAO接口中使用注解配置二级缓存

    1
    2
    3
    4
    @CacheNamespace(blocking = true)//mybatis基于注解方式实现配置二级缓存
    public interface IUserDao {
    ...
    }
0%