注解开发

注解的开发方式

使用package指定UserMapper接口所在的包

    <mappers>
        <!--
        加载这个包下所有的映射文件,如果没有映射文件也会读取接口中注解
        <package name="com.tyhxzy.dao"/>
        -->

        <!--如果没有配置文件,也可以直接指定接口的类全名-->
        <mapper class="com.tyhxzy.dao.UserMapper"/>
    </mappers>

@Select查询

根据用户id查询用户

  1. 增加通过id查询用户的方法
  2. 方法上使用注解@Select("SQL语句")
  3. 在SQL语句使用#{id}占位符
package com.tyhxzy.dao;

import com.tyhxzy.entity.User;
import org.apache.ibatis.annotations.Select;

/**
 * DAO的接口
 */
public interface UserMapper {

    /*
    通过id查询用户的方法
     */
    @Select("select * from user where id=#{id}")
    User findUserById(Integer id);

}

效果

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 2(Integer)
DEBUG - <==      Total: 1
User{id=2, username='白骨精', birthday=1992-11-12, sex='女', address='白虎岭白骨洞'}

@Results @Result和 @ResultMap

package com.tyhxzy.dao;

import com.tyhxzy.entity.User;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * DAO的接口
 */
public interface UserMapper {

    /*
    通过id查询user2表中一条记录
    如果有些列的名字与属性名字不对应,需要使用@Results和@Result这2个注解进行关系映射

    @Results 创建一个映射关系,它的属性是一个@Result的数组
    @Result 每个@Result映射一个列和属性
        column:表的列名
        property:实体类的属性名
        id:true表示这是主键列,默认是false
     */
    @Select("select * from user2 where id=#{id}")
    @Results(id = "user2Map", value = {
            @Result(column = "id", property = "id", id = true),
            @Result(column = "user_name", property = "username")
    })
    User findUser2ById(Integer id);

    /**
     * 查询user2表中所有的用户
     * @ResultMap 引用上面方法定义的映射
     */
    @Select("select * from user2")
    @ResultMap("user2Map")
    List<User> findAllUser2();

}

小结

注解 属性 说明
@Select value:查询的SQL语句 执行查询的操作
@Results value:@Result类型的数组 创建一个映射关系
id: 给这个映射定义一个名字
@Result column:列名字 每个指定一列的映射关系
property:实体类的属性名
id:是否是主键列 true 是主键
@ResultMap value:指定上面的@Results的id 引用@Results定义的映射
@CacheNamespace 开启二级缓存

修改用户(@Update)

  1. 写修改方法
  2. 在方法上使用注解@Update("SQL语句")
  3. 占位符使用#
/**
 * 更新一个用户信息
 * #{user对象的属性名}
 */
@Update("update user set birthday=#{birthday}, address=#{address} where id=#{id}")
int updateUser(User user);
//更新用户
@Test
public void testUpdateUser() {
    User user = new User();
    user.setBirthday(Date.valueOf("1998-03-09"));
    user.setAddress("天河区猪吉路");
    user.setId(2);
    int row = userMapper.updateUser(user);
    System.out.println("更新了" + row + "行");
}

删除用户(@Delete)

  1. 编写删除方法
  2. 使用注解@Delete("SQL")
  3. 使用#{id},删除指定的用户
//通过id删除一个用户
@Delete("delete from user where id=#{id}")
int deleteUser(Integer id);
//删除用户
@Test
public void testDeleteUser() {
    int i = userMapper.deleteUser(4);
    System.out.println("删除了" + i + "行记录");
}

@Delete和@Update和@Insert三个注解可以通用,但不建议

新增用户(@Insert)

  1. 在UserMapper接口中,增加新增用户方法
  2. 使用注解@Insert("SQL语句")
/**
 * 添加用户
 */
@Insert("insert into user values(null,#{username},#{birthday},#{sex},#{address})")
int addUser(User user);
//添加一个用户
@Test
public void testAddUser() {
    User user = new User();
    user.setUsername("嫦娥");
    user.setBirthday(Date.valueOf("1999-11-11"));
    user.setAddress("广寒宫");
    user.setSex("女");
    int row = userMapper.addUser(user);
    System.out.println("添加了" + row + "行记录");
}

获取新增主键值@SelectKey

接口代码

/**
 * 添加用户
 @SelectKey注解作用:获取最新插入记录的主键
     keyColumn:表中主键的列名
     keyProperty:实体类中主键对应的属性名
     resultType:主键的数据类型
     before:在插入语句执行前true,后执行false
     statement:写要查询的SQL语句
 */
