一、MyBatis插件机制简介
1、插件机制
Mybatis通过插件(Intercept)可以做到拦截四大对象相关方法的执行,根据需求,完成相关数据的动态改变。
Excutor
StatementHandler
ParameterHandler
ResultSetHandler
2、插件原理
四大对象的每个对象在创建时,都会执行interceptChain.pluginAll()、会经过每个插件对象的plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象方法需要经过代理
二、分页插件
1、mybatis-config.xml全局配置文件中配置
<!-- 注册插件property 属性会被插件的获取到-->
<plugins>
<plugin interceptor="com.ming.plugin.MyPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
</plugins>
2、spring整合mybatis,在MybatisSqlSessionFactoryBean进行配置
<!-- 配置SqlSessionFactoryBean
Mybatis提供的: org.mybatis.spring.SqlSessionFactoryBean
MP提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.ming.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<!--注册插件-->
<property name="plugins">
<!--分页插件注册-->
<list>
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
</list>
</property>
</bean>
package com.ming.mp.test;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.ming.mp.beans.Employee;
import com.ming.mp.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.List;
/**
* 分页插件测试
*/
public class TestPage {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeMapper mapper = context.getBean("employeeMapper", EmployeeMapper.class);
@Test
public void testPaginationInterceptor() {
List<Employee> employeeList = mapper.selectPage(new Page<Employee>(1, 1), new EntityWrapper<Employee>().orderDesc(Arrays.asList("1")));
employeeList.stream().forEach(System.out::print);
}
}
package com.ming.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ming.bean.User;
import com.ming.util.PageResult;
public interface IUserService extends IService<User> {
PageResult findPage(int pageNum,int pageSize);
}
https://www.cnblogs.com/xifengxiaoma/p/11027551.html
package com.ming.service.impl;
import com.alibaba.druid.sql.PagerUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ming.bean.User;
import com.ming.mapper.UserMapper;
import com.ming.service.IUserService;
import com.ming.util.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
UserMapper userMapper;
@Override
public PageResult findPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> list = userMapper.selectList(null);
PageInfo<User> pageInfo = new PageInfo<>(list);
PageResult pageResult = PageResult.getPageResult(pageInfo);
return pageResult;
}
}
package com.ming.util;
import com.github.pagehelper.PageInfo;
import lombok.Data;
import java.util.List;
@Data
public class PageResult {
/**
* 当前页码
*/
private int pageNum;
/**
* 每页数量
*/
private int pageSize;
/**
* 总条数
*/
private long totalSize;
/**
* 页码总数
*/
private int totalPages;
/**
* 返回的数据集合
*/
private List<?> Data;
/**
* 处理分页结果集
*
* @param pageInfo
* @return
*/
public static PageResult getPageResult(PageInfo<?> pageInfo) {
PageResult pageResult = new PageResult();
pageResult.setPageNum(pageInfo.getPageNum());
pageResult.setPageSize(pageInfo.getPageSize());
pageResult.setTotalSize(pageInfo.getTotal());
pageResult.setTotalPages(pageInfo.getPages());
pageResult.setData(pageInfo.getList());
return pageResult;
}
}
package com.jinnjo.goods.utils;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;
/**
* 功能描述
* 手动分页
* @author subingbing
* 2018/8/23
*/
public class ManualPageableUtil {
public static <T> List<T> backPageList(List<T> source, Pageable pageable, Integer totalElements){
if(source.size() > 0) {
List<T> dest = new ArrayList<>();
Integer pageSize = pageable.getPageSize();
Integer pageNumber = pageable.getPageNumber();
int length = (pageNumber + 1) * pageSize<=totalElements?(pageNumber + 1) * pageSize:totalElements;
for (int i = pageNumber * pageSize; i < length; i++) {
dest.add(source.get(i));
}
return dest;
}else{
return source;
}
}
}
三、执行分析插件
1、com.baomidou.mybatisplus.plugins.SqlExplainInterceptor
2、SQL 执行分析拦截器,只支持 MySQL5.6.3 以上版本
3、 该插件的作用是分析 DELETE UPDATE 语句,防止小白或者恶意进行 DELETE UPDATE 全表操作
4、只建议在开发环境中使用,不建议在生产环境使用
5、在插件的底层 通过 SQL 语句分析命令:Explain 分析当前的SQL 语句,根据结果集中的 Extra 列来断定当前是否全表操作
注册执行分析插件
<!-- 配置SqlSessionFactoryBean
Mybatis提供的: org.mybatis.spring.SqlSessionFactoryBean
MP提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.ming.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<!--注册插件-->
<property name="plugins">
<list>
<!--分页插件注册-->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
<!--注册执行分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<!--作用就是我发现了当前你的操作是全表删除或更新、会阻止你的操作-->
<property name="stopProceed" value="true"/>
</bean>
</list>
</property>
</bean>
debug查看源码
/**
* 执行分析插件
*
*/
@Test
public void explainTest(){
mapper.delete(null);
}
PaginationInterceptor进行debug可以看出是执行的SQL分析sqlExplain
四、性能分析插件
1、 com.baomidou.mybatisplus.plugins.PerformanceInterceptor
2、性能分析拦截器,用于输出每条 SQL 语句及其执行时间
3、SQL 性能执行分析,开发环境使用 , 超过指定时间,停止运行。有助于发现问题
<!-- 配置SqlSessionFactoryBean
Mybatis提供的: org.mybatis.spring.SqlSessionFactoryBean
MP提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.ming.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<!--注册插件-->
<property name="plugins">
<list>
<!--分页插件注册-->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
<!--注册执行分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<!--作用就是我发现了当前你的操作是全表删除或更新、会阻止你的操作-->
<property name="stopProceed" value="true"/>
</bean>
<!--注册性能分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
<!--SQL是否格式化-->
<property name="format" value="true"></property>
<property name="maxTime" value="100"></property>
</bean>
</list>
</property>
</bean>
/**
* 测试性能分析插件
*/
@Test
public void testPerformanceInterceptor() {
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("1111111@qq.com");
employee.setAge(18);
employee.setGender("1");
mapper.insert(employee);
}
五、乐观锁插件
1、 com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor
2、如果想实现如下需求: 当要更新一条记录的时候,希望这条记录没有被别人更新
3、乐观锁的实现原理:取出记录时,获取当前 version 2更新时,带上这个 version 2执行更新时, set version = yourVersion+1 where version = yourVersion 如果 version 不对,就更新失败
4、 @Version 用于注解实体字段,必须要有。
乐观锁插件
<!-- 配置SqlSessionFactoryBean
Mybatis提供的: org.mybatis.spring.SqlSessionFactoryBean
MP提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.ming.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<!--注册插件-->
<property name="plugins">
<list>
<!--分页插件注册-->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
<!--注册执行分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<!--作用就是我发现了当前你的操作是全表删除或更新、会阻止你的操作-->
<property name="stopProceed" value="true"/>
</bean>
<!--注册性能分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
<!--SQL是否格式化-->
<property name="format" value="true"></property>
<property name="maxTime" value="100"></property>
</bean>
<!--乐观锁插件-->
<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
</bean>
</list>
</property>
</bean>
实体类和数据库添加version字段并用@version注解
@Version
private Integer version;
如果version版本不一样更新失败
/**
* 测试乐观锁插件
*/
@Test
public void testOptimisticLockerInterceptor() {
Employee employee = new Employee();
employee.setId(15);
employee.setLastName("TOM_B");
employee.setEmail("11111117997798@qq.com");
employee.setAge(18);
employee.setGender("1");
employee.setVersion(1);
mapper.updateById(employee);
}
/**
* 测试乐观锁插件 单线程下修改成功
*/
@Test
public void testOptimisticLockerInterceptor2() {
//1.查出用户version信息 Preparing: SELECT id,last_name AS lastName,email,gender,age,version FROM tbl_employee WHERE id=?
Employee employee = mapper.selectById(1);
//2.修改用户UPDATE tbl_employee SET last_name=?, email=?, gender=?, version=? WHERE id=? and version=?
employee.setLastName("测试乐观锁");
employee.setEmail("aaa@qq.com");
//3.执行更新操作
mapper.updateById(employee);
}
/**
* 测试乐观锁插件 单线程下修改成功
*/
@Test
public void testOptimisticLockerInterceptor3() {
// 线程1
Employee employee = mapper.selectById(1);
employee.setLastName("测试乐观锁111");
employee.setEmail("aaa@qq.com");
//模拟另外一个线程执行了插队操作
Employee employee2 = mapper.selectById(1);
employee2.setLastName("测试乐观锁222");
employee2.setEmail("aaa@qq.com");
//自旋锁来多次尝试提交
mapper.updateById(employee2);//如果没有乐观锁就会覆盖插队线程的值
mapper.updateById(employee);
}