全国咨询热线:400-618-4000

Spring Boot中使用MyBatis注解配置详解

创建时间:2018年09月21日17时27分

1.0 之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也遇到不少问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见的情况举例说明用法。

1.1 在做下面的示例之前,先准备一个整合好MyBatis的工程,可参见:使用Springboot整合mybatis

2.0 传参方式

      下面通过几种不同传参方式来实现前文中实现的插入操作。

2.1使用@Param

     使用这种最简单的传参方式,如下:

[Java] 纯文本查看 复制代码
1
2
@Insert("insert into book (bookname) values #{bookname}")
    Integer insertBook(@Param("bookname") String bookname);

这种方式很好理解,@Param 中定义的 bookname 对应了SQL中的  #{bookname}。

2.2 使用Map

[Java] 纯文本查看 复制代码
1
2
@Insert("insert into book (bookname) values #{bookname, jdbcType =VARCHAR}")
    Integer insertBookByMap(Map<String,Object> map );

对于Insert语句中需要的参数,我们只需要在map中填入同名的内容即可,具体如下面代码所示:

[Java] 纯文本查看 复制代码
1
2
3
Map<String, Object> map = new HashMap<>();
       map.put("bookname", "CCC");
       bookDaoMapper.insertBookByMap(map);

2.3 使用对象

除了Map对象,我们也可直接使用普通的Java对象来作为查询条件的传参,比如我们可以直接使用Book对象:

[Java] 纯文本查看 复制代码
1
2
@Insert("insert into book (bookname) values #{bookname}")
    Integer insertBookByPo(Book book);

3.0 增删改查

3.1 MyBatis针对不同的数据库操作分别提供了不同的注解来进行配置,在之前的示例中演示了@Insert,下面针对User表做一组最基本的增删改查作为示例:

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
public interface UserMapper {
        @Select("SELECT * FROM user WHERE name = #{name}")
        User findByName(@Param("name") String name);
        @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
        int insert(@Param("name") String name, @Param("age") Integer age);
        @Update("UPDATE user SET age=#{age} WHERE name=#{name}")
        void update(User user);
        @Delete("DELETE FROM user WHERE id =#{id}")
        void delete(Long id);
    }

在完成了一套增删改查后,单元测试来验证上面操作的正确性:省略。。。

3.2 返回结果的绑定

       对于增、删、改操作相对变化较小。而对于“查”操作,我们往往需要进行多表关联,汇总计算等操作,那么对于查询的结果往往就不再是简单的实体对象了,往往需要返回一个与数据库实体不同的包装类,那么对于这类情况,就可以通过@Results和@Result注解来进行绑定,具体如下:      
[Java] 纯文本查看 复制代码
1
2
3
4
5
6
@Results({
    @Result(property = "name", column = "name"),
    @Result(property = "age", column = "age")
})
@Select("SELECT name, age FROM user")
List<User> findAll();

    4.0 Mybatis注解进行一对多和多对多查询
      4.1模拟的业务查询

      系统中的用户user都有唯一对应的地址信息address,每个用户可以有多量车car,类似如下结构
      
[Java] 纯文本查看 复制代码
1
2
3
4
5
|-- user
    |-- address
    |-- carList
        |-- car1
        |-- car2

      4.2 对应的实体类如下
      
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//省略setter/getter
public class Address {
    private Long id;
    private String province;
    private String city;
}
public class Car {
    private Long id;
    private String color;
    private String name;
    //用户id
    private Long userId;
}
public class User {
    private Long id;
    //地址信息,和用户是一对一的关系
    private Address address;
    //地址id
    private Long addressId;
    //用户拥有的车,和用户是一对多的关系
    private List<Car> cars;
}
    4.3 对应的建表语句和模拟数据如下
   
[SQL] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nick_name` varchar(50) DEFAULT NULL,
  `address_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `address` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `province` varchar(50) DEFAULT NULL,
  `city` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `car` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `color` varchar(50) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO
    `user`
VALUES
    ('1', 'baby', '1'),
    ('2', 'kingboy', '2'),
    ('3', 'boy', '3'),
    ('4', 'kingbaby', '4');
INSERT INTO
    `address`
VALUES
    ('1', '北京', '北京'),
    ('2', '天津', '天津'),
    ('3', '安徽', '宿州'),
    ('4', '广东', '广州');
