Mybatis其他特性-延迟加载-缓存-注解开发

  • 掌握mybatis延迟加载

  • 掌握mybatis缓存

  • 掌握mybatis注解开发

mybatis延迟加载

延迟加载介绍

延迟加载也叫懒加载。指的是按需加载,在实际用到数据的时候才加载,不需要用到数据的时候不加载。延迟加载的特点先从单表查询,需要时再从关联表查询数据。因为都是单表的操作,对于提升数据库性能有一定的帮助。

案例演示

使用延迟加载,都是在多表关联查询的场景。mybatis框架提供的一对一关联查询配置(association)标签,一对多关联查询配置(collection)标签,支持延迟加载。

association延迟加载

需求

查询全部订单数据,并且关联查询出订单所属的用户数据。使用延迟加载方式实现。

需求实现

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FyxMKRhX-1582185585506)(media/e1593e8ae78f1f367d7b7c9a946269ee.png)]

改造UserMapper.xml,增加根据用户Id查询的sql配置
<!--根据用户Id查询用户-->
<select id="findUserById" parameterType="int" resultType="user">
    SELECT
        u.id,
        u.username,
        u.birthday,
        u.sex,
        u.address
    FROM
        `user` u
    WHERE
        id = #{id}
</select>
改造OrdersMapper.xml,一对一关联查询
<!-- 配置订单到用户的一对一关联关系,说明:
type:要映射的类型
id:唯一标识名称,通过id引用该resultMap
-->
<resultMap type="orders" id="ordersUsersResultMap">
    <!-- 配置订单主键字段对应关系 -->
    <id column="id" property="id"/>
    <!-- 配置订单的普通字段对应关系 -->
    <result column="user_id" property="userId"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- association:配置一对一关联关系,说明:
          1.使用延迟加载方式实现:
              select:配置要调用的查询sql语句id(名称空间+"."+sql语句id)。
              column:配置要传递的参数字段
       -->
    <association property="user" javaType="User"
                 select="cn.itheima.mapper.UserMapper.findUserById"
                 column="user_id">
    </association>

</resultMap>

<!-- 查询全部订单数据,并且关联查询订单所属的用户数据(resultMap)-->
<select id="findAllOrdersAndUserResultMap" resultMap="ordersUsersResultMap">
    SELECT
    o.id,
    o.user_id,
    o.number,
    o.createtime,
    o.note
    FROM
    orders o
</select>
配置sqlMapConfig.xml,开启mybatis延迟加载

打开mybatis官方文档,找到settings配置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIY7F4x7-1582185585507)(media/1539657006281.png)]

  • lazyLoadingEnabled:开启延迟加载开关,默认是false未开启,需要配置为true

  • aggressiveLazyLoading:将积极加载改为消极加载,默认是false消极加载,不需要配置

<!--全局参数配置-->
<settings>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--加载策略:消极加载-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

测试只查询订单数据(不查询用户数据)
/**
 * 测试查询全部订单数据,并且关联查询订单所属的用户数据(resultMap)
 */
@Test
public void findAllOrdersAndUserResultMapTest(){
    //1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取接口代理对象
    OrdersMapper  mapper = sqlSession.getMapper(OrdersMapper.class);

    // 4.执行数据库操作
    List<Orders> list = mapper.findAllOrdersAndUserResultMap();
    for(Orders o:list){
        // 只获取订单数据,此时不会去查询用户数据
        System.out.println("订单id:"+o.getId()+",订单编号:"+o.getNumber()+
                ",订单创建时间:"+o.getCreatetime()+",所属用户id:"+o.getUserId());

    }

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BCUorMVf-1582185585507)(media/15240cd170ec8a29194cc970ce84a6fb.png)]

测试延迟加载用户数据
/**
 * 测试查询全部订单数据,并且关联查询订单所属的用户数据(resultMap)
 */
