MyBatisPlus核心技术
一、MyBatisPlus快速开始
1 MyBatisPlus概述
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatisPlus的愿景是成为MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍
官方网址:https://mp.baomidou.com/
2 MyBatisPlus准备工作
2.1 前期准备
-
准备数据表,导入tb_employee.sql文件
-
创建Maven工程MyBatisPlus
-
引入Maven依赖
<dependencies>
<!-- mybatisplus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
- 在com.bzcxy.pojo下创建相关实体类(JavaBean)
@Data
public class Employee {
private Integer id;
private String empName;
private String useremail;
private String gender;
private Integer age;
}
2.2 搭建Spring+Mybatis环境
- Mybatis全局配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--控制台输出sql语句配置 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
- 数据库配置文件jdbc.properties
jdbcusername=root
jdbcpassword=root
jdbcUrl=jdbc:mysql://localhost:3306/baizhan?useSSL=false&useUnicode=true&characterEncoding=utf8
driverClass=com.mysql.jdbc.Driver
- Spring配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- dataSource 配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${driverClass}"> </property>
<property name="url" value="${jdbcUrl}"/>
<property name="username" value="${jdbcusername}"/>
<property name="password" value="${jdbcpassword}"/>
<!-- 对dataSource 数据源进行事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- spring与mybatis整合配置-->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 自动扫描所有mapper ,将mapper接口生成代理注入到Spring-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:basePackage="com.bzcxy.mapper"
p:sqlSessionFactoryBeanName="sqlSessionFactory"/>
</beans>
3 集成MyBatisPlus
调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory
MyBatis的SqlSessionFactory
org.mybatis.spring.SqlSessionFactoryBean
MyBatis-Plus的SqlSessionFactory
com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryB ean
测试
import com.bzcxy.mapper.EmployeeMapper;
import com.bzcxy.mapper.UserMapper;
import com.bzcxy.pojo.Employee;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class AppTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
private UserMapper userMapper = applicationContext.getBean("userMapper",UserMapper.class);
private EmployeeMapper employeeMapper =
applicationContext.getBean("employeeMapper",EmployeeMapper.class);
@Test
//测试环境
public void testEnv() throws SQLException {
//从Spring容器中获取dataSource
DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
//获取链接
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
@Test
public void delall()
{
userMapper.deleteAll();
}
@Test
public void testlogic()
{
// Employee employee = new Employee();
// employee.setId(2);
// employee.deleteById();
Employee employee = new Employee();
employee.setId(2);
Employee e = employee.selectById();
System.out.println(e);
}
@Test
public void testfill()
{
Employee employee = new Employee();
employee.setEmpName("百战程序员");
employee.setUseremail("[email protected]");
employee.setAge(3);
employee.setGender("m");
employee.insert();
}
}
二、MyBatisPlus通用CRUD
1 BaseMapper
针对employee表进行CRUD
- 基于MyBatis:
编写EmployeeMapper接口,并手动编写CRUD方法
提供EmployeeMapper.xml映射文件,并手动编写每个方法对应的SQL语 句 - 基于MyBatisPlus
只需要创建EmployeeMapper接口并继承BaseMapper接口,这就是使用
MyBatisPlus需要完成的所有操作,甚至不需要创建SQL的映射文件
// BaseMapper<T>:泛型指定的是所需操作的实体类
//XXMapper继承该接口后,即可获得CRUD功能 不需要XXMapper.xml
public interface EmployeeMapper extends BaseMapper<Employee> {
void deleteAll();
}
2 通用CRUD
2.1 insert
通过BaseMapper的insert方法可以直接传入一个实体类对象进行插入操作
public class CRUDTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
applicationContext.getBean("employeeMapper",EmployeeMapper.class);
@Test
public void testInsert() throws SQLException {
//创建实体类对象
Employee employee = new Employee();
//设置相关属性
employee.setEmpName("MP");
employee.setUseremail("[email protected]");
employee.setGender("m");
employee.setAge(15);
//通过BaseMapper的insert方法插入数据
int count = employeeMapper.insert(employee);
//代表受影响的行数
// System.out.println(employee.getId());
//插入成功以后 直接获取主键
System.out.println(employee.getId());
}
}
注: 由于没有主键策略及相关属性不匹配,以上代码会报错
2.2 @TableId
@TableId注解于实体类属性上,用于指定哪个属性为主键
参数:
type主键的策略类型
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator
的方法nextUUID(默认default方法)
@Data
public class Employee {
//指定id属性为主键,value是数据表中的主键列名,如果属性名和列名一致,则可以 省略
//指定type主键策略为自增的IdType.AUTO
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String empName;
private String useremail;
private String gender;
private Integer age;
}
2.3 @TableName
@TableName注解于实体类上,用于指定该类为哪个表的实体类
@Data
//mybatisplus 会找跟类名一样的表名 进行查询
//@TableName指定表名为tb_employee
@TableName(value = "tb_employee")
public class Employee {
//指定id属性为主键,value是数据表中的主键列名,如果属性名和列名一致,则可以 省略
//指定type主键策略为自增的IdType.AUTO
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String empName;
private String useremail;
private String gender;
private Integer age;
}
2.4 @TableField
@TableField注解于实体类非主键属性上,用于在属性和列名不同的情况下,指定映射关系
@Data
//mybatisplus 会找跟类名一样的表名 进行查询
//@TableName指定表名为tb_employee
@TableName(value = "tb_employee")
public class Employee {
//指定id属性为主键,value是数据表中的主键列名,如果属性名和列名一致,则可以 省略
//指定type主键策略为自增的IdType.AUTO
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
}
再次执行代码,插入成功!
2.5 全局策略配置
如果针对每一个实体类都要添加相关注解,比较麻烦,可以使用通用的配置进行统一管理。
在applicationContext.xml中配置全局策略,全局配置有多个,在此仅仅配置DB
<!-- 配置DB-->
<bean id="DBConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<!--是否使用驼峰转下划线命名,默认开启 可以省略 -->
<property name="tableUnderline" value="true"/>
<!--设置所有主键为自增模式 -->
<property name="idType" value="AUTO"/>
<!--设置表名前缀 -->
<property name="tablePrefix" value="tb_"/>
</bean>
将DBconfig加入GlobalConfig
<!-- 全局配置策略-->
<bean id="globalconfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="DBConfig"/>
</bean>
在sqlSessionFactory中添加globalConfig配置
<!-- spring与mybatis整合配置-->
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--将globalConfig注入 sqlSessionFactory-->
<property name="globalConfig" ref="globalconfig"/>
</bean>
修改POJO
@Data
//mybatisplus 会找跟类名一样的表名 进行查询
//@TableName指定表名为tb_employee
public class Employee {
//指定id属性为主键,value是数据表中的主键列名,如果属性名和列名一致,则可以 省略
//指定type主键策略为自增的IdType.AUTO
@TableId(value = "id")
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
}
测试
2.6 获取主键值
MyBatis:需要通过useGenerateKeys,以及KeyProperty来设置
<insert id="save" parameterType="com.bzcxy.pojo.User" useGeneratedKeys="true" keyProperty="id"/>
<!-- mybatis 支持主键自增,主键的值使用的是原生的jdbc方法 getGeneratedKey -->
MyBatisPlus:自动将主键值回写到实体类对象中
//插入成功以后 直接获取主键
System.out.println(employee.getId());
2.7 update
通过BaseMapper的updateById方法可以直接传入一个实体类对象根据id进行更新没有设置的值则忽略
@Test
public void testUpdate()
{
//创建实体类对象
Employee employee = new Employee();
//设置需要更新的属性
employee.setEmpName("baizhan");
employee.setGender("f");
//设置需要修改的数据id
employee.setId(5);
//根据主键进行更新,没有设置的值则忽略
employeeMapper.updateById(employee);
}
2.8 delete
- 通过deleteById方法可以根据id删除一条记录
int deleteById(Serializable id);
//根据主键删除
//删除11号数据
employeeMapper.deleteById(11);
- 通过deleteBatchIds进行批量删除
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
参数是实现Collection接口的集合
@Test
public void testDelete()
{
//根据主键删除
//删除11号数据
// employeeMapper.deleteById(11);
//通过deleteBatchIds进行批量删除
//创建list容器,加入将要删除的元素
List<Integer> ids = new ArrayList<>();
ids.add(12);
ids.add(13);
//同时删除12 13
//employeeMapper.deleteBatchIds(ids);
Map<String,Object> map = new HashMap<>();
map.put("emp_name","baizhan");
map.put("gender","m");
employeeMapper.deleteByMap(map);
}
- 通过deleteByMap进行条件删除
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
根据columnMap的key与value删除,key为列名,value为值,如果有多个key- value键值对,则按照and进行删除
Map<String,Object> map = new HashMap<>();
map.put("emp_name","baizhan");
//删除emp_name='baizhan'的数据
employeeMapper.deleteByMap(map);
Map<String,Object> map = new HashMap<>();
map.put("emp_name","baizhan");
map.put("gender","m");
//删除emp_name为baizhan,gender为m的数据
employeeMapper.deleteByMap(map);
2.9 select
- 通过selectById根据id获取一条数据
T selectById(Serializable id);
//根据id查找一条数据,并返回实体类对象
Employee e = employeeMapper.selectById(1);
System.out.println(e);
- 通过selectBatchIds进行批量查询
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
参数是实现Collection接口的集合
//根据id集合查找
//创建主键集合
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
//根据id = 1 2 3进行查询 返回一个 Employee对象集合
List<Employee> emps = employeeMapper.selectBatchIds(ids);
System.out.println(emps);
- 通过selectByMap进行条件查询
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
根据columnMap的key与value查询,key为列名,value为值,如果有多个key- value键值对,则按照and进行查询
@Test
public void testselect()
{
//根据id查找一条数据,并返回实体类对象
// Employee e = employeeMapper.selectById(1);
// System.out.println(e);
//根据id集合查找
//创建主键集合
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
//根据id = 1 2 3进行查询 返回一个 Employee对象集合
// List<Employee> emps = employeeMapper.selectBatchIds(ids);
// System.out.println(emps);
Map<String,Object> map = new HashMap<>();
map.put("gender","m");
//查询所有gender=m的数据
List<Employee> emps = employeeMapper.selectByMap(map);
System.out.println(emps);
}
- 通过selectPage进行分页查询
IPage<E> selectPage(IPage page, @Param("ew") Wrapper<T> queryWrapper);
page(分页条件):创建Page对象并初始化分页条件
//第0行数据开始取两行数据
new Page<>(0,2)
queryWrapper
(条件构造器):暂且为null
IPage
(分页对象):返回的分页结果集、分页数据等
进行分页查询时,需要在sqlSessionFactory
中添加分页插件
<!-- 插件-->
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
<property name="interceptors">
<list>
<!-- 注册分页插件-->
<bean class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"></bean>
</list>
</property>
</bean>
</array>
</property>
测试
@Test
public void testselectPage()
{
//分页数据
Page page = new Page(0,2);
IPage<Employee> res = employeeMapper.selectPage(page,null);
//从分页对象中获取结果集
List<Employee> emps = res.getRecords();
System.out.println(emps);
}
三、条件构造器
1 QueryWrapper
- Mybatis-Plus 通过 QueryWrapper 来让用户自由的构建SQL条件,简单便捷,没有额外的负担,能够有效提高开发效率
- 查询包装器QueryWrapper, 主要用于处理 sql 拼接,排序,实体参数查询等
- 使用的是数据库字段,不是 Java 属性!
- 需要给QueryWrapper提供实体类类型
- 条件参数说明:
2 select
- selectList
需要给QueryWrapper提供实体类类型new QueryWrapper()
public class QueryWrapperTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
applicationContext.getBean("employeeMapper",EmployeeMapper.class);
@Test
public void testselectList()
{
QueryWrapper<Employee> qw = new QueryWrapper<>();
//查询性别为女,且年龄小于等于35的数据
qw.like("gender","f").le("age",35);
// List<Employee> emps = employeeMapper.selectList(qw);
// System.out.println(emps);
//查询年龄小于25或者年龄大于30的人
List<Employee> emps = employeeMapper.selectList(new QueryWrapper<Employee>()
.lt("age",25)
.or()
.gt("age",30));
System.out.println(emps);
}
}
- selectPage
@Test
public void testselectpage()
{
//创建分页数据
Page page = new Page(2,2);
//获取第二页2条数据,且名字包含老师
IPage<Employee> res = employeeMapper.selectPage(page,
new QueryWrapper<Employee>().like("emp_name","老师"));
//通过IPage的getRecords方法获取
List<Employee> emps = res.getRecords();
System.out.println(emps);
}
3 update
@Test
public void testupdate()
{
//修改名字包含李且age=32数据
//修改emp_name=baizhan [email protected]
//创建条件构造器
QueryWrapper<Employee> qw = new QueryWrapper<>();
qw.like("emp_name","李").eq("age",32);
//创建实体类对象
Employee employee = new Employee();
employee.setEmpName("baizhan");
employee.setUseremail("[email protected]");
employeeMapper.update(employee,qw);
}
4 delete
@Test
public void testdel()
{
//删除age 在30-35之间 且名字包含a数据
employeeMapper.delete(new QueryWrapper<Employee>()
.between("age",30,35)
.like("emp_name","a"));
}
5 orderby
- orderByAsc/orderByDesc:升序/降序方法
@Test
public void testOrderby()
{
//查询所有老师,按照年龄排序
//按照年龄升序
// List<Employee> emps = employeeMapper.selectList(new QueryWrapper<Employee>()
// .like("emp_name","老师")
// .orderByAsc("age"));
// System.out.println(emps);
List<Employee> emps = employeeMapper.selectList(new QueryWrapper<Employee>()
.like("emp_name","老师")
.orderByDesc("age"));
System.out.println(emps);
}
四、ActiveRecord
ActiveRecord(活动记录)也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。
ActiveRecord主要思想:
- 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录
- 通常表的每个字段在类中都有相应的属性
- ActiveRecord同时负责把自己持久化(即不通过mapper,依靠实体类本身),在ActiveRecord中封装了对数据库的访问,即CURD
- 是一种领域模型模式,封装了部分业务逻辑
1 Model
实体类继承Model类之后,能通过实体直接进行CRUD操作,而不需要进行调用Mapper
注:必须存在对应的原始mapper并继承BaseMapper并且可以使用的前提下
修改Employee实体类
@Data
//继承Model类 开启AR模式
public class Employee extends Model<Employee> {
@TableId(value = "id")
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
//重写pkVal方法,返回主键
@Override
protected Serializable pkVal()
{
return this.id;
}
}
2 insert
public class ARTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void testinsert()
{
Employee employee = new Employee();
employee.setEmpName("百战程序员");
employee.setUseremail("[email protected]");
employee.setAge(3);
employee.setGender("m");
//使用AR的insert方法,将实体类自身插入数据库
employee.insert();
}
}
3 update
@Test
public void testupdate()
{
//创建实体类对象
Employee employee = new Employee();
employee.setId(15);
employee.setEmpName("北京尚学堂");
employee.setUseremail("[email protected]");
//使用AR的updateByid,实体类对象将自身更新
employee.updateById();
}
4 delete
- deleteById根据id删除自身
@Test
public void testdel()
{
//创建实体类对象
Employee employee = new Employee();
// employee.setId(15);
// //通过AR的deletebyid方法 将实体类自身删除
// employee.deleteById();
//通过AR的delete方法 参数是条件构造器 删除自身
employee.delete(new QueryWrapper<Employee>()
.like("emp_name","tom"));
}
- delete根据条件构造器删除
//通过AR的delete方法 参数是条件构造器 删除自身
employee.delete(new QueryWrapper<Employee>() .like("emp_name","tom"));
5 select
- selectById根据自身id获取数据
@Test
public void testselect()
{
//创建实体类对象
Employee employee = new Employee();
// employee.setId(2);
//通过AR的selectByid查询实体类对象
// Employee e = employee.selectById();
// System.out.println(e);
//通过AR的selectAll方法查询所有数据
// List<Employee> emps = employee.selectAll();
// System.out.println(emps);
//通过AR的selectList方法返回数据 参数是条件构造器
// List<Employee> emps =
// employee.selectList(new QueryWrapper<Employee>().like("gender","m"));
// System.out.println(emps);
//通过AR的selectCount方法返回结果集数量
int count =
employee.selectCount(new QueryWrapper<Employee>().like("emp_name","老师"));
System.out.println("老师一共有:" + count);
}
- selectAll获取所有数据
//通过AR的selectAll方法查询所有数据
List<Employee> emps = employee.selectAll();
- selectList根据条件构造器查询
//通过AR的selectList方法返回数据 参数是条件构造器
//查询所有性别为m的数据
List<Employee> emps = employee.selectList(new QueryWrapper<Employee>(). like("gender","m"));
- selectCount根据条件查询结果集数量
//通过AR的selectCount方法返回结果集数量
//查询所有老师的数量
int count = employee.selectCount(new QueryWrapper<Employee> ().like("emp_name","老师"));
五、MyBatisPlus插件机制
1 插件概述
1、 插件机制: Mybatis 通过插件(Interceptor) 可以做到拦截四大对象相关方法的执行,根据需求,完
成相关数据的动态改变。
- Executor
Mybatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射,另外,它还处理了二级缓存的操作,也可以通过插件来实现自定义的二级缓存的。 - StatementHandler
Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里,我们可以使用插件来实现对一级缓存的操作(禁用等等)。 - ParameterHandler
Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。 - ResultSetHandler
Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。
2、 插件原理
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理.
3、常用插件
- 分页插件
(com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerI nterceptor) - 防止全表更新与删除插件
(com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInne rInterceptor) - 乐观锁插件
(com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLocke rInnerInterceptor)
2 分页插件
com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
分页对象使用
public class InterceporTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void testpage()
{
//创建实体类对象
Employee employee = new Employee();
//创建分页信息
Page page = new Page(0,2);
IPage<Employee> res = employee.selectPage(page,null);
//从分页对象中获取记录及
List<Employee> emps = res.getRecords();
// System.out.println(emps);
//获取总共记录数
Long total = page.getTotal();
System.out.println("total=" + total);
//获取当前获得了多少数据
Long size = page.getSize();
System.out.println("size=" + size);
//获取一共多少页
Long pages = page.getPages();
System.out.println("pages=" + pages);
//是否有上一页 是否有下一页
Boolean previous = page.hasPrevious();
Boolean next = page.hasNext();
System.out.println(previous);
System.out.println(next);
}
}
3 防止全表更新与删除插件
com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor
-
只支持 MySQL5.6.3 以上版本
-
该插件的作用是分析 DELETE UPDATE 语句,防止小白或者恶意进行 DELETE UPDATE 全表操作
-
只建议在开发环境中使用,不建议在生产环境使用
-
在插件的底层 通过 SQL 语句分析命令:Explain 分析当前的 SQL 语句,根据结果集中的 Extra 列来断定当前是否全表操作。
在applicationContext.xml中注册防止全表更新与删除插件插件
<!--注册防止全表更新与删除插件 -->
<bean class="com.baomidou.mybatisplus.extension.plugins.inner.BlockAttack InnerInterceptor"></bean>
添加该插件后,如果删除整表,则会抛出异常
@Test
public void testdel()
{
Employee employee = new Employee();
employee.delete(null);
}
4 乐观锁插件
com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor
- 当要更新一条记录的时候,希望这条记录没有被别人更新
- 乐观锁的实现原理:
取出记录时,获取当前 version
更新时,带上这个 version
执行更新时, set version = yourVersion+1 where version = yourVersion
如果 version 不对,就更新失败 - @Version 用于注解实体属性,必须要有在applicationContext.xml中注册乐观锁插件
<!--注册乐观锁插件 -->
<bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticL ockerInnerInterceptor"></bean>
修改Employee类,添加整型version 属性,并在该属性上面增加@Version
@Data
//继承Model类 开启AR模式
public class Employee extends Model<Employee> {
@TableId(value = "id")
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
//实现乐观锁
@Version
private Integer version;
//重写pkVal方法,返回主键
@Override
protected Serializable pkVal()
{
return this.id;
}
}
给tb_employee表添加一列整型version并初始化为1
更新数据时会校验版本号,新旧版本号一致时,表示当前数据未被其他事务更新过,更新通
过,版本号自动+1;
@Test
public void testversion()
{
Employee employee = new Employee();
employee.setId(2);
employee.setEmpName("百战程序员");
//如果版本号跟数据库一致 更新成功,版本号+1
// 不一致 更新失败
employee.setVersion(2);
employee.updateById();
}
六、MyBatisPlus进阶
1 自定义全局操作
根据 MybatisPlus 的 AbstractSqlInjector可以自定义各种你想要的 sql ,注入到全局中,相当
于自定义 Mybatisplus 自动注入的方法。
导入tb_user.sql,并创建User相关文件
User实体类
@Data
public class User {
@TableId
private Integer id;
private String username;
private Integer userage;
}
UserMapper类
public interface UserMapper extends BaseMapper<User> {
}
在 Mapper 接口中定义相关的 CRUD 方法
public interface UserMapper extends BaseMapper<User> {
void deleteAll();
}
重写 AbstractMethod的injectMappedStatement方法,实现 Mapper 接口中方法要注入的
SQL
新建com.bzcxy.inject及在包下创建自己的AbstractMethod类(deleteAll)
public class DeleteAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement
(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
//定义sql语句
String sql = "delete from " + tableInfo.getTableName();
//方法名
String method = "deleteAll";
//构建sqlsource
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration,sql,modelClass);
//通过addDeleteMappedStatement构建
return this.addDeleteMappedStatement(mapperClass,method,sqlSource);
}
}
创建自己的AbstractSqlInjector的类(MySqlInject)并重写getMethodList方法
public class MySqlInject extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methods = new ArrayList<>();
//添加自定义方法
methods.add(new DeleteAll());
return methods;
}
}
将MySqlInject注册到全局配置中
<!--定义自定义注入器 -->
<bean id="MySqlinjector" class="com.bzcxy.inject.MySqlInject"></bean>
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="DBConfig"/>
<!-- 自定义注入器注册到全局配置 -->
<property name="sqlInjector" ref="MySqlinjector"/>
</bean>
注:需要将防止全表更新与删除插件删除
直接使用deleteAll()
@Test
public void delall()
{
userMapper.deleteAll();
}
2 逻辑删除
物理删除:从数据库中直接删除
逻辑删除:在数据库中没有被删除,而是通过一个变量来代表它被删除。
- logicDeleteValue逻辑删除全局值
- logicNotDeleteValue 逻辑未删除全局值
- 在 POJO 的逻辑删除字段 添加 @TableLogic 注解
给tb_employee表添加一列整型deleted并初始化为0
修改Employee类,添加整型deleted属性,并在该属性上面增加@TableLogic
@Data
//继承Model类 开启AR模式
public class Employee extends Model<Employee> {
@TableId(value = "id")
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
@Version
private Integer version;
//逻辑删除属性
@TableLogic
private Integer deleted;
private Date updatetime;
//重写pkVal方法,返回主键
@Override
protected Serializable pkVal()
{
return this.id;
}
}
在applicationContext.xml的DBConfig配置逻辑删除全局值
<!-- 配置DB-->
<bean id="DBConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<!--是否使用驼峰转下划线命名,默认开启 可以省略 -->
<property name="tableUnderline" value="true"/>
<!--设置所有主键为自增模式 -->
<property name="idType" value="AUTO"/>
<!--设置表名前缀 -->
<property name="tablePrefix" value="tb_"/>
<!--逻辑删除全局值 逻辑删除1 逻辑未删除0 -->
<property name="logicDeleteValue" value="1"/>
<property name="logicNotDeleteValue" value="0"/>
<!--注册sequence -->
<property name="keyGenerator" ref="keyGenerator"/>
</bean>
测试:
@Test
public void testlogic()
{
// Employee employee = new Employee();
// employee.setId(2);
// employee.deleteById();
Employee employee = new Employee();
employee.setId(2);
Employee e = employee.selectById();
System.out.println(e);
}
以上代码仅仅将deleted置为1,并没有从数据库中删除
3 自动填充
3.1 概述
com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
MetaObjectHandler接口是MyBatisPlus提供的的一个扩展接口,可以利用这个接口在插入或者更新数据的时候,为一些字段指定默认值。
比如:向数据库插入一条数据的时候,少不了一些向createTime、updateTime此类字段,每次插入的数据都要设置这些个值,十分繁琐,通过实现MetaObjectHandler接口重写insertFill、updateFill方法可以自动填写。
本质上 MetaObjectHandler获取对象的属性值或者是给对象的属性设置值,最终是要通过Reflector 获取到属性的对应方法的 Invoker, 最终 invoke.
3.2 实现步骤
给tb_employee表添加一列日期型(datetime)数据updatetime
修改Employee类,添加Date型update_Time属性
@Data
//继承Model类 开启AR模式
public class Employee extends Model<Employee> {
@TableId(value = "id")
private Integer id;
private String empName;
//指定useremail属性对应的列名为email
@TableField(value = "email")
private String useremail;
private String gender;
private Integer age;
@Version
private Integer version;
//逻辑删除属性
@TableLogic
private Integer deleted;
//自动填充字段
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatetime;
//重写pkVal方法,返回主键
@Override
protected Serializable pkVal()
{
return this.id;
}
}
在updateTime上添加 @TableField注解并设置fill参数
//自动填充字段
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatetime;
填充策略:
创建com.bzcxy.handler包,并实现继承MetaObjectHandler接口的类(MyMetaObjectHandler),重写insertFill和updateFill方法
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//自动填充逻辑
}
@Override
public void updateFill(MetaObject metaObject) {
//自动填充逻辑
}
}
将MyMetaObjectHandler类注册到GlobalConfig中
<!--定义自定义注入器 -->
<bean id="MySqlinjector" class="com.bzcxy.inject.MySqlInject"></bean>
<!-- 全局配置策略-->
<bean id="globalconfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="DBConfig"/>
<!-- <property name="sqlInjector" ref="MySqlinjector"/> -->
<property name="metaObjectHandler" ref="MyMetaObjectHandler"/>
</bean>
重写insertFill和updateFill填充逻辑,每次执行update和insert时,都更新当前时间
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//自动填充逻辑
//通过metaObject来获取需要被填充字段的值 updatetime
Object fieldvalue = this.getFieldValByName("updatetime",metaObject);
if (fieldvalue == null)
{
//获取当前时间
Date now = new Date();
//将当前时间赋值给fieldvalue
this.setFieldValByName("updatetime",now,metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {
//自动填充逻辑
//通过metaObject来获取需要被填充字段的值 updatetime
Object fieldvalue = this.getFieldValByName("updatetime",metaObject);
if (fieldvalue == null)
{
//获取当前时间
Date now = new Date();
//将当前时间赋值给fieldvalue
this.setFieldValByName("updatetime",now,metaObject);
}
}
}
4 代码生成器
4.1 概述
-
MP 代码生成器提供了大量的自定义设置,生成的代码完全能够满足各类型的需求
-
MP 的代码生成器 和 Mybatis MBG 代码生成器:
MP 的代码生成器都是基于 java 代码来生成。MBG 基于 xml 文件进行代码生成MBG 的代码生成器可生成: 实体类、Mapper 接口、Mapper 映射文件
MP 的代码生成器可生成: 实体类(可以选择是否支持 AR)、Mapper 接口、Mapper 映射文件、 Service 层、Controller 层
4.2 使用步骤
MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关
依赖
代码生成器依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,可以选择自己熟悉的模板引擎,
如果不满足要求,可以采用自定义模板引擎。
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
1.全局配置
public class GenratorTest {
@Test
public void test()
{
//1.全局配置
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(true)//是否开启AR模式
.setAuthor("zhangsihang")//设置作者
.setOutputDir(System.getProperty("user.dir")+"/src/main/java")//设置输出目录
.setFileOverride(true)//是否文件覆盖
.setOpen(false)//生成后是否打开资源管理器(文件夹)
.setIdType(IdType.AUTO)//主键策略
.setBaseResultMap(true);//是否生成基本的sql映射文件
}
}
2.数据源配置
//2.数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)//设置数据源为mysql
.setUrl("jdbc:mysql://localhost:3306/baizhan")//设置url
.setDriverName("com.mysql.jdbc.Driver")//设置驱动
.setUsername("root")
.setPassword("root");
3.策略配置
//3.策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tb_employee")//指定要映射的数据表,可以有多个
.setNaming(NamingStrategy.underline_to_camel)// 命名规则下划线转驼峰
.setColumnNaming(NamingStrategy.underline_to_camel)//列名规则
.setEntityLombokModel(true)//是否生成lombok注解
.setTablePrefix("tb_");//设置表名前缀
4.包名配置
//4.包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.bzcxy.generator")//设置根包名
.setMapper("mapper")//mapper接口位置
.setEntity("pojo")//pojo位置
.setController("controller")//控制器位置
.setService("service")//service位置
.setXml("mapper");//映射文件页在mapper中
5.整合配置
//5.整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config);
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setStrategy(strategyConfig);
autoGenerator.setPackageInfo(packageConfig);
autoGenerator.execute();
6.执行生成
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
public class GenratorTest {
@Test
public void test()
{
//1.全局配置
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(true)//是否开启AR模式
.setAuthor("zhangsihang")//设置作者
.setOutputDir(System.getProperty("user.dir")+"/src/main/java")//设置输出目录
.setFileOverride(true)//是否文件覆盖
.setOpen(false)//生成后是否打开资源管理器(文件夹)
.setIdType(IdType.AUTO)//主键策略
.setBaseResultMap(true);//是否生成基本的sql映射文件
//2.数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)//设置数据源为mysql
.setUrl("jdbc:mysql://localhost:3306/baizhan")//设置url
.setDriverName("com.mysql.jdbc.Driver")//设置驱动
.setUsername("root")
.setPassword("root");
//3.策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tb_employee")//指定要映射的数据表,可以有多个
.setNaming(NamingStrategy.underline_to_camel)// 命名规则下划线转驼峰
.setColumnNaming(NamingStrategy.underline_to_camel)//列名规则
.setEntityLombokModel(true)//是否生成lombok注解
.setTablePrefix("tb_");//设置表名前缀
//4.包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.bzcxy.generator")//设置根包名
.setMapper("mapper")//mapper接口位置
.setEntity("pojo")//pojo位置
.setController("controller")//控制器位置
.setService("service")//service位置
.setXml("mapper");//映射文件页在mapper中
//5.整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config);
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setStrategy(strategyConfig);
autoGenerator.setPackageInfo(packageConfig);
autoGenerator.execute();
}
}
5 Oracle主键策略
MySQL: 支持主键自增。 IdType.Auto
Oracle: 序列(Sequence)
添加maven依赖
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.8.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.nls</groupId>
<artifactId>orai18n</artifactId>
<version>21.1.0.0</version>
</dependency>
在db.properties中添加Oracle的连接信息
orcldriver=oracle.jdbc.OracleDriver
orclurl=jdbc:oracle:thin:@localhost:1521:orcl
orclusername=system
orclpassword=root
修改applicationContext.xml数据源为oracle
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password-->
<property name="driverClassName" value="${orcldriver}"> </property>
<property name="url" value="${orclurl}"/>
<property name="username" value="${orclusername}"/>
<property name="password" value="${orclpassword}"/>
</bean>
在oracle中新建一个tb_useroracle表
Create table tb_useroracle(
id number(11) primary key,
name varchar(255)
);
同时新建一个序列(Sequence)
create sequence seq_useroracle start with 100 increment by 1;
Sequence的常用操作
//查询序列的下一个值 dual代表虚表
select seq_useroracle.nextval from dual
//查询序列的当前值
select seq_useroracle.currval from dual
DBConfig中注册Sequence
<!--配置Oracle主键Sequence -->
<bean id="keyGenerator" class="com.baomidou.mybatisplus.extension.incrementer.OracleKeyGene rator"/>
<bean id="DBConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<!-- 表名是否使用驼峰转下划线命名,只对表名生效 默认开启 可以不用添加-->
<property name="tableUnderline" value="true"/>
<!-- 设置所有主键为自增模式-->
<property name="idType" value="AUTO"/>
<!-- 设置表明前缀-->
<property name="tablePrefix" value="tb_"/>
<!-- 逻辑删除 配置删除的值为1 未删除的值为0-->
<property name="logicDeleteValue" value="1"/>
<property name="logicNotDeleteValue" value="0"/>
<!--注册Sequence -->
<property name="keyGenerator" ref="keyGenerator"/>
</bean>
创建Useroracle实体类并配置主键 Sequence
在类上加@KeySequence注解
给主键配置@TableId(type = IdType.INPUT)
@KeySequence(value=”序列名”)
@Data
@KeySequence(value = "seq_useroracle") public class Useroracle {
@TableId(type = IdType.INPUT)
private Integer id;
private String name;
}
创建UseroracleMapper继承BaseMapper
public interface UseroracleMapper extends BaseMapper<User> {
}
测试
public class SequenceTest {
private ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
UseroracleMapper useroracleMapper =
applicationContext.getBean("useroracleMapper",UseroracleMapper.class);
@Test
public void test()
{
Useroracle useroracle = new Useroracle();
useroracle.setName("百战1");
useroracleMapper.insert(useroracle);
}
}
向Oracle中插入数据
可以将@keySequence 定义在父类中,可实现多个子类对应的多个表公用一个 Sequence父类
@KeySequence(value = "seq_useroracle")
public class ParentSequence {
}
子类继承即可公用Sequence
@Data
public class Useroracle extends ParentSequence{
@TableId(type = IdType.INPUT)
private Integer id;
private String name;
}
6 MyBatisX快速开发插件
MybatisX 辅助 idea 快速开发插件,极高提升开发效率。
可以实现 Java 与 xml 跳转,根据 Mapper 接口中的方法自动生成 xml 结构.
安装MybatisX :
- 搜索安装
File -> Settings -> Plugins->Marketplace 输入 MybatisX 安装下载
- 手动安装
File -> Settings -> Plugins-> Install plugin from disk 找到MybatisX 安装包进行安装
注:需要到https://plugins.jetbrains.com/搜索跟自己IDEA对应版本的MybatisX插件安装完成后需要重启IDEA
给EmployeeMapper编写一个EmployeeMapper.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.bzcxy.mapper.EmployeeMapper">
</mapper>
通过Generate statement在EmployeeMapper.xml自动生成xml结构
可以通过(Ctrl+右键+方法名)在xml和接口之间切换
练习源码:https://gitee.com/cutelili/my-batisplus
Git应用与实战