MyBatis-Plus

简介 | MyBatis-Plus (baomidou.com)

MyBatis-plus +MyBatis

简单的整合mybatis里操作数据库的方法

  • 将对应的Mapper对象继承BaseMapper(使其能调用BaseMpper里的操作方法)
  • 使用MyBatis-plus 的方式产生SqlSessionFactory
  • 最后调用BaseMapper的方法操作数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testUserList() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new
//这里使用的是MP中的MybatisSqlSessionFactoryBuilder
MybatisSqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 可以调用BaseMapper中定义的方法
List<User> list = userMapper.selectList(null);
for (User user : list) {
System.out.println(user);
}
}

子项目都继承了父项目来引用mybatis-plus

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>

MyBatis-plus +Spring

整合Spring+MyBatis-plus主要是解析数据源,将配置信息写在spring的配置文件中

pom文件中需要导入spring的mybatis-plus的配置

itcast-mybatis-plus-spring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--扫描配置源    -->
<context:property-placeholder location="classpath:*.properties"/>
<!-- 定义数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="maxActive" value="10"/>
<property name="minIdle" value="5"/>
</bean>
<!--这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper接口,使用的依然是Mybatis原生的扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mp.simple.mapper"/>
</bean>

spring也是同样引用mybatis-plus

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>

Springboot+Mybatis-plus

需要在application.yml的配置文件中配置数据源,而其他的映射或者扫描器springboot已经注解封装完成不需要自己重复写

1
2
3
4
5
6
7
# 配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: nian0209

pom文件里springboot项目需要引用父类文件

spring-boot-starter-parent 里面封装了springboot项目的启动对应的配置文件

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

引用mybatis-plus需要引用springboot对应的mybatis-plus配置信息

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>

BaseMapper提供的CRUD

@TableField

在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
1、对象中的属性名和字段名不一致的问题(非驼峰)
2、对象中的属性字段在表中不存在的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@TableName("tb_user") //指定数据库表名
public class User {
@TableId(type = IdType.AUTO) //主键自增长
private Long id;
private String userName;
@TableField(select = false) //查询不显示详细值
private String password;
private String name;
private Integer age;
@TableField(value = "email") //字段名不一样
private String email;
@TableField(exist = false)
private String address; //该字段在数据库中不存在
}

更新操作

在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新

根据id更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//根据id更新
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;

@Test
public void testUpdateById() {
User user = new User();
user.setId(6L); //主键
user.setAge(21); //更新的字段
//根据id更新,更新不为null的字段
this.userMapper.updateById(user);
}
}
根据条件更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//根据条件更新
//QueryWrapper进行更新 封装成对象 匹配条件进行更新
@Test
public void testUpdate() {
User user = new User();
user.setAge(22); //更新的字段
//更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id", 6);
//执行更新操作
int result = this.userMapper.update(user, wrapper);
System.out.println("result = " + result);
}


//UpdateWrapper进行更新 可以设置set---更新内容的操作
@Test
public void testUpdate() {
//更新的条件以及字段
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 6).set("age", 23);
//set的字段是数据库表字段
//执行更新操作
int result = this.userMapper.update(null, wrapper);
System.out.println("result = " + result);
}

删除操作

根据id删除deleteById
1
2
3
4
5
6
@Test
public void testDeleteById() {
//执行删除操作
int result = this.userMapper.deleteById(6L);
System.out.println("result = " + result);
}
根据条件删除deleteByMap
1
2
3
4
5
6
7
8
9
@Test
public void testDeleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age",20);
columnMap.put("name","张三");
//将columnMap中的元素设置为删除的条件,多个之间为and关系
int result = this.userMapper.deleteByMap(columnMap);
System.out.println("result = " + result);
}
delete

更加情况分为两种不同参数的删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testDeleteByMap() {
//用法一:
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("user_name","caocao").eq("password","123");

//用法二:
User user = new User();
user.setUserName("caocao");
user,setPassword("123");


int result = this.userMapper.delete(wrapper);
System.out.println("result = " + result);
}
deleyeBetchIds

批量删除 传入的参数需要是一个数组类型

1
2
3
4
5
6
@Test
public void testDeleteByMap() {
//根据id集合批量删除
int result = this.userMapper.deleteBatchIds(Arrays.asList(1L,10L,20L));
System.out.println("result = " + result);
}

查询操作

根据id查询selectById
1
2
3
4
5
6
@Test
public void testSelectById() {
//根据id查询数据
User user = this.userMapper.selectById(2L);
System.out.println("result = " + user);
}
根据数组集合查询数据
1
2
3
4
5
6
7
8
@Test
public void testSelectBatchIds() {
//根据id集合批量查询
List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 10L));
for (User user : users) {
System.out.println(user);
}
}
根据条件查询一条数据selectOne
1
2
3
4
5
6
7
8
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("name", "李四");
//根据条件查询一条数据,如果结果超过一条会报错
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
根据条件查询总记录数
1
2
3
4
5
6
7
8
@Test
public void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄大于23岁
//根据条件查询数据条数
Integer count = this.userMapper.selectCount(wrapper);
System.out.println("count = " + count);
}
分页查询selectPage
1
2
3
4
5
6
7
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

需要进行分页必须写一个mybatis提供的分页插件

MybatisPlusInterceptor MybatisPlus的分页拦截器

MybatisPlus自带的分页插件

1
2
3
4
5
6
7
8
9
@Configuration //标记为配置类
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  @Test
public void testSelectPage() {

//分页条件
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 20); //年龄大于20岁

Page<User> page = new Page<>(1,1);//参数1:第几页,参数二:每页但是条数据
//根据条件查询数据
IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数:" + iPage.getTotal());
System.out.println("总页数:" + iPage.getPages());

List<User> users = iPage.getRecords();//获得当前页集合数据
for (User user : users) {
System.out.println("user = " + user);
}
}

QueryWrapper各参数

参数

SQL注入原理

MP在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中

在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类

img

在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?>
mapperClass) {
Class<?> modelClass = extractModelClass(mapperClass);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache =
GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
List<AbstractMethod> methodList = this.getMethodList();
if (CollectionUtils.isNotEmpty(methodList)) {
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant,
modelClass);
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass,
modelClass, tableInfo));
} else {
logger.debug(mapperClass.toString() + ", No effective injection method
was found.");
}
mapperRegistryCache.add(className);
}
}
}

在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass,
tableInfo)); 是关键,循环遍历方法,进行注入。

1
2
3
4
5
6
7
8
9
10
/**
* 注入自定义 MappedStatement
*
* @param mapperClass mapper 接口
* @param modelClass mapper 泛型
* @param tableInfo 数据库表反射信息
* @return MappedStatement
*/
public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?
> modelClass, TableInfo tableInfo);

后置内容再添加