@Test
public void findAllOrdersAndUserResultMapTest(){
    //1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取接口代理对象
    OrdersMapper  mapper = sqlSession.getMapper(OrdersMapper.class);

    // 4.执行数据库操作
    List<Orders> list = mapper.findAllOrdersAndUserResultMap();
    for(Orders o:list){
        // 只获取订单数据
        System.out.println("订单id:"+o.getId()+",订单编号:"+o.getNumber()+
                ",订单创建时间:"+o.getCreatetime()+",所属用户id:"+o.getUserId());

        // 获取用户数据,延迟加载方式去查询用户数据
        System.out.println("订单所属用户数据:"+o.getUser());

    }

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tqjiMpZ-1582185585508)(media/27762b8b51b50132020e4ad83fd2a4d1.png)]

collection延迟加载

需求

查询全部用户数据,并且关联查询出用户所有的订单数据。使用延迟加载方式实现。

需求实现

改造OrdersMapper.xml,增加根据用户id查询订单数据配置
<!--根据用户id查询订单数据-->
<select id="findOrdersByUserId" parameterType="int" resultType="orders">
    SELECT
        o.id,
        o.user_id,
        o.number,
        o.createtime,
        o.note
    FROM
        orders o
    WHERE
        o.user_id=#{id}
</select>
改造UserMapper.xml,一对多关联查询
<!-- 配置用户到订单的一对多关联关系,说明:
   type:要映射的类型
   id:唯一标识名称,通过id引用该resultMap
 -->
<resultMap type="user" id="usersOrdersResultMap">
    <!-- 配置用户的主键字段对应关系 -->
    <id column="id" property="id"/>
    <!-- 配置用户的普通字段对应关系 -->
    <result column="username" property="username"/>
    <result column="birthday" property="birthday"/>
    <result column="sex" property="sex"/>
    <result column="address" property="address"/>

    <!-- collection:配置一对多关联关系,说明:
        1.使用延迟加载方式实现
            select:配置要调用的查询sql语句id(名称空间+"."+sql语句id)。
            column:配置要传递的参数字段
     -->
    <collection property="ordersList" javaType="List" ofType="Orders"
     select="cn.itheima.mapper.OrdersMapper.findOrdersByUserId"
     column="id"
    >
    </collection>

</resultMap>

<!-- 查询全部用户数据,并且关联查询出用户的所有订单数据 -->
<select id="findAllUsersAndOrders" resultMap="usersOrdersResultMap">
    SELECT
        u.id,
        u.username,
        u.birthday,
        u.sex,
        u.address
    FROM
        `user` u
</select>

配置sqlMapConfig.xml,开启mybatis延迟加载

打开mybatis官方文档,找到settings配置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jwQV9sHY-1582185585508)(media/1539657351663.png)]

  • lazyLoadingEnabled:开启延迟加载开关,默认是false未开启,需要配置为true

  • aggressiveLazyLoading:将积极加载改为消极加载,默认是false消极加载,不需要配置

<!--全局参数配置-->
<settings>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--加载策略:消极加载-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
测试值查询用户数据(不查询订单数据)
/**
 * 测试查询全部用户数据,并且关联查询出用户的所有订单数据
 */
@Test
public void findAllUsersAndOrdersTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    List<User> list = mapper.findAllUsersAndOrders();
    for(User u:list){
        // 只获取用户数据。此时不会查询订单数据
        System.out.println("用户id:"+u.getId()+"用户名称:"+u.getUsername());

        // 获取订单数据。延迟加载方式去查询订单数据
        //System.out.println("用户:"+u.getUsername()+",拥有订单数量:"+u.getOrdersList().size());
    }

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfqF9Npn-1582185585509)(media/8e08316ae5b68d3c3fec2063f60c3dbc.png)]

测试延迟加载订单数据
/**
 * 测试查询全部用户数据,并且关联查询出用户的所有订单数据
 */