INSERT INTO
    `car`
VALUES
    ('1', 'green', '路虎', '1'),
    ('2', 'white', '奔驰', '2'),
    ('3', 'blue', '玛莎拉蒂', '4'),
    ('4', 'yellow', '兰博基尼', '4');
  4.4 @One一对一映射
   4.4.1 以获取用户的唯一地址为例,首先我们定义一个根据地址id查询地址的查询方法
   
[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
public interface AddressRepository {
    /**
     * 根据地址id查询地址
     */
    @Select("SELECT * FROM `address` WHERE id = #{id}")
    Address findAddressById(Long id);
}
   4.4.2 然后我们定义一个根据用户id查询用户的方法
   
[Java] 纯文本查看 复制代码
1
2
3
4
public interface UserRepository {
    @Select("SELECT * FROM `user` where id = #{id}")
    User findUserWithAddress(Long id);
}
  4.4.3 这个时候我们查询出来的user对象中的address属性是空的,和address并没有任何关联。 
   那么我们要把user中的addressId传递给AddressRepository的查询地址的方法, 
   然后把查询出的地址对象address赋值给user的address属性,那么我们怎么做呢?
   
[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
public interface UserRepository {
    @Select("SELECT * FROM `user` where id = #{id}")
    @Results({
            @Result(property = "address", column = "address_id",
                    one = @One(select = "com.kingboy.repository.address.AddressRepository.findAddressById"))
    })
    User findUserWithAddress(Long id);
}
    4.4.5 我们要使用@Resutl注解对返回的结果进行配置, 
            - property = “address”, 表示要将返回的查询结果赋值给user的address属性 
            - column = “address_id” 是指将user表中的address_id作为com.kingboy.repository.address.AddressRepository.findAddressById的查询参数 
            - one 表示这是一个一对一的查询 
            - @One(select = “方法全路径) 表示我们调用的方法
    4.5 @Many一对多查询
    4.5.1 以获取用户拥有的所有车car为例,首先我们定义一个根据用户id查询车的查询方法

[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
public interface CarRepository {
    /**
     * 根据用户id查询所有的车
     */
    @Select("SELECT * FROM `car` WHERE user_id = #{userId}")
    List<Car> findCarByUserId(Long userId);
}

    4.5.2 然后我们定义一个根据用户id查询用户的方法
   
[Java] 纯文本查看 复制代码
1
2
3
4
public interface UserRepository {
    @Select("SELECT * FROM `user` where id = #{id}")
    User findUserWithAddress(Long id);
}
    4.5.3 这个时候我们查询出来的user对象中的List属性是空的,和car的查询方法并没有任何关联。 
    那么我们要把user中的用户id传递给CarRepository的查询车的方法, 
    然后把查询出的集合对象List赋值给user的cars属性,那么我们怎么做呢?
   
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
public interface UserRepository {
    /**
     * 查询带有车信息的用户===============演示一对多(关于多对多其实就是两个一对多组成)
     */
    @Select("SELECT * FROM `user` WHERE id = #{id}")
    @Results({
            @Result(property = "cars", column = "id",
                    many = @Many(select = "com.kingboy.repository.car.CarRepository.findCarByUserId"))
    })
    User getUserWithCar(Long id);
}
    4.5.4 我们要使用@Resutl注解对返回的结果进行配置, 
                - property = “cars”, 表示要将返回的查询结果赋值给user的cars属性 
                - column = “id” 是指将user表中的用户主键id作为com.kingboy.repository.address.CarRepository.findCarByUserId的查询参数 
                - many 表示这是一个一对多的查询 
                - @Many(select = “方法全路径) 表示我们调用的方法, 方法参数userId就是上面column指定的列值
    5.0 @One @Many的总结
                首先我们统一下概念:查询Address或Car的方法,接下来统称为User的附属查询。


                共同点: 
                - 无论是一对一还是一对多,都是通过附属查询来实现的,我们需要定义这个附属查询方法。 
                - 在主查询方法中通过@One、@Many指定附属查询方法的全路径。 
                - 都通过column来传递参数给附属方法。


                不同点: 
                - 一对一,那么附属方法返回的是一个单独的对象 
                - 一对多,那么附属方法返回的是一个对象集合




作者:传智播客JavaEE培训学院
首发:http://
Java.itcast.cn/