利用Mybatis Plus优雅的添加自动填充公共字段,如createDate,createName等

一、原生Mybatis拦截器实现

以前用Mybatis的时候添加公共字段都是定义一个Mybatis的拦截器,继承Mybatis的Interceptor,拦截Sql语法构建的处理对象StatementHandler,如:

/**
 * 公共字段填充拦截器
 * 
 * @Author ZHANGCHAO
 * @Date 2019/11/18 
 * @Param 
 * @return 
 **/
@SuppressWarnings("all")
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class CommonDataInterceptor implements Interceptor {

    private static final Log logger = LogFactory.getLog(CommonDataInterceptor.class);

    private Properties props = null;

    private String UUIDColumn = null;
    private String createDateColumn = null;
    private String createByColumn = null;
    private String updateDateColumn = null;
    private String updateByColumn = null;
    private String versionColumn = null;
    private String delFlagColumn = null;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String interceptMethod = invocation.getMethod().getName();
        if (!"prepare".equals(interceptMethod)) {
            return invocation.proceed();
        }

        StatementHandler handler = (StatementHandler) PluginUtil.processTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(handler);
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        SqlCommandType sqlCmdType = ms.getSqlCommandType();
        if (sqlCmdType != SqlCommandType.UPDATE && sqlCmdType != SqlCommandType.INSERT) {
            return invocation.proceed();
        }

        if (null == props || props.isEmpty()) {
            return invocation.proceed();
        }
        UUIDColumn = props.getProperty("UUIDColumn", "UUID");
        createDateColumn = props.getProperty("createDateColumn", "SysCreateDate");
        createByColumn = props.getProperty("createByColumn", "SysCreateBy");
        updateDateColumn = props.getProperty("updateDateColumn", "SysUpdateDate");
        updateByColumn = props.getProperty("updateByColumn", "SysUpdateBy");
        versionColumn = props.getProperty("versionColumn", "versionNum");
        delFlagColumn = props.getProperty("delFlagColumn", "SysDelFlag");


        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        Object parameterObject = boundSql.getParameterObject();
        //获取原始sql
        String originalSql = (String) metaObject.getValue("delegate.boundSql.sql");
        logger.debug("==> originalSql: " + originalSql);
        //追加参数
        String newSql = "";
        if (sqlCmdType == SqlCommandType.UPDATE && updateDateColumn.length() > 0) {
            newSql = addUpdateData(parameterObject, originalSql);
        } else if (sqlCmdType == SqlCommandType.INSERT && createDateColumn.length() > 0) {
            newSql = addInsertData(parameterObject, originalSql);
        }
        //修改原始sql
        if (newSql.length() > 0) {
            logger.debug("[mybatis共通拦截][替换sql]==> newSql: " + newSql);
            metaObject.setValue("delegate.boundSql.sql", newSql);
        }
        return invocation.proceed();
    }

    private String addInsertData(Object parameterObject, String originalSql) {
        String sql = null;
        SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("-yyyy-MM-dd HH:mm:ss.SSS-");
        TimestampValue nowTime = new TimestampValue(TIMESTAMP_FORMAT.format(new Date()));
//		StringValue UUIDValue = UUIDUtil.getUUID();
        sql = addInsertDataDetail(parameterObject, originalSql, createDateColumn, null, nowTime);
        sql = addInsertDataDetail(parameterObject, sql, UUIDColumn, null, new StringValue("-" + UUIDUtil.getUUID() + "-"));
        sql = addInsertDataDetail(parameterObject, sql, updateDateColumn, null, nowTime);
        sql = addInsertDataDetail(parameterObject, sql, createByColumn, null, new StringValue("-admin-"));
        sql = addInsertDataDetail(parameterObject, sql, updateByColumn, null, new StringValue("-admin-"));
        sql = addInsertDataDetail(parameterObject, sql, versionColumn, null, new StringValue("-0-"));
        sql = addInsertDataDetail(parameterObject, sql, delFlagColumn, null, new StringValue("-0-"));
        return sql;
    }

    private String addInsertDataDetail(Object parameterObject, String originalSql, String columnName, String fieldName, Expression expression) {
        try {
            Statement stmt = CCJSqlParserUtil.parse(originalSql);
            if (!(stmt instanceof Insert)) {
                return originalSql;
            }

            //为了防止多租户注解的丢失,先把多租户注解暂存
            String tenantStr = "";
            if (originalSql.indexOf("/*") > -1) {
                int tenantIndexStart = originalSql.indexOf("/*");
                int tenantIndexEnd = originalSql.indexOf("*/");
                tenantStr = originalSql.substring(tenantIndexStart, tenantIndexEnd + 2);
            }

            Insert update = (Insert) stmt;
            List<Column> columns = update.getColumns();
            if (!contains(columns, columnName)) {
                Column versionColumn = new Column();
                versionColumn.setColumnName(columnName);
                columns.add(versionColumn);
                ItemsList itemList = update.getItemsList();
                if (itemList instanceof ExpressionList) {//单个
                    ExpressionList expressionList = (ExpressionList) itemList;
                    List<Expression> expressions = expressionList.getExpressions();
                    if (expression != null) {
                        expressions.add(expression);
                    } else {
                        Object value = getFieldValue(parameterObject, 0, fieldName);
                        expressions.add(new StringValue(value.toString()));
                    }
                } else if (itemList instanceof MultiExpressionList) {//批量
                    MultiExpressionList multiExpressionList = (MultiExpressionList) itemList;
                    List<ExpressionList> expressionLists = multiExpressionList.getExprList();
                    for (int i = 0; i < expressionLists.size(); i++) {
                        ExpressionList expressionList = expressionLists.get(i);
                        List<Expression> expressions = expressionList.getExpressions();
                        if (expression != null) {
                            expressions.add(expression);
                        } else {
                            Object value = getFieldValue(parameterObject, i, fieldName);
                            expressions.add(new StringValue(value.toString()));
                        }
                    }
                } else {//insert select
                    columns.remove(columns.size() - 1);
                }
            }

            return tenantStr + stmt.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return originalSql;
        }
    }

    private String addUpdateData(Object parameterObject, String originalSql) {
        String sql = null;
        SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("-yyyy-MM-dd HH:mm:ss.SSS-");
        sql = addUpdateDataDetail(parameterObject, originalSql, updateDateColumn, null,
                new TimestampValue(TIMESTAMP_FORMAT.format(new Date())));
        sql = addUpdateDataDetail(parameterObject, sql, updateByColumn, null, new StringValue("-admin-"));
        return sql;
    }

    private String addUpdateDataDetail(Object parameterObject, String originalSql, String columnName, String fieldName, Expression expression) {
        StringBuilder sb = new StringBuilder();
        String sqlList[] = originalSql.split(";");

        //为了防止多租户注解的丢失,先把多租户注解暂存
        String tenantStr = "";
        if (originalSql.indexOf("/*") > -1) {
            int tenantIndexStart = originalSql.indexOf("/*");
            int tenantIndexEnd = originalSql.indexOf("*/");
            tenantStr = originalSql.substring(tenantIndexStart, tenantIndexEnd + 2);
        }

        for (int i = 0; i < sqlList.length; i++) {
            if (i > 0) {
                sb.append(";");
            }
            try {
                Statement stmt = CCJSqlParserUtil.parse(sqlList[i]);
                if (!(stmt instanceof Update)) {
                    return originalSql;
                }
                Update update = (Update) stmt;
                List<Column> columns = update.getColumns();
                if (contains(columns, columnName)) {
                    sb.append(sqlList[i]);
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
                sb.append(sqlList[i]);
                continue;
            }
            if (expression == null) {
                Object value = getFieldValue(parameterObject, i, fieldName);
                String sql = addUpdateDateToSql(sqlList[i], columnName, new StringValue(value.toString()));
                sb.append(sql);
            } else {
                String sql = addUpdateDateToSql(sqlList[i], columnName, expression);
                sb.append(sql);
            }
        }
        return tenantStr + sb.toString();
    }

    private String addUpdateDateToSql(String originalSql, String columnName, Expression expression) {
        try {
            Statement stmt = CCJSqlParserUtil.parse(originalSql);
            if (!(stmt instanceof Update)) {
                return originalSql;
            }
            Update update = (Update) stmt;
            List<Column> columns = update.getColumns();
            if (!contains(columns, columnName)) {
                Column versionColumn = new Column();
                versionColumn.setColumnName(columnName);
                columns.add(versionColumn);
                List<Expression> expressions = update.getExpressions();
                expressions.add(expression);
            }
            return stmt.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return originalSql;
        }
    }

    /**
     * insertUserParams(int id, String name,  String password, int version);      MapperMethod.ParamMap<V> {0=100, 1=name, 2=pass, 3=111, param3=pass, param4=111, param1=100, param2=name}
     * insertUserParams(@Param("id")int id, @Param("name")String name,
     *
     * @Param("password")String password, @Param("version")int version);         MapperMethod.ParamMap<V> {password=pass, name=name, id=100, param3=pass, version=111, param4=111, param1=100, param2=name}
     * insertUserWithDate(User user);                                             com.chrhc.mybatis.autodate.domain.User@d6e7bab
     * insertUserWithDate(@Param("user")User user);                               MapperMethod.ParamMap<V> {user=com.chrhc.mybatis.autodate.domain.User@6a6afff2, param1=com.chrhc.mybatis.autodate.domain.User@6a6afff2}
     * insertBatchUser(List<User> users);                                         DefaultSqlSession.StrictMap<V> {collection=[com.chrhc.mybatis.autodate.domain.User@33bc72d1, com.chrhc.mybatis.autodate.domain.User@1a75e76a], list=[com.chrhc.mybatis.autodate.domain.User@33bc72d1, com.chrhc.mybatis.autodate.domain.User@1a75e76a]}
     * insertBatchUser(@Param("users")List<User> users);                          MapperMethod.ParamMap<V>{users=[com.chrhc.mybatis.autodate.domain.User@5e2c3d18, com.chrhc.mybatis.autodate.domain.User@2cb4893b], param1=[com.chrhc.mybatis.autodate.domain.User@5e2c3d18, com.chrhc.mybatis.autodate.domain.User@2cb4893b]}
     */
    @SuppressWarnings("rawtypes")
    private Object getFieldValue(Object parameterObject, int index, String fieldName) {
        Object value = null;
        if (parameterObject instanceof DefaultSqlSession.StrictMap) {
            DefaultSqlSession.StrictMap map = (DefaultSqlSession.StrictMap) parameterObject;
            Object object = map.get("list");
            if (object == null) {
                object = map.get("array");
            }
            if (object != null) {
                value = getValueFromListOrArray(object, index, fieldName);
            }
        } else if (parameterObject instanceof MapperMethod.ParamMap<?>) {
            MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameterObject;
            Object param1 = map.get("param1");
            if (param1.getClass().isArray() || List.class.isAssignableFrom(param1.getClass())) {
                value = getValueFromListOrArray(param1, index, fieldName);
            }
            if (value == null) {
                if (TypeUtil.isSimpleType(param1 == null ? null : param1.getClass())) {
                    Set keys = map.keySet();
                    for (Object key : keys) {
                        if (fieldName.equals(key)) {
                            value = map.get(key);
                            break;
                        }
                    }
                } else {
                    MetaObject metaObject = SystemMetaObject.forObject(param1);
                    value = metaObject.getValue(fieldName);
                }
            }
        } else if (parameterObject instanceof Map) {
            Map map = (Map) parameterObject;
            value = map.get(fieldName);
        } else {
            MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
            value = metaObject.getValue(fieldName);
        }
        if (value == null) {
            // throw new IllegalArgumentException("value of["+fieldName+"]can not be empty");
            return "";
        }
        return value;
    }

    @SuppressWarnings("rawtypes")
    private Object getValueFromListOrArray(Object parameterObject, int index, String fieldName) {
        Object entity = null;
        if (parameterObject instanceof List) {
            entity = ((List) parameterObject).get(index);
        } else if (parameterObject != null && parameterObject.getClass().isArray()) {
            entity = ((Object[]) parameterObject)[index];
        }
        if (entity != null) {
            MetaObject metaObject = SystemMetaObject.forObject(entity);
            return metaObject.getValue(fieldName);
        }
        return null;
    }

    private boolean contains(List<Column> columns, String columnName) {
        if (columns == null || columns.size() <= 0) {
            return false;
        }
        if (columnName == null || columnName.length() <= 0) {
            return false;
        }
        for (Column column : columns) {
            if (column.getColumnName().equalsIgnoreCase(columnName)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {
        if (null != properties && !properties.isEmpty()) {
            props = properties;
        }
    }

}

然后新建Mybatis配置类实例化这个拦截器:

package com.yyk.sz.common.mybatis.plugin;

import com.github.pagehelper.PageHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

/**
 * Mybatis插件配置
 * 
 * @Author ZHANGCHAO
 * @Date 2019/11/18 
 * @Param 
 * @return 
 **/
@Configuration
public class MyBatisPluginsConfiguration {
    private static final Log logger = LogFactory.getLog(MyBatisPluginsConfiguration.class);

    /**
     * 插件:共通字段添加
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public CommonDataInterceptor commonDataInterceptor(){
        CommonDataInterceptor commonDate = new CommonDataInterceptor();
        Properties commonDateProps = new Properties();
        commonDateProps.setProperty("UUIDColumn",   "UUID");
        commonDateProps.setProperty("createDateColumn",   "SysCreateDate");
        commonDateProps.setProperty("updateDateColumn",   "SysUpdateDate");
        commonDateProps.setProperty("versionColumn",      "versionNum");
        commonDateProps.setProperty("delFlagColumn",      "SysDelFlag");
        commonDateProps.setProperty("createByColumn",     "SysCreateBy");
        commonDateProps.setProperty("updateByColumn",     "SysUpdateBy");
        commonDate.setProperties(commonDateProps);
        return commonDate;
    }

    /**
     * 插件:多租户分库
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public TenantInterceptor tenantInterceptor(){
        TenantInterceptor tenantInterceptor = new TenantInterceptor();
        return tenantInterceptor;
    }


    /**
     * 插件:分页
     * @return PageHelper
     */
    @Bean
    @ConditionalOnMissingBean
    public PageHelper pageHelper(){
        PageHelper pageHelper = new PageHelper();
        Properties props = new Properties();
        props.setProperty("reasonable", "true");                //pageNum合理化,即,只有一页数据,pageNum为2则相当于1,为0也相当于1
        props.setProperty("supportMethodsArguments", "true");   //自动根据params配置取值
        props.setProperty("params", "count=countSql");
        props.setProperty("returnPageInfo", "check");           //用来支持直接返回PageInfo类型,默认值none,
                                                                //可选参数always总是返回PageInfo类型,check检查返回类型是否为PageInfo,
                                                                //none返回Page(List)类型。
                                                                //用法和配置参考com.github.pagehelper.test.basic包下的PageInfoTest,
                                                                //特别要注意接口的返回值和xml中的resultType类型。

        pageHelper.setProperties(props);

        logger.debug("Mybatis PageHelper plugin init finished");
        return pageHelper;
    }
}

二、Mybatis-Plus方式

现在项目引入了Mybatis Plus,就不需要原生拦截器那么麻烦了,大大简化了代码。

1、新建自定义的Handler继承元数据拦截器 MetaObjectHandler:

package com.yorma.enterprise.zbus.provider.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 元对象处理 -- 填充器
 *
 * @author ZhangChao
 * @date 2019/9/24 11:11
 * @since 1.0.0
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);

    @Override
    public void insertFill(MetaObject metaObject) {
        LOGGER.info("start insert fill ....");
        // 获取到需要被填充的字段值
        Object byDate = getFieldValByName("byDate", metaObject);
        Object byName = getFieldValByName("byName", metaObject);
        // 如果不手动设置,就自动填充默认值
        if (byDate == null){
            this.setInsertFieldValByName("byDate", LocalDateTime.now(), metaObject);
        }
        // 如果不手动设置,就自动填充默认值
        if (byName == null){
            this.setInsertFieldValByName("byName", "admin", metaObject);
        }

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        LOGGER.info("start update fill ....");
        this.setUpdateFieldValByName("updateDate", LocalDateTime.now(), metaObject);
    }
}

插入用setInsertFieldValByName()方法,更新用setUpdateFieldValByName()方法来设置公共字段。

***注意:Mybatis Plus 3.3.x之后版本MetaObjectHandler更改了,需要这样配置:

package com.yorma.enterprise.zbus.provider.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * 元对象处理 -- 填充器
 * 自动填充公共字段
 *
 * @author ZhangChao
 * @date 2019/9/24 11:11
 * @since 1.0.0
 */
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        // 获取到需要被填充的字段值
        Object createBy = getFieldValByName("createBy", metaObject);
        Object createDate = getFieldValByName("createDate", metaObject);
        // 如果不手动设置,就自动填充默认值
        if (createBy == null){
            this.strictInsertFill(metaObject, "createBy", String.class, "-admin-");
        }
        // 如果不手动设置,就自动填充默认值
        if (createDate == null){
            this.strictInsertFill(metaObject,"createDate", Date.class, new Date());
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        // 获取到需要被填充的字段值
        Object updateBy = getFieldValByName("updateBy", metaObject);
        Object updateDate = getFieldValByName("updateDate", metaObject);
        // 如果不手动设置,就自动填充默认值
        if (updateBy == null){
            this.strictUpdateFill(metaObject, "updateBy", String.class,"-admin-");
        }
        // 如果不手动设置,就自动填充默认值
        if (updateDate == null){
            this.strictUpdateFill(metaObject,"updateDate", Date.class, new Date());
        }
    }
}