@Test
public void findAllUsersAndOrdersTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    List<User> list = mapper.findAllUsersAndOrders();
    for(User u:list){
        // 只获取用户数据。此时不会查询订单数据
        //System.out.println("用户id:"+u.getId()+"用户名称:"+u.getUsername());

        // 获取订单数据。延迟加载方式去查询订单数据
        System.out.println("用户:"+u.getUsername()+",拥有订单数量:"+u.getOrdersList().size());
    }

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtDG1uc8-1582185585509)(media/42d42b1c6a9d64450d13e435ac2883bc.png)]

mybatis缓存

mybatis框架提供了缓存策略,通过缓存策略可以减少查询数据库的次数,提升系统性能。在mybatis框架中缓存分为一级缓存,和二级缓存。

一级缓存

一级缓存介绍

一级缓存是sqlSession范围的缓存,只能在同一个sqlSession内部有效。它本身已经存在,一级缓存可以直接使用。

案例演示

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CzzX9HIi-1582185585510)(media/c44f817b9414ca1ce7b47838af90d9c3.png)]

测试一级缓存

说明:通过同一个sqlSession对象,执行两次相同的查询,观察发出sql语句的次数。

/**
 * 测试一级缓存
 */
@Test
public void OneCacheTest(){

    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 创建用户对象
    User user = new User();
    user.setUsername("%小%");
    user.setSex("1");

    // 第一次查询
    List<User> list = mapper.findUserByNameAndSex(user);
    System.out.println(list);

    System.out.println("-----------------华丽丽分割线--------------------");

    // 第二次查询
    List<User> list1 = mapper.findUserByNameAndSex(user);
    System.out.println(list1);

    // 4.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R6K5TAI1-1582185585510)(media/7ec6f60b49f9000534966eee02706d75.png)]

执行结果说明:

虽然执行了两次查询,但是只执行了一次数据库操作(整个过程只有一条sql语句)。这就是mybatis提供的一级缓存在起作用。因为一级缓存的存在,第二次发起同样的查询,并没有再去查询数据库,而是直接从一级缓存中取数据。

一级缓存分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixeQgK3P-1582185585510)(media/1539654737945.png)]

一级缓存是sqlSession范围缓存。当调用sqlSession的修改、添加、删除、提交、关闭等方法时,一级缓存会被清空。

  • 第一次查询id为1的用户,先去缓存中查找是否有id为1的用户数据。如果没有,从数据库中查询用户数据,并将数据存储到一级缓存中

  • 第二次查询id为1的用户,先去缓存中查找是否有id为1的用户数据。此时缓存中有,直接从缓存中取出用户数据

  • 如果sqlSession执行添加、修改、删除、提交、关闭等操作,清空sqlSession中的一级缓存数据。清空的目的是为了让缓存中存放最新数据,避免脏读

测试一级缓存清空

/**
 * 测试一级缓存
 */
@Test
public void OneCacheTest(){

    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 创建用户对象
    User user = new User();
    user.setUsername("%小%");
    user.setSex("1");

    // 第一次查询
    List<User> list = mapper.findUserByNameAndSex(user);
    System.out.println(list);

    System.out.println("-----------------华丽丽分割线--------------------");

    // sqlSession提交,测试清空一级缓存
    sqlSession.commit();

    // 第二次查询
    List<User> list1 = mapper.findUserByNameAndSex(user);
    System.out.println(list1);

    // 4.释放资源
    sqlSession.close();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uzHazDdX-1582185585511)(media/e5061b4a6bde4597f30ff4661e9dca65.png)]

二级缓存

二级缓存介绍

二级缓存是mapper映射级别缓存,作用范围跨越sqlSession,即可以在多个sqlSession之间共享二级缓存数据。

案例演示

在sqlMapConfig.xml配置开启二级缓存

打开mybatis官方文档,找到settings配置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dSsS9kOO-1582185585511)(media/1539654934947.png)]

<!--全局参数配置-->
<settings>
    <!--配置开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

