1
mybatis 拦截器,通过拦截4大对象的方法的执行,完成相关数据的动态改变。
Executor :执行器
StatementHandler :编译器
ParameterHandler :sql参数处理器
ResultSetHandler :结果集处理
插件原理:
4大对象的每个对象创建时都会经过 interceptorChain.pluginAll(),拦截每个插件,经过每个插件的plugin()方法,目的就是给4大对象每个都创建代理。
2.分页插件
com.baomidou.mybatisplus.plugins.paginationInterceptor
使用:
@Configuration
public class MybatisPlusConfig {
//将自定义对象交给Spring容器管理
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3.逻辑删除
--------
yml:
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为0)
User 实体:
@TableLogic
private Integer deleted;
执行删除就会变为update,查询也会默认加上 where deleted = 0
4.自定义sql拦截器,为创建时间,创建人,修改时间,修改人,赋默认值
自定义拦截器
@Slf4j
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) // 此处 update 包含 增加, 修改操作
public class MyInterceptor implements Interceptor {
/**
* 获取用户缓存信息
*/
public User getLoginUser() {
try{
return UserInfo.getUserInfo(); // 在项目中通过线程或框架获取用户数据
}catch (Exception e){
return new User();
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
Object parameter = invocation.getArgs()[1];
if (parameter == null) {
return invocation.proceed();
}
//sql命令类型为插入,自动注入创建人信息
if (SqlCommandType.INSERT == sqlCommandType) {
User userEntity = getLoginUser();
if(null == userEntity){
return null;
}
Field[] fields = parameter.getClass().getDeclaredFields();
for (Field field : fields) {
log.info("------field.name------" + field.getName());
try {
// 注入创建用户id
if ("createUser".equals(field.getName())) {
field.setAccessible(true);
Object local_createBy = field.get(parameter);
field.setAccessible(false);
if (local_createBy == null || local_createBy.equals("")) {
if (userEntity != null) {
// 登录人账号
field.setAccessible(true);
field.set(parameter, Long.parseLong(userEntity.getUserId().toString()));
//field.set("updateBy", userEntity.getUsername());
field.setAccessible(false);
}
}
}
// 注入创建时间
if ("createTime".equals(field.getName())) {
field.setAccessible(true);
Object local_createDate = field.get(parameter);
field.setAccessible(false);
if (local_createDate == null || local_createDate.equals("")) {
field.setAccessible(true);
field.set(parameter, System.currentTimeMillis());
//field.set("updateTime", new Date());
field.setAccessible(false);
}
}
} catch (Exception e) {
}
}
}
//sql命令类型为更新,自动注入修改人信息
if (SqlCommandType.UPDATE == sqlCommandType) {
User userEntity = getLoginUser();
if(null == userEntity){
return null;
}
Field[] fields = parameter.getClass().getDeclaredFields();
for (Field field : fields) {
log.info("------field.name------" + field.getName());
try {
// 注入创建用户id
if ("updateUser".equals(field.getName())) {
field.setAccessible(true);
Object local_createBy = field.get(parameter);
field.setAccessible(false);
if (local_createBy == null || local_createBy.equals("")) {
if (userEntity != null) {
// 登录人账号
field.setAccessible(true);
field.set(parameter, Long.parseLong(userEntity.getUserId().toString()));
field.setAccessible(false);
}
}
}
// 注入创建时间
if ("updateTime".equals(field.getName())) {
field.setAccessible(true);
Object local_createDate = field.get(parameter);
field.setAccessible(false);
if (local_createDate == null || local_createDate.equals("")) {
field.setAccessible(true);
field.set(parameter, System.currentTimeMillis());
field.setAccessible(false);
}
}
} catch (Exception e) {
}
}
}
return invocation.proceed();
}
}
注册插件
@Configuration
public class MybatisPlusConfig {
//分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
//自动填充 创建人,创建时间,修改人,修改时间
@Bean
public Boolean setAutoFillValueInterceptor(SqlSessionFactory sqlSessionFactory) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
configuration.addInterceptor(new MyInterceptor());
return Boolean.TRUE;
}
}
每个实体都要加
private Long createUser;
private Long createTime;
private Long updateUser;
private Long updateTime;
5.打印控制台sql并自动填充where 参数
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
}
)
@Log4j2
public class MybatisSqlLogInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object proceed = invocation.proceed();
// 获取xml中的一个select/update/insert/delete节点,是一条SQL语句
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
Object parameter = null;
// 获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
// BoundSql就是封装myBatis最终产生的sql类
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
// 获取节点的配置
Configuration configuration = mappedStatement.getConfiguration();
// 获取到最终的sql语句
String sql = showSql(configuration, boundSql);
System.err.println("mybatis sql: " + sql);
return proceed;
}
/**
* 如果参数是String,则添加单引号,
* 如果是日期,则转换为时间格式器并加单引号;
* 对参数是null和不是null的情况作了处理
*/
private static String getParameterValue(Object obj) {
String value;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(new Date()) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
/**
* 进行?的替换
*
* @param configuration
* @param boundSql
* @return
*/
public String showSql(Configuration configuration, BoundSql boundSql) throws NoSuchFieldException, IllegalAccessException {
// 获取参数
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// sql语句中多个空格都用一个空格代替
String sql = getRealSql().replaceAll("[\\s]+", " ");
if (parameterObject != null) {
// 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 如果根据parameterObject.getClass()可以找到对应的类型,则替换
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
// 该分支是动态sql
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(obj)));
} else {
// 打印出缺失,提醒该参数缺失并防止错位
sql = sql.replaceFirst("\\?", "缺失");
}
}
}
}
return sql;
}
/**
* 获取真实sql, mybatis将其存在ThreadLocal中
* @return
* @throws IllegalAccessException
* @throws NoSuchFieldException
*/
private String getRealSql() throws IllegalAccessException, NoSuchFieldException {
Field field = ErrorContext.class.getDeclaredField("sql");
field.setAccessible(true);
Object sql = field.get(ErrorContext.instance());
if (sql != null) {
return sql.toString();
}
return "";
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}