* setInsertFieldValByName方法变更为:strictInsertFill

* setUpdateFieldValByName方法变更为:strictUpdateFill

2、实体类中的公共字段需要加入注解fill = FieldFill.INSERT 或fill = FieldFill.UPDATE,如:

    /**
     * 创建人
     */
    @TableField(value = "CREATE_BY",fill = FieldFill.INSERT)
    private String createBy;

    /**
     * 创建日期
     */
    @TableField(value = "CREATE_DATE",fill = FieldFill.INSERT)
    private Date createDate;

    /**
     * 更新人
     */
    @TableField(value = "UPDATE_BY",fill = FieldFill.UPDATE)
    private String updateBy;

    /**
     * 更新日期
     */
    @TableField(value = "UPDATE_DATE",fill = FieldFill.UPDATE)
    private Date updateDate;

3、搞定,测试吧。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Mybatis-Plus提供了公共字段填充的功能,可以在插入和更新操作时自动填充公共字段,减少代码重复和出错的可能性。下面是Java代码实现公共字段填充的示例: 1. 创建公共字段填充器类 ```java @Component public class MyMetaObjectHandler implements MetaObjectHandler { // 插入时填充字段 private static final String CREATE_TIME = "createTime"; private static final String UPDATE_TIME = "updateTime"; private static final String CREATE_BY = "createBy"; private static final String UPDATE_BY = "updateBy"; @Override public void insertFill(MetaObject metaObject) { // 填充创建时间和更新时间 this.strictInsertFill(metaObject, CREATE_TIME, LocalDateTime::now, LocalDateTime.class); this.strictInsertFill(metaObject, UPDATE_TIME, LocalDateTime::now, LocalDateTime.class); // 填充创建人和更新人 this.strictInsertFill(metaObject, CREATE_BY, "system", String.class); this.strictInsertFill(metaObject, UPDATE_BY, "system", String.class); } @Override public void updateFill(MetaObject metaObject) { // 填充更新时间 this.strictUpdateFill(metaObject, UPDATE_TIME, LocalDateTime::now, LocalDateTime.class); // 填充更新人 this.strictUpdateFill(metaObject, UPDATE_BY, "system", String.class); } } ``` 2. 配置公共字段填充器 ```java @Configuration public class MybatisPlusConfig { @Autowired private MyMetaObjectHandler metaObjectHandler; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加公共字段填充器 List<MetaObjectHandler> metaObjectHandlers = new ArrayList<>(); metaObjectHandlers.add(metaObjectHandler); interceptor.setMetaObjectHandlers(metaObjectHandlers); return interceptor; } } ``` 在以上示例中,我们创建了一个名为MyMetaObjectHandler的公共字段填充器类,实现了MetaObjectHandler接口,并在insertFill和updateFill方法中分别填充了创建时间、更新时间、创建人和更新人等公共字段。然后在MybatisPlusConfig中将MyMetaObjectHandler配置到MybatisPlusInterceptor中,作为公共字段填充器。这样,在执行插入和更新操作时,就会自动填充公共字段,无需手动设置,大大提高了开发效率和数据准确性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值