@Insert("insert into user values(null,#{username},#{birthday},#{sex},#{address})")
@SelectKey(keyColumn = "id", keyProperty = "id", resultType = Integer.class, before = false, statement = "select LAST_INSERT_ID()")
int addUser(User user);

测试

//添加一个用户
@Test
public void testAddUser() {
    User user = new User();
    user.setUsername("嫦娥");
    user.setBirthday(Date.valueOf("1999-11-11"));
    user.setAddress("广寒宫");
    user.setSex("女");
    int row = userMapper.addUser(user);
    System.out.println("添加了" + row + "行记录");
    Integer id = user.getId();
    System.out.println("新加的主键是:" + id);
}

注解小结

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

注解 描述
@Select 用于查询
@Results 创建表的列与实体类的属性映射
@Result 每个定义一列和一个属性的对应的关系
@ResultMap 引用上面@Results定义的映射关系
@Update 更新
@Delete 删除
@Insert 添加
@SelectKey 获取最新添加的主键

一对一

注解 描述 对应xml配置标签
@One 配置一对一的关联查询 association
@Many 配置一对多的关联查询 collection

准备项目

操作步骤

  1. 复制项目为

  2. 修改mybatis-config.xml

    1. 保持延迟加载的配置
    2. 修改mapper为class属性

mybatis-config.xml

<!--开启延迟加载-->
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

UserMapper接口

package com.tyhxzy.dao;


import com.tyhxzy.entity.OrderForm;
import com.tyhxzy.entity.User;

import java.util.List;

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

    /**
     通过id查找用户
     */
    User findUserById(int id);

    /**
     通过id查询用户扩展信息
     */
    UserInfo findUserInfoById(int id);

    /**
     通过userId查询这个用户所有的订单信息
     */
    List<OrderForm> findOrdersByUserId(int userId);

}

步骤

需求

查询1个用户数据,并且采用延迟加载关联查询出用户扩展数据

在UserMapper接口中增加查询方法

  1. 编写方法:通过id查询用户扩展信息

    1. 方法名:findUserInfoById,用做后面方法引用的id
    2. 使用@Select注解编写SQL语句
  2. 编写方法:通过id查询1个用户

    1. @Select编写查询
    2. @Results配置1对1关联映射
    3. @Results内部使用@Result
      1. column属性:user主表中主键id
      2. property属性:对应的用户扩展信息属性userInfo
      3. one属性指定为@One注解
        1. select属性:为对应的方法名 findUserInfoById
        2. fetchType属性:延迟加载类型 FetchType.LAZY

代码

package com.tyhxzy.dao;


import com.tyhxzy.entity.OrderForm;
import com.tyhxzy.entity.User;
import com.tyhxzy.entity.UserInfo;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

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

    /**
     通过id查找用户
     @Result
     column: 指定主表的主键,下一条语句要查询的方法参数值
     property:指定另一方在当前属性类中属性名
     one: 配置一对一的关联关系,值是@One的注解

     @One注解:配置一对一的关联关系
     select:表示下一条要查询的方法名字
     fetchType: 抓取的类型,两个取值
        FetchType.LAZY 表示延迟加载
        FetchType.EAGER 表示及时加载
     */
    @Select("select * from user where id=#{id}")
    @Results({
            @Result(column = "id", property = "id", id=true),  //必须要指定主键的映射
            @Result(column = "id", property = "userInfo",
                    one = @One(select = "findUserInfoById", fetchType = FetchType.LAZY))
    })
    User findUserById(int id);

    /**
     通过id查询用户扩展信息
     */
    @Select("select * from user_info where id=#{id}")
    UserInfo findUserInfoById(int id);

}

测试

  1. 只查询用户名和性别
  2. 同时查询用户扩展信息
//查询一个用户和它的扩展信息,使用延迟加载
@Test
public void testFindUserAndInfo() {
    User user = userMapper.findUserById(1);
    //输出用户的属性
    System.out.println("编号:" + user.getId());
    System.out.println("姓名:" + user.getUsername());
    System.out.println("住址:" + user.getAddress());
    //获取用户的扩展信息
    UserInfo userInfo = user.getUserInfo();
    System.out.println("身高:" + userInfo.getHeight());
    System.out.println("体重:" + userInfo.getWeight());
}

小结