修改用户实体类对象,实现java.io.Serializable接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s591UrUZ-1582185585511)(media/2aeb7ecf8cfc00cce8f7be40fced1bec.png)]

在UserMapper.xml,开启使用缓存

二级缓存还需要在具体的mapper映射文件中明确开启,这样做的原因是缓存数据要消耗资源,只有在需要使用的时候开启,可以避免资源的过度消耗。

在这里插入图片描述

测试二级缓存

说明:通过两个sqlSession对象,执行两次相同的查询,观察发出sql语句的次数。

/**
 * 测试二级缓存
 */
@Test
public void TwoCacheTest(){

    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession1
    SqlSession sqlSession1 = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);

    // 创建用户对象
    User user = new User();
    user.setUsername("%小%");
    user.setSex("1");

    List<User> list1 = mapper1.findUserByNameAndSex(user);
    System.out.println(list1);

    // 4.释放资源
    sqlSession1.close();

    System.out.println("-----------------华丽丽分割线--------------------");

    // 5.打开SqlSession2
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 6.获取mapper代理对象
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

    // 创建用户对象
    User user2 = new User();
    user2.setUsername("%小%");
    user2.setSex("1");

    List<User> list2 = mapper2.findUserByNameAndSex(user2);
    System.out.println(list2);

    // 7.释放资源
    sqlSession2.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nXZxtPf5-1582185585512)(media/360605621caf5e42fa1f66f2c6cc9cdc.png)]

  • sqlSession1查询,从二级缓存中取数据,缓存命中率为0,即缓存中没有数据

  • 发起sql语句执行数据库操作,取出数据,并把数据存放到二级缓存

  • sqlSession2查询,从二级缓存中取数据,缓存命中率为0.5,缓存中有数据,直接取出

二级缓存分析

  • sqlSession1执行UserMapper中的查询方法,将数据写入UserMapper的二级缓存中

  • sqlSession2执行UserMapper中的查询方法,从二级缓存中取出数据

  • sqlSession3执行UserMapper中的增删改、提交方法,清空UserMapper二级缓存

mybatis注解开发

在第一天mybatis框架的介绍中,mybatis框架是通过xml或者注解方式,配置实现java对象与sql语句的对应关系。也就是说mybatis框架中除了使用xml方式配置,还能使用注解的方式配置。

注解实现基本CRUD操作

需求

通过注解方式实现用户表(user)的增删改查操作。

  1. 新增一个用户

  2. 根据用户id查询用户

  3. 根据用户id修改用户

  4. 根据用户id删除用户

需求实现

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWC9JUNi-1582185585512)(media/836b4ce85eedee5c3ed1955d148c5f01.png)]

新增用户(@Insert)

在UserMapper接口中,增加新增用户方法
package cn.itheima.mapper;

import cn.itheima.po.User;
import org.apache.ibatis.annotations.Insert;

/**
 * 持久层接口:UserMapper
 */
public interface UserMapper {

    /**
     * 1.新增用户
     *
     * 注解说明:
     *      @Insert:相当于xml配置中,insert标签,用于放置新增sql语句
     */
    @Insert("insert into `user`(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
    void addUserAnnotation(User user);

}
测试
package cn.itheima.test;

import cn.itheima.mapper.UserMapper;
import cn.itheima.po.User;
import cn.itheima.util.SqlSessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import java.util.Date;
/**
 * 测试注解开发
 */
public class UserMapperTest {

    /**
 * 测试新增用户
 */
    @Test
    public void addUserAnnotationTest(){
        // 1.获取SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

        // 2.打开SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 3.获取mapper代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 4.执行数据库操作
        // 创建用户对象
        User user = new User();
        user.setUsername("注解开发-新增用户");
        user.setSex("1");
        user.setBirthday(new Date());
        user.setAddress("注解开发-用户地址");

        mapper.addUserAnnotation(user);

        // 5.释放资源
        sqlSession.close();
    }


}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyCgOf3C-1582185585512)(media/ceed7a32ae7107339a91f50907ce9ea3.png)]

