SpringBoot集成MyBatis通用Mapper4
Posted 一宿君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot集成MyBatis通用Mapper4相关的知识,希望对你有一定的参考价值。
SpringBoot集成MyBatis通用Mapper4
- 1、为什么使用通用Mapper
- 2、**Spring Boot 集成**
- 3、对象关系映射
- 3.1、简单示例
- 3.2、测试
- 3.3、**数据库映射(注解)**
- 3.3.1、**`@NameStyle` 注解(Mapper)**
- 3.3.2、**`@Table` 注解(JPA)**
- 3.3.3、**`@Column` 注解(JPA)**
- 3.3.4、**`@ColumnType` 注解(Mapper)**
- 3.3.5、**`@Transient` 注解(JPA)**
- 3.3.6、**`@Id` 注解(JPA)**
- 3.3.7、**`@KeySql` 注解(Mapper)**
- 3.3.**8、`@GeneratedValue` 注解(JPA)**
- 3.3.9、**`@Version` 注解(Mapper)**
- 3.3.10、**`@RegisterMapper` 注解(Mapper)**
- 3.3.11、**自定义注解**
- 3.4、**主键策略**
- 3.5、**乐观锁**
- 3.6、全局主键
1、为什么使用通用Mapper
我们知道Mybatis是一个完全与数据库打交道的工具,最初我们对于数据库的操作,即使是使用了Mybatis之后,大部分也都是手动编辑xml配置文件,其实这也是很消耗时间的,后来就出现了MyBatis代码生成器(简称MBG),可以根据数据库链接自动逆向生成实体类
、Mapper接口
和XML配置文件
,但是在后续使用MBG的过程中,如果数据库字段比较多,且变化比较频繁的话,就需要反复重新生成代码,并且由于MBG会覆盖生成代码和追加生成XML配置文件,导致每次重新生成的配置文件都要进行大量的比对修改,这就很烦人了,不是我们最初想要的结果。
其实在我们工作当中大多数情况还有一个问题,就是我们实际操作中对于单表的操作,仅仅只是增删改查,根本没有必要生成一大堆sql,这样的话会使xml文件产生大量冗余,内容多,看着不舒服,运行起来也消耗时间内存。
MBG中其实也定义了很多常用的单标操作方法,为了解决上述的问题,也为了兼容MBG的方法避免项目重构频繁,在MBG的基础上
结合了部分JPA
的部分注解就产生了通用Mapper
,通用Mapper可以很简单的获取基础的单表操作方法,也可以很方便的扩展通用方法。
作用及优点:
-
极大的提升开发效率,减少工作量。
-
可以随意按照自己的需要选择通用方法。
-
很方便的开发自己的通用方法。
-
极其方便的使用MyBatis单表的正删改查。
-
支持单表操作,不支持通用的多表联合查询。
-
强调:
实体类中不是数据库中的字段需要添加@Transient注解(排除映射)。
2、Spring Boot 集成
Spring Boot 在微服务领域中已经成为主流。
这里简单介绍通用 Mapper 如何同 Spring Boot 进行集成。
为了能适应各种情况的用法,这里也提供了多种集成方式,基本上分为两大类。
- 基于 starter 的自动配置
- 基于
@MapperScan
注解的手工配置
在 starter 的逻辑中,如果你没有使用
@MapperScan
注解,你就需要在你的接口上增加@Mapper
注解,否则 MyBatis 无法判断扫描哪些接口。
通用Mapper需要MyBatis的依赖!!!
2.1、引入相关依赖
<!--mybatis集成springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--通用mapper集成springboot-->
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<artifactId>mysql-connector-java</artifactId>
<groupId>mysql</groupId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
mapper最新版本号如上所示,你也可以从下面地址查看:
http://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter
注意:引入该 starter 时,和 MyBatis 官方的 starter 没有冲突,但是官方的自动配置不会生效!
如果你需要对特定的通用 Mapper
进行配置,你可以在 Spring Boot 的配置文件中配置 mapper.
前缀的配置。
例如在 yml 格式中配置:
mapper:
mappers:
- com.wbs.dao.Mapper1 #接口1
- com.wbs.dao.Mapper2 #接口2
notEmpty: true
在 properties 配置中:
mapper.mappers=com.wbs.dao.Mapper1,com.wbs.dao.Mapper2
mapper.notEmpty=true
由于 Spring Boot 支持 Relax 方式的参数,因此你在配置 notEmpty
时更多的是用 not-empty
,也只有在 Spring Boot 中使用的时候参数名不必和配置中的完全一致。
2.2、@MapperScan 注解配置
你可以给带有 @Configuration
的类配置该注解,或者直接配置到 Spring Boot 的启动类上,如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.wbs.dao")
public class GeneralMapperApplication
public static void main(String[] args)
SpringApplication.run(GeneralMapperApplication.class, args);
注意:这里使用的 tk.mybatis.spring.annotation.MapperScan
!
你可以直接在 Spring Boot 的配置文件中直接配置通用 Mapper,还可以使用注解中提供的两个属性进行配置:
/**
* 通用 Mapper 的配置,一行一个配置
*
* @return
*/
String[] properties() default ;
/**
* 还可以直接配置一个 MapperHelper bean
*
* @return
*/
String mapperHelperRef() default "";
注意:这两个属性配置方式的优先级更高,所以建议在 Spring Boot 中通过配置文件(或 Environment)配置。
3、对象关系映射
3.1、简单示例
示例针对 MySql 数据库(数据库对主键影响较大,和 insert 关系密切)。
数据库有如下表:
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`name` varchar(255) DEFAULT NULL COMMENT '姓名',
`des` varchar(255) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
对应的 Java 实体类型如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "user_info")
public class UserInfo implements Serializable
private static final long serialVersionUID = 1L;
/**
* 主键id
* @mbg.generated Mon Dec 13 15:55:47 CST 2021
*/
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
/**
* 姓名
* @mbg.generated Mon Dec 13 15:55:47 CST 2021
*/
private String name;
/**
* 描述
* @mbg.generated Mon Dec 13 15:55:47 CST 2021
*/
private String des;
最简单的情况下,只需要一个 @Id
注解标记字段为主键即可。数据库中的字段名和实体类中的字段名是完全相同的,这中情况下实体和表可以直接映射,也就是不需要@Column
注解了。
提醒:如果实体类中没有一个标记
@Id
的字段,当你使用带有ByPrimaryKey(根据主键查询)
的方法时,所有的字段都会作为主键来联合使用,也就会出现类似where id = ? and name = ? and des= ?
的情况,这种情况效率是极低的。
- 后续会介绍代码生成器,可以自动生成上面的实体和下面的接口代码!
通用 Mapper 提供了大量的通用接口,这里以最常用的 Mapper 接口为例
该实体类对应的数据库操作接口如下:
import tk.mybatis.mapper.common.Mapper;
public interface UserInfoMapper extends Mapper<UserInfo>
在SpringBoot中只需在启动类上配置注解@MapperScan("接口路径")
, 能注册并且被扫描到,该接口提供的方法就都可以使用。
一旦继承了Mapper<T>
接口, 所继承的Mapper
接口就拥有了以下通用的方法:
//根据实体类不为null的字段进行查询,条件全部使用=号and条件
List<T> select(T record);
//根据实体类不为null的字段查询总数,条件全部使用=号and条件
int selectCount(T record);
//根据主键进行查询,必须保证结果唯一
//单个字段做主键时,可以直接写主键的值
//联合主键时,key可以是实体类,也可以是Map
T selectByPrimaryKey(Object key);
//插入一条数据
//支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
//优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
int insert(T record);
//插入一条数据,只插入不为null的字段,不会影响有默认值的字段
//支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
//优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
int insertSelective(T record);
//根据实体类中字段不为null的条件进行删除,条件全部使用=号and条件
int delete(T key);
//通过主键进行删除,这里最多只会删除一条数据
//单个字段做主键时,可以直接写主键的值
//联合主键时,key可以是实体类,也可以是Map
int deleteByPrimaryKey(Object key);
//根据主键进行更新,这里最多只会更新一条数据
//参数为实体类
int updateByPrimaryKey(T record);
//根据主键进行更新
//只会更新不是null的数据
int updateByPrimaryKeySelective(T record);
以上是常见的增删改查单表操作,还有很多类似的方法就不一一列举了!
3.2、测试
从 Spring容器中获取到该接口的Bean之后就可以直接使用:
@Autowired
UserInfoMapper userInfoMapper;
/**
* 使用通用mapper中的selectAll方法
*/
@Test
void test1()
List<UserInfo> userInfos = userInfoMapper.selectAll();
for (UserInfo userInfo : userInfos)
System.out.println(userInfo.toString());
/**
* 根据实体类不为null的字段查询总数,条件全部使用=号and条件
*/
@Test
void test3()
int count = userInfoMapper.selectCount(new UserInfo( 1L,"张三","法外狂徒"));
System.out.println("人数为:" + count);
/**
* 根据主键进行查询,必须保证结果唯一
* 单个字段做主键时,可以直接写主键的值
* 联合主键时,key可以是实体类,也可以是Map
*/
@Test
void test4()
UserInfo userInfo1 = new UserInfo();
userInfo1.setId(1L);
UserInfo userInfo = userInfoMapper.selectByPrimaryKey(userInfo1);
System.out.println("主键为1的货是:" + userInfo);
/**
* 插入一条数据
* 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
* 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*/
@Test
void test5()
UserInfo userInfo = new UserInfo();
userInfo.setName("马六");
userInfo.setDes("吃瓜群众");
int result = userInfoMapper.insert(userInfo);
if (result > 0)
System.out.println(userInfo + ":该条数据插入成功!");
/**
* 插入一条数据,只插入不为null的字段,不会影响有默认值的字段
* 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
* 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*/
@Test
void test6()
UserInfo userInfo = new UserInfo();
userInfo.setName("啥七");
//userInfo.setDes("啥也不是");
int result = userInfoMapper.insertSelective(userInfo);
if (result > 0)
System.out.println(userInfo + ":该条数据插入成功!");
/**
* 根据实体类中字段不为null的条件进行删除,条件全部使用=号and条件
*/
@Test
void test7()
UserInfo userInfo = new UserInfo();
userInfo.setName("啥七");
int result = userInfoMapper.delete(userInfo);
if (result > 0)
System.out.println(userInfo + ":该条数据删除成功!");
/**
* 通过主键进行删除,这里最多只会删除一条数据
* 单个字段做主键时,可以直接写主键的值
* 联合主键时,key可以是实体类,也可以是Map
*/
@Test
void test8()
int result = userInfoMapper.deleteByPrimaryKey(4L);
if (result > 0)
System.out.println(4L + "主键:该条数据删除成功!");
/**
* 根据主键进行更新,这里最多只会更新一条数据
* 参数为实体类;参数实体类中字段为null的字段也会更新
*/
@Test
void test9()
UserInfo userInfo = new UserInfo();
userInfo.setId(3L);
userInfo.setName("王五五五五五");
int result = userInfoMapper.updateByPrimaryKey(userInfo);
if (result > 0)
System.out.println(userInfo + ":该条数据更新成功!");
/**
* 根据主键进行更新
* 只会更新参数实体类中不是null的数据
*/
@Test
void test10()
UserInfo userInfo = new UserInfo();
userInfo.setId(2L);
userInfo.setName("李四四四四");
int result = userInfoMapper.updateByPrimaryKeySelective(userInfo);
if (result > 0)
System.out.println(userInfo + ":该条数据更新成功!");
如果想要增加自己写的方法,可以直接在 UserInfoMapper
中增加。
方式1:
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
public interface UserInfoMapper extends Mapper<UserInfo>
/**
* 自定义查询所有方法
* @return
*/
List<UserInfo> selectAll2();
对应的UserInfoMapper.xml
文件:
<?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="com.wbs.dao.UserInfoMapper">
<!--查询所有用户信息-->
<select id="selectAll2" resultType="com.wbs.pojo.UserInfo">
select * from user_info
</select>
</mapper>
方式2(基于JPA注解方式):
import tk.mybatis.mapper.common.Mapper;
public interface UserInfoMapper extends Mapper<UserInfo>
/**
* 手动编写,根据名字查询用户
* @param name
* @return
*/
@Select("select * from user_info where name = #name")
UserInfo selectByName(String name);
上述两种方式均可以实现自定义扩展方法,并且互不影响,在接口中添加其他方法的时候和只用 MyBatis 效果是完全一样的,但是需要注意,在对应的 XML 中,不能出现和继承通用Mapper接口中同名的方法!
-
多态!
在接口中,只要不是通过注解来实现接口方法,接口是允许重名的,真正调用会使用通用 Mapper 提供的方法。
3.3、数据库映射(注解)
上述看到的是最简单的情况,其实实际使用过程中也不会更复杂,下面是更详细的映射配置。
通用 Mapper 中,默认情况下是将实体类字段按照驼峰转下划线
形式的表名列名进行转换。
例如:
实体类的
userName
可以映射到表的user_name
上。如果想要修改默认的转换方式,可以在后续的配置中,修改
style
全局配置。
数据库映射主要涉及到一些注解和全局配置,这里会介绍所有注解,后面会有配置的介绍。
通用 Mapper 默认使用了几个简单的注解,其他 JPA 的注解默认并不支持,但是如果你开发自己的通用方法,你可以使用 JPA 注解或者引入自己的注解。
3.3.1、@NameStyle
注解(Mapper)
这个注解可以在类上进行配置,优先级高于对应的 style
全局配置。
注解支持以下几个选项:
normal, //原值
camelhump, //驼峰转下划线
uppercase, //转换为大写
lowercase, //转换为小写
camelhumpAndUppercase, //驼峰转下划线大写形式
camelhumpAndLowercase, //驼峰转下划线小写形式
使用时,直接在类上配置即可,例如:
@NameStyle(Style.camelhumpAndUppercase)
public class UserInfo
配置该注解后,对该类和其中的字段进行转换时,会将形如 userName
的字段转换为表中的 USER_NAME
字段。
3.3.2、@Table
注解(JPA)
@Table
注解可以配置 name
,catalog
和 schema
三个属性,配置 name
属性后,直接使用提供的表名,不再根据实体类名进行转换。其他两个属性中,同时配置时,catalog
优先级高于 schema
,也就是只有 catalog
会生效。
配置示例如下:
//只要这里对应数据库表名即可,也就是将UserInfoTable实体类映射到user_info数据库表
@Table(name = "user_info")
public class UserInfoTable
3.3.3、@Column
注解(JPA)
@Column
注解支持 name
, insertable
和 updateable
三个属性。
name
配置映射的列名。insertable
对提供的insert
方法有效,设置为false
就不会出现在 SQL 中。updateable
对提供的update
方法有效,设置为false
就不会出现在 SQL 中。
配置示例如:
@Column(name = "user_name")
private String name;
除了直接映射 name
到 user_name
这种用法外,在使用关键字的情况,还会有下面的用法:
//会用英文的``引起来
@Column(name = "`des`")
private String des;
对于关键字这种情况,通用 Mapper 支持自动转换,可以查看后续配置文档中的 wrapKeyword 配置。
3.3.4、@ColumnType
注解(Mapper)
这个注解提供了三个属性:
column
属性和@Column
中的name
属性作用相同,但是@Column
的优先级更高。jdbcType
属性是指定字段的类型。typeHandler
用于设置特殊类型处理器,常见的是枚举。
用法示例如下:
@ColumnType以上是关于SpringBoot集成MyBatis通用Mapper4的主要内容,如果未能解决你的问题,请参考以下文章
spring boot集成MyBatis 通用Mapper 使用总结
Springboot集成mybatis通用Mapper与分页插件PageHelper(推荐)
SpringBoot + MyBatis-Plus +SpringSecurity +Thymeleaf +LayUI 通用业务模块集成安全认证
SpringBoot + MyBatis-Plus +SpringSecurity +Thymeleaf +LayUI 通用业务模块集成安全认证