注解 作用
@Select 查询操作
@Results 指定映射,必须要指定主键的映射,不然主键没有值
@Result column:指定主表的主键,下一个要执行的方法的参数值
property:实体类中另一方的属性名
one:@One 指定一对一的关联映射
@One select:下一个要查询的方法名
fetchType:抓取类型,是否使用延迟加载
1. FetchType.LAZY 延迟加载(懒加载)
2. FetchType.EAGER 及时加载

一对多

目标

  1. 查询1号用户数据
  2. 关联查询出1号用户全部订单数据
  3. 采用延迟加载方式实现

UserMapper接口

  1. 通过user_id查询当前用户订单的方法

    1. 使用@Select注解
  2. 修改findUserById()方法,增加1对多延迟加载配置

    1. 在findUserById()方法修改注解

    2. 在@Results注解中添加@Result注解

      1. column: 为user表中的主键id
      2. property:对应用户表中的订单集合属性orders
      3. many为 @Many注解
        1. select:为上面的查询方法名
        2. fetchType:为延迟加载

代码

package com.tyhxzy.dao;


import com.tyhxzy.entity.OrderForm;
import com.tyhxzy.entity.User;
import com.tyhxzy.entity.UserInfo;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

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

    /**
     通过id查找用户
     @Result
     column: 指定主表的主键,下一条语句要查询的方法参数值
     property:指定另一方在当前属性类中属性名
     one: 配置一对一的关联关系,值是@One的注解

     @One和@Many注解:配置一对一或一对多的关联关系
     select:表示下一条要查询的方法名字
     fetchType: 抓取的类型,两个取值
        FetchType.LAZY 表示延迟加载
        FetchType.EAGER 表示及时加载
     */
    @Select("select * from user where id=#{id}")
    @Results({
            //必须要指定主键的映射
            @Result(column = "id", property = "id", id=true),
            //一对一关联映射
            @Result(column = "id", property = "userInfo",
                    one = @One(select = "findUserInfoById", fetchType = FetchType.LAZY)),
            //一对多的关联映射
            @Result(column = "id", property = "orders",
                    many = @Many(select = "findOrdersByUserId", fetchType = FetchType.LAZY))
    })
    User findUserById(int id);

    /**
     通过id查询用户扩展信息
     */
    @Select("select * from user_info where id=#{id}")
    UserInfo findUserInfoById(int id);

    /**
     通过userId查询这个用户所有的订单信息
     */
    @Select("SELECT * FROM order_form WHERE user_id=#{userId}")
    @Results({
            @Result(column = "user_id", property = "userId"),
            @Result(column = "create_time", property = "createTime")
    })
    List<OrderForm> findOrdersByUserId(int userId);

}

测试

  1. 只查用户信息
  2. 查询订单信息
@Test
public void testFindUserAndOrders() {
    User user = userMapper.findUserById(1);
    //输出用户的属性
    System.out.println("编号:" + user.getId());
    System.out.println("姓名:" + user.getUsername());
    System.out.println("住址:" + user.getAddress());
    //访问订单的信息
    List<OrderForm> orders = user.getOrders();
    orders.forEach(System.out::println);
}

查询结果

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1
编号:1
姓名:孙悟空
住址:花果山水帘洞

DEBUG - ==>  Preparing: SELECT * FROM order_form WHERE user_id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 3
OrderForm{oid=1, userId=1, number='10001001', createTime=2020-07-01 09:45:45.0, note='请提前安装'}
OrderForm{oid=2, userId=1, number='10001002', createTime=2020-07-01 09:45:45.0, note='包邮'}
OrderForm{oid=3, userId=1, number='10001003', createTime=2020-07-01 09:45:45.0, note='选择红色'}

小结

@Result注解属性 说明
column 主表的主键,下一个查询方法的参数值
property 在实体类中另一方的属性名
many 指定一对多的关联关系
@Many注解属性 说明
select 下一个查询的方法名字
fetchType 抓取类型:
1. FetchType.LAZY 懒加载
2. FetchType.EAGER 及时加载

连接查询

目标

一对多的表连接查询,自动封装关联对象

需求

  1. 查询所有的订单信息和下订单的用户名
  2. 使用注解的方式表连接查询所有订单列和用户表的姓名列

image-20230525164659938

步骤

  1. 订单对象中包含了用户对象,多对一的关系
  2. 使用@select注解编写表连接的查询语句
  3. 使用@Results和@Result定义将username这一列映射成user.username,指定为对象名.属性名
  4. 在控制台上输出所有的订单对象和订单中的用户对象

代码

将OrderForm中添加User对象的属性

public class OrderForm {

