mybatis-plus是一个优秀的ORM框架,除了提供一些常规的功能,还提供了一些高级功能,比如拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)等,这些功能都可以方便我们进行业务开发,以下是详细讲解:
Mybatis-plus拦截器
Mybatis-plus提供了拦截器机制,使得我们可以对Sql语句进行拦截和修改,甚至可以在sql语句执行前后进行处理,这样可以实现一些高级功能,比如动态表名、分表、数据权限等。
接下来是一个通过Mybatis-plus拦截器实现动态表名的示例:
public class DynamicTableNameInterceptor implements Interceptor {
/**
* 根据规则构造新表名
* @param name 原始表名
* @param parameter sql执行参数
* @param mapper sql执行Mapper对象
* @return 返回新表名
*/
@Override
public String changeTableName(String name, Object parameter, MapperMethod mapper) {
// 从执行参数中获取参数值
Long userId = (Long) ReflectionUtils.invokeGetter(parameter, "userId");
// 构造新表名
String tableName = "user" + userId % 10;
return tableName;
}
/**
* 拦截器逻辑实现
* @param invocation 拦截器调用方法
* @return 返回执行结果
* @throws Throwable 抛出异常
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取MappedStatement对象
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
// 获取sql参数
Object parameter = invocation.getArgs()[1];
// 获取MapperMethod对象
MapperMethod mapper = (MapperMethod) invocation.getArgs()[2];
// 获取原始表名
String name = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1);
// 获取新表名
String tableName = changeTableName(name, parameter, mapper);
// 替换表名
SqlSource sqlSource = mappedStatement.getSqlSource();
String sql = sqlSource.getBoundSql(parameter).getSql();
sql = sql.replaceAll(name, tableName);
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), sql, sqlSource.getBoundSql(parameter).getParameterMappings(), parameter);
MappedStatement.Builder builder = new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId(), new BoundSqlSqlSource(newBoundSql), mappedStatement.getSqlCommandType());
builder.resource(mappedStatement.getResource());
builder.fetchSize(mappedStatement.getFetchSize());
builder.statementType(mappedStatement.getStatementType());
builder.timeout(mappedStatement.getTimeout());
builder.parameterMap(mappedStatement.getParameterMap());
builder.resultMaps(mappedStatement.getResultMaps());
builder.cache(mappedStatement.getCache());
builder.flushCacheRequired(mappedStatement.isFlushCacheRequired());
builder.useCache(mappedStatement.isUseCache());
MappedStatement newMappedStatement = builder.build();
invocation.getArgs()[0] = newMappedStatement;
// 执行sql语句
return invocation.proceed();
}
private static class BoundSqlSqlSource implements SqlSource {
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
Mybatis-plus字段填充器
Mybatis-plus提供了字段填充器,使得我们可以在插入或更新数据时自动填充某些字段,比如创建人、创建时间、更新人、更新时间等。
接下来是一个通过Mybatis-plus字段填充器实现创建时间和更新时间字段自动填充的示例:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入数据时填充创建时间和更新时间
* @param metaObject MyMetaObjectHandler对象
*/
@Override
public void insertFill(MetaObject metaObject) {
// 填充创建时间
setValue("createTime", LocalDateTime.now(), metaObject);
// 填充更新时间
setValue("updateTime", LocalDateTime.now(), metaObject);
}
/**
* 更新数据时填充更新时间
* @param metaObject MyMetaObjectHandler对象
*/
@Override
public void updateFill(MetaObject metaObject) {
// 填充更新时间
setValue("updateTime", LocalDateTime.now(), metaObject);
}
/**
* 设置字段值
* @param fieldName 字段名称
* @param value 字段值
* @param metaObject MyMetaObjectHandler对象
*/
private void setValue(String fieldName, Object value, MetaObject metaObject) {
if (metaObject.hasSetter(fieldName)) {
metaObject.setValue(fieldName, value);
}
}
}
Mybatis-plus类型处理器
Mybatis-plus提供了类型处理器,可以方便地将java类型转换为数据库类型,比如将Java的LocalDateTime类型转换为MYSQL的datetime类型。
接下来是一个通过Mybatis-plus类型处理器将LocalDataTime类型转成long类型的示例:
public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
/**
* 将LocalDateTime类型转换为long类型保存到数据库中
* @param preparedStatement PreparedStatement对象
* @param i 参数下标
* @param localDateTime LocalDateTime对象
* @param jdbcType jdbc类型
* @throws SQLException 执行异常
*/
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, LocalDateTime localDateTime, JdbcType jdbcType) throws SQLException {
preparedStatement.setLong(i, localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli());
}
/**
* 将long类型转换为LocalDateTime类型返回
* @param resultSet ResultSet对象
* @param s 列名
* @return 返回LocalDateTime对象
* @throws SQLException 执行异常
*/
@Override
public LocalDateTime getNullableResult(ResultSet resultSet, String s) throws SQLException {
long dateTimeStamp = resultSet.getLong(s);
if (dateTimeStamp != 0) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(dateTimeStamp), ZoneOffset.of("+8"));
}
return null;
}
/**
* 将long类型转换为LocalDateTime类型返回
* @param resultSet ResultSet对象
* @param i 列的下标
* @return 返回LocalDateTime对象
* @throws SQLException 执行异常
*/
@Override
public LocalDateTime getNullableResult(ResultSet resultSet, int i) throws SQLException {
long dateTimeStamp = resultSet.getLong(i);
if (dateTimeStamp != 0) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(dateTimeStamp), ZoneOffset.of("+8"));
}
return null;
}
/**
* 将long类型转换为LocalDateTime类型返回
* @param callableStatement CallableStatement对象
* @param i 列的下标
* @return 返回LocalDateTime对象
* @throws SQLException 执行异常
*/
@Override
public LocalDateTime getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long dateTimeStamp = callableStatement.getLong(i);
if (dateTimeStamp != 0) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(dateTimeStamp), ZoneOffset.of("+8"));
}
return null;
}
}
Mybatis-plus表名替换
表名替换功能可以让我们在查询时将真实表名替换为其他名字,这样可以避免其他应用程序获取到真实的表名。
接下来是一个通过Mybatis-plus表名替换功能将真实表名替换为别名表名的示例:
public class TableNameHandler implements ITableNameHandler {
/**
* 替换表名为别名
* @param tableName 表名
* @param tablePojo 实体类型
* @return 返回替换后的表名
*/
@Override
public String dynamicTableName(String tableName, Class<?> tablePojo) {
if (User.class.isAssignableFrom(tablePojo)) {
tableName = "user_alias";
} else if (Order.class.isAssignableFrom(tablePojo)) {
tableName = "order_alias";
}
return tableName;
}
}
Mybatis-plus SqlInjector
SqlInjector实现类可以方便的扩展Mybatis-plus的业务,比如多表关联查询、联合主键处理等。
接下来是一个通过Mybatis-plus SqlInjector实现联合主键处理的示例:
public abstract class AbstractLogicDeleteByIdsWithFill<T> implements ISqlInjector {
/**
* 通过ID批量逻辑删除
* @param tableMeta 逻辑删除表的表元信息
* @param idList ID列表
* @return 返回更新记录数
*/
protected abstract Integer deleteByIdsWithFill(TableInfo tableMeta, Collection<? extends Serializable> idList);
/**
* 获取删除标记字段
* @param tableMeta 表元信息
* @return 返回逻辑删除字段名称
*/
protected abstract String getLogicDeleteFieldName(TableInfo tableMeta);
/**
* 是否开启了多标签
* @param globalConfig 全局配置
* @param idSql 待执行的SQL
* @return 返回是否开启了多标签
*/
protected abstract boolean openMultiDelete(GlobalConfig globalConfig, String idSql);
/**
* 获取被逻辑删除的记录数
* @param metaObject 元对象,可以获取到Mybatis-plus的参数对象
* @return 返回记录数
*/
protected abstract String getDeleteCount(MetaObject metaObject);
@Override
public void injectSqlRunner(SqlRunner sqlRunner) {
GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(sqlRunner.getSqlSessionFactory());
MetaObject metaObject = sqlRunner.getSqlObject().getMetaObject();
TableInfo tableInfo = MetaUtils.getTableInfo(metaObject);
String logicDeleteField = getLogicDeleteFieldName(tableInfo);
// 获取所有ID
List<Serializable> idList = getIdList(metaObject, tableInfo);
if (idList.isEmpty()) {
return;
}
// 更新被逻辑删除的记录数
metaObject.setValue("deleteCount", getDeleteCount(metaObject));
// 生成SQL
String deleteSql = SqlHelper.logicDeleteSql(tableInfo, globalConfig.getLogicDeleteValue(logicDeleteField), globalConfig.getLogicNotDeleteValue(logicDeleteField), SqlHelper.getTableStructure(metaObject), idList);
logger.debug("logic-delete-sql: {}", deleteSql);
// 是否开启了多标签
if (openMultiDelete(globalConfig, deleteSql)) {
AtomicInteger results = new AtomicInteger(0);
String[] idArray = idList.stream().map((id) -> "'" + id + "'").toArray(String[]::new);
int batchSize = SqlExecutorConstants.BATCH_SIZE;
for (int i = 0; i < idArray.length; i += batchSize) {
StringBuilder sb = new StringBuilder(batchSize * 7);
sb.append(deleteSql).append(" AND id IN (");
for (int j = i; j < i + batchSize && j < idArray.length; j++) {
sb.append(idArray[j]).append(",");
}
sb.setCharAt(sb.length() - 1, ')');
String batchSql = sb.toString();
logger.debug("logic-delete-sql: {}", batchSql);
results.addAndGet(sqlRunner.execute(batchSql));
}
metaObject.setValue("result", results.get());
} else {
// 执行SQL
int result = deleteByIdsWithFill(tableInfo, idList);
metaObject.setValue("result", result);
}
}
/**
* 获取ID列表
* @param metaObject 元对象,可以获取到Mybatis-plus的参数对象
* @param tableInfo 表元信息
* @return 返回所有ID
*/
protected List<Serializable> getIdList(MetaObject metaObject, TableInfo tableInfo) {
String idPropertyName = tableInfo.getKeyProperty();
Object idPropertyValue = metaObject.getValue(idPropertyName);
if (idPropertyValue == null) {
return Collections.emptyList();
} else if (idPropertyValue instanceof Collection) {
return (List<Serializable>) ((Collection) idPropertyValue).stream().map((id) -> (Serializable) id).collect(Collectors.toList());
}
return Collections.singletonList((Serializable) idPropertyValue);
}
}
public class LogicDeleteByIdsWithFill extends AbstractLogicDeleteByIdsWithFill<User> {
/**
* 通过ID批量逻辑删除
* @param tableMeta 逻辑删除表的表元信息
* @param idList ID列表
* @return 返回更新记录数
*/
@Override
protected Integer deleteByIdsWithFill(TableInfo tableMeta, Collection<? extends Serializable> idList) {
// 自定义业务逻辑
return null;
}
/**
* 获取删除标记字段
* @param tableMeta 表元信息
* @return 返回逻辑删除字段名称
*/
@Override
protected String getLogicDeleteFieldName(TableInfo tableMeta) {
return "deleteFlag";
}
/**
* 是否开启了多标签
* @param globalConfig 全局配置
* @param idSql 待执行的SQL
* @return 返回是否开启了多标签
*/
@Override
protected boolean openMultiDelete(GlobalConfig globalConfig, String idSql) {
return false;
}
/**
* 获取被逻辑删除的记录数
* @param metaObject 元对象,可以获取到Mybatis-plus的参数对象
* @return 返回记录数
*/
@Override
protected String getDeleteCount(MetaObject metaObject) {
User user = (User) metaObject.getValue("et");
return "SELECT COUNT(*) FROM user WHERE deleteFlag=1 AND name='" + user.getName() + "'";
}
}
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:mybatis-plus拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理) - Python技术站