根据用户id查询用户(@Select)

在UserMapper接口中,增加查询方法
/**
     * 2.根据用户id查询用户
     * 
     * 注解说明:
     *      @Select:相当于xml配置中,select标签,用于放置查询sql语句
     */
@Select("select * from `user` where id=#{id}")
User findUserByIdAnnotation(Integer id);
测试
/**
 * 测试根据用户id查询用户
 */
@Test
public  void findUserByIdAnnotationTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    User user = mapper.findUserByIdAnnotation(45);
    System.out.println(user);

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hxSeafUi-1582185585512)(media/6d15d96dd3ba3883ae0573b6ab23e976.png)]

根据用户Id修改用户(@Update)

在UserMapper接口中,增加修改方法
  /**
     * 3.根据用户id修改用户
     *
     * 注解说明:
     *      @Update:相当于xml配置中,update标签,用于放置修改sql语句
     */
    @Update("update `user` set username=#{username},sex=#{sex} where id=#{id}")
    void updateUserAnnotation(User user);

测试
/**
 * 测试根据用户id修改用户
 */
@Test
public void updateUserAnnotationTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    // 创建用户对象
    User user = new User();
    user.setId(45);
    user.setUsername("注解开发-新增用户-进行修改操作");
    user.setSex("2");

    mapper.updateUserAnnotation(user);

    // 5.释放资源
    sqlSession.close();
}

在这里插入图片描述

根据用户id删除用户(@Delete)

在UserMapper接口中,增加删除方法
  /**
     * 4.根据用户id删除用户
     *
     * 注解说明:
     *      @Delete:相当于xml配置中,delete标签,用于放置删除sql语句
     */
    @Delete("delete from `user` where id=#{id}")
    void deleteUserAnnotation(Integer id);

测试
/**
 * 测试根据用户id删除用户
 */
@Test
public void deleteUserAnnotationTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    mapper.deleteUserAnnotation(46);

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IsbqtPA3-1582185585513)(media/8cc44954c36cae8924fed46b5ec7c627.png)]

获取数据库维护主键值(@SelectKey)

在addUser方法上,添加@SelectKey注解
/**
 * 1.新增用户
 *
 * 注解说明:
 *      @Insert:相当于xml配置中,insert标签,用于放置新增sql语句
 *      
 *      @SelectKey:相当于xml配置中的selectKey标签,用于获取数据库维护的主键值
 *      属性:
 *          statement:指定查询主键值sql语句
 *          keyColumn:主键字段
 *          keyProperty:主键属性
 *          resultType:主键字段属性类型
 *          before:布尔类型值,true相当于xml配置中的order="BEFORE";false相当于xml配置中的order="AFTER"
 */
@Insert("insert into `user`(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
@SelectKey(statement ={"select LAST_INSERT_ID()"},
        keyColumn = "id",
        keyProperty = "id",
        resultType = Integer.class,
        before = false)
void addUserAnnotation(User user);

测试
/**
 * 测试新增用户
 */
@Test
public void addUserAnnotationTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    // 创建用户对象
    User user = new User();
    user.setUsername("注解开发-新增用户");
    user.setSex("1");
    user.setBirthday(new Date());
    user.setAddress("注解开发-用户地址");

    System.out.println("执行前:"+user);
    mapper.addUserAnnotation(user);
    System.out.println("执行后:"+user);

    // 5.释放资源
    sqlSession.close();
}

在这里插入图片描述

注解小结

在注解方式实现基本CRUD操作中,使用的注解有:

注解 描述 对应xml配置标签
@Insert 配置新增sql语句 <insert/>
@Select 配置查询sql语句 <select/>
@Update 配置修改sql语句 <update/>
@Delete 配置删除sql语句 <delete/>
@SelectKey 查询数据库维护主键值 <selectKey/>

注解高级应用

