MyBatis-Plus
简介 | MyBatis-Plus (baomidou.com)
MyBatis-plus +MyBatis
简单的整合mybatis里操作数据库的方法
- 将对应的Mapper对象继承BaseMapper(使其能调用BaseMpper里的操作方法)
- 使用MyBatis-plus 的方式产生SqlSessionFactory
- 最后调用BaseMpper的方法操作数据库
@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
<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
<!--扫描配置源 -->
<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
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>
Springboot+Mybatis-plus
需要在application.yml的配置文件中配置数据源,而其他的映射或者扫描器springboot已经注解封装完成不需要自己重复写
# 配置数据源
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项目的启动对应的配置文件
<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配置信息
<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、对象中的属性字段在表中不存在的问题
@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更新
//根据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);
}
}
根据条件更新
//根据条件更新
//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
@Test
public void testDeleteById() {
//执行删除操作
int result = this.userMapper.deleteById(6L);
System.out.println("result = " + result);
}
根据条件删除deleteByMap
@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
更加情况分为两种不同参数的删除
@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
批量删除 传入的参数需要是一个数组类型
@Test
public void testDeleteByMap() {
//根据id集合批量删除
int result = this.userMapper.deleteBatchIds(Arrays.asList(1L,10L,20L));
System.out.println("result = " + result);
}
查询操作
根据id查询selectById
@Test
public void testSelectById() {
//根据id查询数据
User user = this.userMapper.selectById(2L);
System.out.println("result = " + user);
}
根据数组集合查询数据
@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
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("name", "李四");
//根据条件查询一条数据,如果结果超过一条会报错
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
根据条件查询总记录数
@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
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
需要进行分页必须写一个mybatis提供的分页插件
MybatisPlusInterceptor MybatisPlus的分页拦截器
MybatisPlus自带的分页插件
@Configuration //标记为配置类
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
@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-oELt6XI7-1656752352796)(C:\Users\29769\AppData\Roaming\Typora\typora-user-images\image-20220702163307785.png)]
在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的,如下:
@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)); 是关键,循环遍历方法,进行注入。
/**
* 注入自定义 MappedStatement
*
* @param mapperClass mapper 接口
* @param modelClass mapper 泛型
* @param tableInfo 数据库表反射信息
* @return MappedStatement
*/
public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?
> modelClass, TableInfo tableInfo);