利用 MyBatis Plus 的 getter 转字段名
Mybatis-Plus结合lambda表达式获取entity的数据库字段名
代码
@Component
public class ColumnResolver<T> extends AbstractLambdaWrapper<T, ColumnResolver<T>> {
/**
* 这个不需要,MyBatis Plus 自己使用的
*
* @return null
*/
@Override
protected ColumnResolver<T> instance() {
return null;
}
// ---- 以下直接调用父类,只是提升了访问级别
@Override
public String columnToString(SFunction<T, ?> column) {
return super.columnToString(column);
}
}
使用时直接注入即可
private final ColumnResolver<Entity> columnResolver;
原理
自动添加 Mapper 时解析实体信息生成表名及字段名
LambdaUtils 会全局缓存解析后的信息
MybatisMapperRegistry#addMapper
--->
TableInfoHelper#initTableInfo(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)
从 AbstractLambdaWrapper 的逻辑可以看出,上面的 ColumnResolver 最好做成多例/prototype 的,因为实例只会初始化一次且只会保存一个实体对应的字段缓存。
public abstract class AbstractLambdaWrapper {
protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
// 缓存获取
ColumnCache cache = getColumnCache(column);
return onlyColumn ? cache.getColumn() : cache.getColumnSelect();
}
protected ColumnCache getColumnCache(SFunction<T, ?> column) {
LambdaMeta meta = LambdaUtils.extract(column);
// 字段名
String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
// 实体类
Class<?> instantiatedClass = meta.getInstantiatedClass();
tryInitCache(instantiatedClass);
return getColumnCache(fieldName, instantiatedClass);
}
private void tryInitCache(Class<?> lambdaClass) {
// 没有初始化
if (!initColumnMap) {
// 忽略
final Class<T> entityClass = getEntityClass();
if (entityClass != null) {
lambdaClass = entityClass;
}
// 根据实体类获取全局缓存, 没有则返回 null (不会实时解析)
columnMap = LambdaUtils.getColumnMap(lambdaClass);
Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
// 当前实例初始化过一次就不会再次解析
initColumnMap = true;
}
}
private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
// 根据字段名获取相应信息, 没有则抛出异常
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
fieldName, lambdaClass.getName());
return columnCache;
}
}
多实体适配
如果要使用多实体,那么就需要进行改部分代码进行适配。
首先要实体对应的column,其次要对初始化方法进行加锁同步处理。
public class ColumnResolver<T> extends AbstractLambdaWrapper<T, ColumnResolver<T>> {
private final ConcurrentMap<Class<?>, Map<String, ColumnCache>> class2ColumnMap = new ConcurrentHashMap<>();
private final Object initLock = new Object();
/**
* 这个不需要,MyBatis Plus 自己使用的
*
* @return null
*/
@Override
protected ColumnResolver<T> instance() {
return null;
}
// ---- 以下直接调用父类,只是提升了访问级别
@Override
public String columnToString(SFunction<T, ?> column) {
return super.columnToString(column);
}
@Override
protected ColumnCache getColumnCache(SFunction<T, ?> column) {
LambdaMeta meta = LambdaUtils.extract(column);
String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
Class<?> instantiatedClass = meta.getInstantiatedClass();
tryInitCache(instantiatedClass);
return getColumnCache(fieldName, instantiatedClass);
}
private void tryInitCache(Class<?> lambdaClass) {
Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
if (Objects.nonNull(columnMap)) {
return;
}
synchronized (initLock) {
tryInitCache0(lambdaClass);
}
}
private void tryInitCache0(Class<?> lambdaClass) {
Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
if (Objects.nonNull(columnMap)) {
return;
}
final Class<T> entityClass = getEntityClass();
if (entityClass != null) {
lambdaClass = entityClass;
}
columnMap = LambdaUtils.getColumnMap(lambdaClass);
Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
class2ColumnMap.put(lambdaClass, columnMap);
}
private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
fieldName, lambdaClass.getName());
return columnCache;
}
}