mybatis框架中注解除了实现基本的CRUD操作以外,还可以实现复杂的关系映射(一对一关联查询,一对多关联查询)。

复杂关系映射注解介绍

注解 描述 对应xml配置标签
@Results 配置java对象属性与sql语句中字段的对应关系,相当于xml配置中的<resultMap>标签 <resultMap/>
@Result 相当于<resultMap>标签中的<id/> 和<result/>子标签 <id/> <result/>
@One 一对一关系映射(延迟加载) <association/>
@Many 一对多关系映射(延迟加载) <collection/>

案例演示

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyGiPrPj-1582185585513)(media/1ce685ada71537658036797c0102493d.png)]

注解实现一对一关联查询

需求:查询全部订单数据,并且关联查询出订单所属的用户数据。

编写OrderMapper接口
package cn.itheima.mapper;

import cn.itheima.po.Orders;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * 订单mapper接口
 */
public interface OrdersMapper {

    /**
     * 查询全部订单数据,并且关联查询出订单所属的用户数据
     * 注解说明:
     *      @Select:
     *          作用:放置查询的sql语句
     *
     *      @Results:
     *          作用:配置java对象属性与sql语句中字段的对应关系,相当于xml配置中的<resultMap>标签
     *
     *      @Result:
     *          作用:相当于<resultMap>标签中的<id/> 和<result/>子标签
     *      属性:
     *          id:布尔类型值,设置是否是主键字段。true是主键字段。默认值是false
     *          column:字段名称
     *          property:属性名称
     */
    @Select("SELECT o.id,o.user_id,o.number,o.createtime,o.note,u.username,u.address "+
            "FROM orders o "+
            "LEFT JOIN `user` u ON o.user_id = u.id"
    )
    @Results({
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "user_id",property = "userId"),
            @Result(column = "number",property = "number"),
            @Result(column = "createtime",property = "createtime"),
            @Result(column = "note",property = "note"),

            @Result(column = "user_id",property = "user.id"),
            @Result(column = "username",property = "user.username"),
            @Result(column = "address",property = "user.address")
    })
    List<Orders> findAllOrdersAndUserAnnotation();
}

测试
package cn.itheima.test;

import cn.itheima.mapper.OrdersMapper;
import cn.itheima.po.Orders;
import cn.itheima.util.SqlSessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import java.util.List;

/**
 * 测试注解开发
 */
public class OrdersMapperTest {

    /**
     * 测试查询全部订单数据,并且关联查询出订单所属的用户数据
     */
    @Test
    public void findAllOrdersAndUserAnnotationTest(){
        // 1.获取SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

        // 2.打开SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 3.获取mapper代理对象
        OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);

        // 4.执行数据库操作
        List<Orders> list = mapper.findAllOrdersAndUserAnnotation();
        for(Orders o:list){
            System.out.println("订单编号:"+o.getNumber()+",所属用户信息:"+o.getUser());
        }

        // 5.释放资源
        sqlSession.close();
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5riUZHK-1582185585513)(media/acd639f090a19a193168b7358078b0ca.png)]

注解实现一对一关联查询,延迟加载实现

需求:查询全部订单数据,并且关联查询出订单所属的用户数据。采用延迟加载实现。

在OrderMapper接口中增加查询方法
/**
 * 查询全部订单数据,并且关联查询出订单所属的用户数据(延迟加载方式实现)
 *
 * 注解说明:
 *      @Select:
 *          作用:放置查询sql语句
 *      @Results:
 *          作用:配置java对象属性与sql语句中的字段名称对应关系,相当于xml中的<resultMap/>标签
 *      @Result:
 *          作用:相当于<resultMap/>标签的<id/>和<result/>子标签
 *          属性:
 *              id:设置是否是主键字段。true主键字段;默认是false
 *              column:字段名称
 *              property:属性名称
 *              one:配置一对一关联查询(延迟加载)
 *      @One:
 *          作用:配置延迟加载一对一关联查询
 *          属性:
 *              select:关联查询sql语句(接口全限定名称+"."+接口方法名称)
 *              fetchType:配置延迟加载方式
 */