    private Integer oid;   // 主键
    private Integer userId;   // 用户id,外键
    private String number;   // 订单编号
    private Timestamp createTime;  // 下单时间,既有日期又有时间
    private String note;   // 备注
  
    private User user;
}  

接口

/*
查询所有的订单信息和下订单的用户名
 */
@Select("SELECT * FROM order_form o INNER JOIN `user` u ON o.user_id = u.id")
@Results({
        @Result(column = "user_id", property = "userId"),
        @Result(column = "create_time", property = "createTime"),
  
        @Result(column = "id", property = "user.id"),
        @Result(column = "username", property = "user.username"),
        @Result(column = "birthday", property = "user.birthday"),
        @Result(column = "sex", property = "user.sex"),
        @Result(column = "address", property = "user.address")
})
List<OrderForm> findOrdersAndUsername();

测试类

@Test
public void testFindOrdersAndUsername() {
    List<OrderForm> forms = userMapper.findOrdersAndUsername();
    for (OrderForm form : forms) {
        System.out.print(form + "\t");
        System.out.println("用户对象:" + form.getUser());
    }
}

结果

DEBUG - ==>  Preparing: SELECT * FROM order_form o INNER JOIN `user` u ON o.user_id = u.id 
DEBUG - ==> Parameters: 
DEBUG - <==      Total: 5

OrderForm{oid=1, userId=1, number='10001001', createTime=2020-07-01 09:45:45.0, note='请提前安装'}	用户对象:User{id=1, username='孙悟空', birthday=1980-10-24, sex='男', address='花果山水帘洞'}
OrderForm{oid=2, userId=1, number='10001002', createTime=2020-07-01 09:45:45.0, note='包邮'}	用户对象:User{id=1, username='孙悟空', birthday=1980-10-24, sex='男', address='花果山水帘洞'}
OrderForm{oid=3, userId=1, number='10001003', createTime=2020-07-01 09:45:45.0, note='选择红色'}	用户对象:User{id=1, username='孙悟空', birthday=1980-10-24, sex='男', address='花果山水帘洞'}
OrderForm{oid=4, userId=2, number='10001004', createTime=2020-07-01 09:45:45.0, note='购买多件'}	用户对象:User{id=2, username='白骨精', birthday=1999-09-09, sex='女', address='天河区猫吉路'}
OrderForm{oid=5, userId=2, number='10001005', createTime=2020-07-01 09:45:45.0, note='安全带'}	用户对象:User{id=2, username='白骨精', birthday=1999-09-09, sex='女', address='天河区猫吉路'}

小结

如果在注解中使用表连接查询也可以在自动在一个实体类中封装另一个实体类的属性

写法:property="对象名.属性名"

学习总结

  1. 掌握MyBatis多表关联查询
association标签的属性 说明 (1对1)
property 指定另一方的属性名
resultMap 指定另一方的映射
collection的属性 说明(1对多)
property 指定另一方的属性名
resultMap 指定另一方的映射
  1. MyBatis的延迟加载
association标签的属性 说明(1对1)
property 指定另一方的属性名
column 要查询的下一个方法的参数值,通常使用主表的主键
select 指定下一个查询方法的id(名字)
collection标签的属性 说明(1对多)
property 指定另一方的属性名
column 要查询的下一个方法的参数值,通常使用主表的主键
select 指定下一个查询方法的id(名字)
  1. 掌握mybatis注解开发
注解 描述
@Select 查询
@Results 映射表中列与实体类属性的名字之间的关系
@Result 每个注解映射一列和一个属性名
@Update 更新
@Delete 删除
@Insert 添加
@SelectKey 获取最后插入的主键
  1. 注解实现多表关联查询
注解 作用
@Select 查询
@Results 映射表中列与实体类属性的名字之间的关系
@Result column 属性:要查询的下一个方法的参数值,通常使用主表的主键
property 属性:指定另一方的属性名
one 属性:指定一对一的关联
many属性:指定一对多的关联
@One select 属性:指定下一个查询方法的id(名字)
fetchType属性:延迟加载,及时加载
@Many select 属性:指定下一个查询方法的id(名字)
fetchType属性:延迟加载,及时加载
  1. 掌握二级缓存的开启配置

    1. 在核心配置文件中开启:cacheEnabled=true
    2. 实体类要序列化
    3. 在XML配置文件中使用<cache>或接口添加注解@CacheNamespace
posted @ 2023-05-25 16:56  YxinHaaa  阅读(7)  评论(0编辑  收藏  举报