MyBaits-Plus 3.X
文章目录
一.简述
1. 简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2. 特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作#支持数据库
二.快速启动
1. Spring Boot
- 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
- 配置:启动类添加
MapperScan
注解
@SpringBootApplication
//启动类添加此注解
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(QuickStartApplication.class, args);
}
}
- 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Resource
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
}
2. Spring MVC
- 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.2.0</version>
</dependency>
- 配置
spring
配置文件中配置MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
</bean>
mybatis
配置文件中,修改SqlSessionFactory
为MyBatis-Plus
的SqlSessionFactory
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring.xml"})
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
@Test
public void testSequence() {
User u = new User();
u.setName("Tomcat");
u.setAge(18);
u.setEmail("[email protected]");
userMapper.insert(u);
Long id1 = u.getId();
u = new User();
u.setName("Tomcat2");
userMapper.insert(u);
Assert.assertEquals("id should increase 1", id1 + 1, u.getId().longValue());
}
@Test
public void testCustomizedSql() {
System.out.println("maxAge=" + userMapper.selectMaxAge());
}
@Test
public void testPagination() {
Page<User> page = new Page<>(1, 3);
userMapper.selectPage(page, null);
Assert.assertTrue("total should not be 0", page.getTotal() != 0);
for (User u : page.getRecords()) {
System.out.println(u);
}
Assert.assertEquals("pagination should be 3 per page", 3, page.getRecords().size());
}
}
三.核心功能
1. 代码生成器
官网链接:代码生成器
- 作用:基于模板生成代码
- 支持模板类型
- Velocity(默认)
- Freemarker
- Beetl
- 自定义模板
2. CRUD接口
官网链接:CRUD接口
//1. 继承BaseMapper,泛型中传入这个表的实体类
//2. 在其他地方通过@Autowire注入即可
public interface UserMapper extends BaseMapper<User> {}
//1. 继承IService,泛型中传入这个表的实体类
//2. 在其他地方通过@Autowire注入即可
public interface UserServcie extends IService<User> {}
- 参数:
参数 | 作用 |
---|---|
T entity | 保存的时候用 |
IPage page | 分页(需要配置分页插件) |
wrapper(条件构造器) | 用于生成sql的where条件 |
Collection entityList | 批量保存或者更新 |
Collection<? extends Serializable> idList | 批量删除 |
Map<String, Object> | 查询的时候用 |
3. 条件构造器
官网链接: 条件构造器
- 作用:用于生成sql的where条件
- api
查询方式 | 说明 |
---|---|
setSqlSelect | 设置 SELECT 查询字段 |
where | WHERE 语句,拼接 + WHERE 条件 |
and | AND 语句,拼接 + AND 字段=值 |
andNew | AND 语句,拼接 + AND (字段=值) |
or | OR 语句,拼接 + OR 字段=值 |
orNew | OR 语句,拼接 + OR (字段=值 |
eq | 等于= |
allEq | 基于 map 内容等于= |
ne | 不等于<> |
gt | 大于> |
ge | 大于等于>= |
lt | 小于< |
le | 小于等于<= |
like | 模糊查询 LIKE |
notLike | 模糊查询 NOT LIKE |
in | IN 查询 |
notIn | NOT IN 查询 |
isNull | NULL 值查询 |
isNotNull | IS NOT NULL |
groupBy | 分组 GROUP BY |
having | HAVING 关键词 |
orderBy | 排序 ORDER BY |
orderAsc | ASC 排序 ORDER BY |
orderDesc | DESC 排序 ORDER BY |
exists | EXISTS 条件语句 |
notExists | NOT EXISTS 条件语句 |
between | BETWEEN 条件语句 |
notBetween | NOT BETWEEN 条件语句 |
addFilter | 自由拼接 SQL |
last | 拼接在最后,例如:last(“LIMIT 1”) |
- 参数
参数 | 作用 |
---|---|
boolean condition | 表示该条件是否加入最后生成的sql中 |
R column | 表示数据库字段(String类型) |
T entity | 查询的时候把entity需要查询的字段set好,然后传入,相当于where and and。。。 |
Collection entityList | 批量保存或者更新 |
Collection<? extends Serializable> idList | 批量删除 |
Map<String, Object> | 和实体类作用差不多 |
String sql | 用来拼接sql |
- 注意:
- 不支持
rpc调用
将Wrapper
进行传输(wrapper很重,非要用可以写dto) - 传入的map或者list为null,则不加入最后sql中
- sql拼接有两种:
apply
和last
,其中last
有sql注入风险
- 不支持
- 分类
- AbstractWrapper :
QueryWrapper
和UpdateWrapper
的父类 - QueryWrapper :比
AbstractWrapper
多一个select(String... sql)
方法 - UpdateWrapper :比
AbstractWrapper
多一个set(String column , Object val)
方法
- AbstractWrapper :
- 使用wrapper自定义sql
4. 分页插件
官网链接: 分页插件
- 配置
- spring mvc
<!-- spring xml 方式 -->
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="sqlParser" ref="自定义解析类、可以没有" />
<property name="dialectClazz" value="自定义方言类、可以没有" />
</plugin>
</plugins>
- spring boot
//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
return paginationInterceptor;
}
}
- 使用
- 测试类
@Resource
private UserMapper userMapper;
@Test
public void queryUserForPage(){
IPage<User> userPage = new Page<>(2, 2);//参数一是当前页,参数二是每页个数
userPage = userMapper.selectPage(userPage, null);
List<User> list = userPage.getRecords();
for(User user : list){
System.out.println(user);
}
}
- 向前端返回json
//----------------------包装类,用来保存分页需要的数据------------------------
@Data
public class UserVo {
private Integer current;
private Integer size;
private Long total;
private List<User> userList;
}
//--------------------------------Controller返回---------------------------------
@GetMapping("queryUser")
@ResponseBody
public UserVo queryList(Integer current, Integer size) {
/**
* 这些代码应该写在service层
*/
UserVo userVo = new UserVo();
IPage<User> page = new Page<>(current, size);
userMapper.selectPage(page, null);
userVo.setCurrent(current);
userVo.setSize(size);
userVo.setTotal(page.getTotal());
userVo.setUserList(page.getRecords());
return userVo;
}
5. Sequence主键
官网链接: Sequence主键
- 作用:唯一主键,保证全局不重复
- 步骤
- 实体类添加两个注解:
@KeySequence
和@TableId(value = "id", type = IdType.INPUT)
- 注:
oracle
这里是input
,mysql
是auto
- 注:
@Data
@KeySequence("SEQ_USER")
public class User {
@TableId(value = "id", type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
}
- 添加配置
@Configuration
public class MybatisPlusConfig {
/**
* sequence主键,需要配置一个主键生成器
* 配合实体类注解 {@link KeySequence} + {@link TableId} type=INPUT
* @return
*/
@Bean
public H2KeyGenerator h2KeyGenerator(){
return new H2KeyGenerator();
}
}
四.扩展功能
1. 逻辑删除
官网链接: 逻辑删除
-
效果:
- 删除时:
update user set deleted=1 where id =1 and deleted=0
- 查找时:
select * from user where deleted=0
- 删除时:
-
添加配置:如果与默认配置相同,则不需要配置
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
- 实体类添加字段配置
@TableLogicprivate
Integer deleted;
2. 自动填充
官网链接: 自动填充
- 实体类添加注解
/**
* 创建时间
*/
@TableField(fill=FieldFill.INSERT)
private Date gmtCreate;
/**
* 修改时间
*/
@TableField(fill=FieldFill.INSERT_UPDATE)
private Date gmtModified;
- 定义处理类
public class MybatisObjectHandler extends MetaObjectHandler{
@Override
public void insertFill(MetaObject metaObject) {
//新增时填充的字段
setFieldValByName("gmtCreate", new Date(), metaObject);
setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//更新时 需要填充字段
setFieldValByName("gmtModified", new Date(), metaObject);
}
}
- 添加全局配置
<!----------------------------------------------xml配置------------------------------------------------->
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!--
AUTO->`0`("数据库ID自增")QW
INPUT->`1`(用户输入ID")
ID_WORKER->`2`("全局唯一ID")
UUID->`3`("全局唯一ID")
-->
<property name="idType" value="2" />
<property name="metaObjectHandler" ref="mybatisObjectHandler"></property>
</bean>
<bean id="mybatisObjectHandler" class="cn.lqdev.learning.mybatisplus.samples.config.MybatisObjectHandler"/>
//------------------------------------------------srpingboot配置------------------------------------------------
public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws IOException {
MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
//加载数据源
mybatisPlus.setDataSource(dataSource);
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
//配置填充器
globalConfig.setMetaObjectHandler(new MetaObjectHandlerConfig());
mybatisPlus.setGlobalConfig(globalConfig);
return mybatisPlus;
}
3. sql注入器
官网链接:sql注入器
- 创建自定义sql方法类
MyInsertAll
,继承AbstractMethod
public class MyInsertAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "insert into %s %s values %s";
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
StringBuilder valueSql = new StringBuilder();
valueSql.append("#{").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x->{
fieldSql.append(x.getColumn()).append(",");
valueSql.append("#{").append(x.getProperty()).append("},");
});
fieldSql.delete(fieldSql.length()-1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
valueSql.insert(0, "(");
valueSql.delete(valueSql.length()-1, valueSql.length());
valueSql.append(")");
SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sql, tableInfo.getTableName(), fieldSql.toString(), valueSql.toString()), modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "myInsertAll", sqlSource, new NoKeyGenerator(), null, null);
}
}
- 创建sql注入类,继承
MyLogicSqlInjector
,并继承DefaultSqlInjector
, 复写getMethodList
方法;并配置
public class MyLogicSqlInjector extends DefaultSqlInjector {
/**
* 如果只需增加方法,保留MP自带方法
* 可以super.getMethodList() 再add
* @return
*/
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new MyInsertAll());
return methodList;
}
}
- 编写自定义BaseMapper接口
MyBaseMapper<T>
继承BaseMapper<T>
,添加自定义sql方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
/**
* 自定义通用方法
*/
int myInsertAll(T entity);
}
- 业务层mapper继承自定义baseMapper
MyBaseMapper
public interface UserMapper extends MyBaseMapper<User> { }
- 使用
@RunWith(SpringRunner.class)
@SpringBootTest
public class DeluxeTest {
@Resource
private UserMapper mapper;
@Test
public void myInsertAll(){
long id =1008888L;
User u = new User().setEmail("[email protected]").setVersion(1).setDeleted(0).setId(id);
mapper.myInsertAll(u);
User user = mapper.selectById(id);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getCreateTime());
}
}
4. 动态数据源
官网链接:动态数据源
- 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
- 添加配置
spring:
datasource:
dynamic:
datasource:
master:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
schema: db/schema.sql
slave_1:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
slave_2:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
slave_3:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
- serviceImpl或者mapper接口的方法上添加依赖
@DS
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
//不配置,就是默认数据源
@Override
public void addUser(User user) {
userMapper.addUser(user.getName(), user.getAge());
}
@DS("slave_1")
@Override
public List selectUsersFromDs() {
return userMapper.selectUsers();
}
@DS("slave")
@Override
public List selectUserFromDsGroup() {
return userMapper.selectUsers();
}
}
5. ActiveRecord 模式
实体类
继承Model类
即可使用CRUD接口
/**
* 通用CURD示例
* @author oKong
*
*/
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
@SpringBootTest
@Slf4j
public class GeneralTest {
@Autowired
IUserService userService;
@Test
public void testInsert() {
User user = new User();
user.setCode("001");
user.setName("okong-insert");
//默认的插入策略为:FieldStrategy.NOT_NULL,即:判断 null
//对应在mapper.xml时写法为:<if test="field!=null">
//这个可以修改的,设置字段的@TableField(strategy=FieldStrategy.NOT_EMPTY)
//所以这个时候,为null的字段是不会更新的,也可以开启性能插件,查看sql语句就可以知道
userService.insert(user);
//新增所有字段,
userService.insertAllColumn(user);
log.info("新增结束");
}
@Test
public void testUpdate() {
User user = new User();
user.setCode("101");
user.setName("oKong-insert");
//这就是ActiveRecord的功能
user.insert();
//也可以直接 userService.insert(user);
//更新
User updUser = new User();
updUser.setId(user.getId());
updUser.setName("okong-upd");
updUser.updateById();
log.info("更新结束");
}
@Test
public void testDelete() {
User user = new User();
user.setCode("101");
user.setName("oKong-delete");
user.insert();
//删除
user.deleteById();
log.info("删除结束");
}
@Test
public void testSelect() {
User user = new User();
user.setCode("201");
user.setName("oKong-selecdt");
user.insert();
log.info("查询:{}",user.selectById());
}
}
6. 乐观锁
官网链接:乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
- 添加配置
<!--------------------------------xml方式配置--------------------------------->
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
//--------------------------------springboot配置-----------------------------------
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
- 注解实体类中代表version字段(表中也要有)
@Version
private Integer version;