@Select("SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o")
@Results(value = {
        @Result(id = true, column = "id", property = "id"),
        @Result(column = "user_id", property = "userId"),
        @Result(column = "number", property = "number"),
        @Result(column = "createtime", property = "createtime"),
        @Result(column = "note", property = "note"),

        @Result(column = "user_id", property = "user",
                one = @One(select = "cn.itheima.mapper.UserMapper.findUserByIdAnnotation", fetchType = FetchType.LAZY)
        )
})
List<Orders> findAllOrdersAndUserAnnotationLazy();

测试
/**
 * 测试查询全部订单数据,并且关联查询出订单所属的用户数据(延迟加载方式实现)
 */
@Test
public void findAllOrdersAndUserAnnotationLazyTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);

    // 4.执行数据库操作
    List<Orders> list = mapper.findAllOrdersAndUserAnnotationLazy();
    for(Orders o:list){
        System.out.println("订单编号:"+o.getNumber()+",订单所属用户:"+o.getUser());
    }

    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ozzyii8V-1582185585514)(media/29abc4d937217491b03076d97156d8a7.png)]

注解实现一对多关联查询

需求:查询全部用户数据,并且关联查询出用户的全部订单数据。采用延迟加载方式实现。

在UserMapper接口中,增加查询方法
/**
 * 5.查询全部用户数据,并且关联查询出用户的全部订单数据
 *
 * 注解说明:
 *      @Select:
 *          作用:配置查询sql语句
 *      @Results:
 *          作用:配置java对象属性名称与sql语句字段名称对应关系,相当于xml配置中<resultMap/>标签
 *      @Result:
 *          作用:相当于<resultMap/>标签的<id/>和<result/>子标签
 *          属性:
 *              id:设置是否主键字段。true是主键字段。默认false
 *              column:字段名称
 *              property:属性名称
 *              many:配置一对多关联关系,相当于xml配置中<collection/>标签
 *      @Many:
 *          作用:配置一对多关联查询
 *          属性:
 *              select:指定关联查询sql语句(接口的全限定名称+"."+接口方法名称)
 *              fetchType:配置延迟加载方式
 */
@Select("SELECT u.id,u.username,u.birthday,u.sex,u.address FROM `user` u ")
@Results({
        @Result(id=true,column = "id",property = "id"),
        @Result(column = "username",property = "username"),
        @Result(column = "birthday",property = "birthday"),
        @Result(column = "sex",property = "sex"),
        @Result(column = "address",property = "address"),

        @Result(column = "id",property = "ordersList",
                many = @Many(select = "cn.itheima.mapper.OrdersMapper.findAllOrdersByUserIdAnnotation",
                        fetchType = FetchType.LAZY))
})
List<User> findAllUsersAndOrdersAnnotation();

在OrderMapper接口中增加根据用户Id查询方法
/**
 * 根据用户id查询订单数据
 *
 * 注解说明:
 *      @Select:配置查询sql语句
 */
@Select("select * from orders where user_id=#{id}")
List<Orders> findAllOrdersByUserIdAnnotation(Integer id);

测试
/**
 * 查询全部用户数据,并且关联查询出用户的全部订单数据
 */
@Test
public  void findAllUsersAndOrdersAnnotationTest(){
    // 1.获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();

    // 2.打开SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 3.获取mapper代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 4.执行数据库操作
    List<User> list = mapper.findAllUsersAndOrdersAnnotation();
    for(User u:list){
        System.out.println("用户名称:"+u.getUsername()+",用户订单:"+u.getOrdersList());
    }


    // 5.释放资源
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBhqXnzP-1582185585514)(media/d319ad7b5ad8f247ca5a460c9ccad37d.png)]

发布了20 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ZHONGZEWEI/article/details/104412266