716 lines
22 KiB
Java
716 lines
22 KiB
Java
package com.imyeyu.spring.util;
|
||
|
||
import com.imyeyu.java.TimiJava;
|
||
import com.imyeyu.java.bean.CallbackArgReturn;
|
||
import com.imyeyu.java.bean.timi.TimiCode;
|
||
import com.imyeyu.java.bean.timi.TimiException;
|
||
import com.imyeyu.java.ref.Ref;
|
||
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||
import com.imyeyu.spring.annotation.table.Column;
|
||
import com.imyeyu.spring.annotation.table.DeleteColumn;
|
||
import com.imyeyu.spring.annotation.table.Id;
|
||
import com.imyeyu.spring.annotation.table.PageIgnore;
|
||
import com.imyeyu.spring.annotation.table.Table;
|
||
import com.imyeyu.spring.annotation.table.Transient;
|
||
import com.imyeyu.spring.bean.Logic;
|
||
import com.imyeyu.spring.bean.Page;
|
||
import com.imyeyu.spring.entity.Creatable;
|
||
import com.imyeyu.spring.entity.Deletable;
|
||
import com.imyeyu.spring.entity.Destroyable;
|
||
import com.imyeyu.spring.entity.Updatable;
|
||
import com.imyeyu.spring.mapper.BaseMapper;
|
||
import com.imyeyu.utils.Text;
|
||
import com.imyeyu.utils.Time;
|
||
import lombok.Getter;
|
||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||
|
||
import java.lang.reflect.Field;
|
||
import java.lang.reflect.ParameterizedType;
|
||
import java.lang.reflect.Type;
|
||
import java.util.ArrayList;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.UUID;
|
||
import java.util.concurrent.ConcurrentHashMap;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* SQL 提供器基类
|
||
* <p>包含所有 SQL 构建逻辑和实体元数据管理</p>
|
||
*
|
||
* @author 夜雨
|
||
* @since 2026-01-07 11:20
|
||
*/
|
||
public abstract class BaseSQLProvider {
|
||
|
||
/** 反射缓存 */
|
||
private static final Map<Class<?>, EntityMeta> ENTITY_META_CACHE = new ConcurrentHashMap<>();
|
||
|
||
/**
|
||
* 根据代理器上下文获取 Mapper 实体类元数据
|
||
*
|
||
* @param context 代理器上下文
|
||
* @return 实体类元数据
|
||
*/
|
||
protected EntityMeta getEntityMeta(ProviderContext context) {
|
||
Type[] types = context.getMapperType().getGenericInterfaces();
|
||
ParameterizedType type = (ParameterizedType) types[0];
|
||
Class<?> entityClass = (Class<?>) type.getActualTypeArguments()[0];
|
||
return getEntityMeta(entityClass);
|
||
}
|
||
|
||
/**
|
||
* 获取实体类元数据
|
||
*
|
||
* @param entityClass 实体类
|
||
* @return 元数据
|
||
*/
|
||
protected EntityMeta getEntityMeta(Class<?> entityClass) {
|
||
return ENTITY_META_CACHE.computeIfAbsent(entityClass, EntityMeta::new);
|
||
}
|
||
|
||
/**
|
||
* 构建分页查询 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param page 分页参数
|
||
* @param offset 偏移量占位符
|
||
* @param limit 限制数量占位符
|
||
* @return SQL
|
||
*/
|
||
protected String buildSelectByPageSQL(EntityMeta meta, String tableName, Page<?> page, String offset, String limit) {
|
||
StringBuilder sql = new StringBuilder();
|
||
sql.append("SELECT %s FROM `%s` WHERE 1 = 1".formatted(meta.selectPageClause, tableName));
|
||
|
||
// 添加软删除条件
|
||
appendSoftDeleteCondition(sql, meta);
|
||
|
||
// 添加 Page 查询条件
|
||
appendPageConditions(sql, page);
|
||
|
||
// 添加排序
|
||
appendOrderBy(sql, meta, page);
|
||
|
||
// 添加分页限制
|
||
sql.append(" LIMIT %s, %s".formatted(offset, limit));
|
||
return sql.toString();
|
||
}
|
||
|
||
/**
|
||
* 构建分页统计 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param page 分页参数
|
||
* @return SQL
|
||
*/
|
||
protected String buildCountByPageSQL(EntityMeta meta, String tableName, Page<?> page) {
|
||
StringBuilder sql = new StringBuilder();
|
||
sql.append("SELECT COUNT(*) FROM `%s` WHERE 1 = 1".formatted(tableName));
|
||
|
||
// 添加软删除条件
|
||
appendSoftDeleteCondition(sql, meta);
|
||
|
||
// 添加 Page 查询条件
|
||
appendPageConditions(sql, page);
|
||
|
||
return sql.toString();
|
||
}
|
||
|
||
/**
|
||
* 添加软删除条件
|
||
*
|
||
* @param sql SQL 构建器
|
||
* @param meta 实体元数据
|
||
*/
|
||
protected void appendSoftDeleteCondition(StringBuilder sql, EntityMeta meta) {
|
||
if (meta.canDelete) {
|
||
sql.append(" AND (`deleted_at` IS NULL OR %s < `deleted_at`)".formatted(Time.now()));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加 Page 查询条件(精准查询和模糊查询)
|
||
*
|
||
* @param sql SQL 构建器
|
||
* @param page 分页参数
|
||
*/
|
||
protected void appendPageConditions(StringBuilder sql, Page<?> page) {
|
||
// 精准查询
|
||
if (TimiJava.isNotEmpty(page.getEqualsExample())) {
|
||
Object obj = page.getEqualsExample();
|
||
EntityMeta metaExample = getEntityMeta(obj.getClass());
|
||
String conditionClause = metaExample.fieldColumnList.stream()
|
||
.filter(fc -> fc.isNotEmpty(obj))
|
||
.map(fc -> "`%s` = '%s'".formatted(fc.columnName, fc.getAsString(obj)))
|
||
.collect(Collectors.joining(" %s ".formatted(page.getEqualsLogic())));
|
||
if (TimiJava.isNotEmpty(conditionClause)) {
|
||
sql.append(" AND ").append(conditionClause);
|
||
}
|
||
}
|
||
// 模糊查询
|
||
if (TimiJava.isNotEmpty(page.getLikesExample())) {
|
||
Object obj = page.getLikesExample();
|
||
EntityMeta metaExample = getEntityMeta(obj.getClass());
|
||
String conditionClause = metaExample.fieldColumnList.stream()
|
||
.filter(fc -> fc.isNotEmpty(obj))
|
||
.map(fc -> "`%s` LIKE CONCAT('%%', '%s', '%%')".formatted(fc.columnName, fc.getAsString(obj)))
|
||
.collect(Collectors.joining(" %s ".formatted(page.getLikesLogic())));
|
||
if (TimiJava.isNotEmpty(conditionClause)) {
|
||
sql.append(" AND (").append(conditionClause).append(')');
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加排序子句
|
||
*
|
||
* @param sql SQL 构建器
|
||
* @param meta 实体元数据
|
||
* @param page 分页参数
|
||
*/
|
||
protected void appendOrderBy(StringBuilder sql, EntityMeta meta, Page<?> page) {
|
||
if (TimiJava.isNotEmpty(page.getOrderMap())) {
|
||
sql.append(" ORDER BY ");
|
||
for (Map.Entry<String, BaseMapper.OrderType> item : page.getOrderMap().entrySet()) {
|
||
sql.append("`%s` %s, ".formatted(
|
||
Text.camelCase2underscore(item.getKey()),
|
||
item.getValue().toString()
|
||
));
|
||
}
|
||
sql.deleteCharAt(sql.length() - 2);
|
||
} else {
|
||
// 默认排序
|
||
if (meta.canCreate && !meta.canUpdate) {
|
||
sql.append(" ORDER BY `created_at` DESC");
|
||
}
|
||
if (meta.canCreate && meta.canUpdate) {
|
||
sql.append(" ORDER BY COALESCE(`updated_at`, `created_at`) DESC");
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 构建示例查询条件子句
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param entity 示例实体
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @param logic 条件连接逻辑
|
||
* @return 条件子句
|
||
*/
|
||
protected String buildExampleConditions(EntityMeta meta, Object entity, String paramPrefix, Logic logic) {
|
||
return meta.fieldColumnList.stream()
|
||
.filter(fc -> fc.isNotEmpty(entity))
|
||
.map(fc -> "`%s` = #{%s%s}".formatted(fc.columnName, paramPrefix, fc.fieldName))
|
||
.collect(Collectors.joining(" %s ".formatted(logic)));
|
||
}
|
||
|
||
/**
|
||
* 构建插入 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 实体对象
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @return SQL
|
||
*/
|
||
protected String buildInsertSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix) {
|
||
String columns = meta.fieldColumnList.stream()
|
||
.map(fc -> "`%s`".formatted(fc.columnName))
|
||
.collect(Collectors.joining(", "));
|
||
String values = meta.fieldColumnList.stream().map(fc -> {
|
||
try {
|
||
if (fc.isAutoUUID && TimiJava.isEmpty(Ref.getFieldValue(entity, fc.field, String.class))) {
|
||
String uuid = UUID.randomUUID().toString();
|
||
if (fc.isAutoUpperUUID) {
|
||
uuid = uuid.toUpperCase();
|
||
}
|
||
Ref.setFieldValue(entity, fc.field, uuid);
|
||
}
|
||
} catch (IllegalAccessException e) {
|
||
throw new TimiException(TimiCode.ERROR).msgKey("auto set field:%s value error".formatted(fc.fieldName));
|
||
}
|
||
if (entity instanceof Creatable creatableEntity && creatableEntity.getCreatedAt() == null) {
|
||
creatableEntity.setCreatedAt(Time.now());
|
||
}
|
||
return "#{%s%s}".formatted(paramPrefix, fc.fieldName);
|
||
}).collect(Collectors.joining(", "));
|
||
return "INSERT INTO `%s` (%s) VALUES (%s)".formatted(tableName, columns, values);
|
||
}
|
||
|
||
/**
|
||
* 构建根据 ID 查询 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param idParam ID 参数占位符(如 "id")
|
||
* @return SQL
|
||
*/
|
||
protected String buildSelectByIdSQL(EntityMeta meta, String tableName, String idParam) {
|
||
TimiException.required(meta.idFieldColumn, "not found id field in %s".formatted(meta.entityClass));
|
||
|
||
StringBuilder sql = new StringBuilder();
|
||
sql.append("SELECT %s FROM `%s` WHERE `%s` = #{%s}".formatted(
|
||
meta.selectAllClause,
|
||
tableName,
|
||
meta.idFieldColumn.columnName,
|
||
idParam
|
||
));
|
||
if (meta.canDelete) {
|
||
sql.append(" AND (`deleted_at` IS NULL OR %s < `deleted_at`)".formatted(Time.now()));
|
||
}
|
||
return sql.append(" LIMIT 1").toString();
|
||
}
|
||
|
||
/**
|
||
* 构建根据示例查询全部 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 示例实体
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @param logic 条件连接逻辑
|
||
* @return SQL
|
||
*/
|
||
protected String buildSelectAllByExampleSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix, Logic logic) {
|
||
String conditionClause = buildExampleConditions(meta, entity, paramPrefix, logic);
|
||
|
||
StringBuilder sql = new StringBuilder();
|
||
sql.append("SELECT %s FROM `%s` WHERE %s".formatted(meta.selectAllClause, tableName, conditionClause));
|
||
if (meta.canDelete) {
|
||
if (TimiJava.isNotEmpty(conditionClause)) {
|
||
sql.append(" AND ");
|
||
}
|
||
sql.append("(`deleted_at` IS NULL OR %s < `deleted_at`)".formatted(Time.now()));
|
||
}
|
||
return sql.toString();
|
||
}
|
||
|
||
/**
|
||
* 构建更新 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 实体对象
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @return SQL
|
||
*/
|
||
protected String buildUpdateSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix) {
|
||
TimiException.required(meta.idFieldColumn, "not found id field in %s".formatted(meta.entityClass));
|
||
TimiException.required(meta.canUpdate, "not allow update for %s".formatted(meta.entityClass));
|
||
|
||
if (entity instanceof Updatable updatable) {
|
||
updatable.setUpdatedAt(Time.now());
|
||
}
|
||
String setClause = meta.fieldColumnList.stream()
|
||
.filter(FieldColumn::isNotId)
|
||
.map(fc -> "`%s` = #{%s%s}".formatted(fc.columnName, paramPrefix, fc.fieldName))
|
||
.collect(Collectors.joining(", "));
|
||
return "UPDATE `%s` SET %s WHERE `%s` = #{%s%s}".formatted(
|
||
tableName,
|
||
setClause,
|
||
meta.idFieldColumn.columnName,
|
||
paramPrefix,
|
||
meta.idFieldColumn.fieldName
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 构建选择性更新 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 实体对象
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @return SQL
|
||
*/
|
||
protected String buildUpdateSelectiveSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix) {
|
||
TimiException.required(meta.idFieldColumn, "not found id field in %s".formatted(meta.entityClass));
|
||
TimiException.required(meta.canUpdate, "not allow update for %s".formatted(meta.entityClass));
|
||
|
||
if (entity instanceof Updatable updatable) {
|
||
updatable.setUpdatedAt(Time.now());
|
||
}
|
||
String setClause = meta.fieldColumnList.stream()
|
||
.filter(FieldColumn::isNotId)
|
||
.filter(fc -> fc.isNotNull(entity))
|
||
.map(fc -> "`%s` = #{%s%s}".formatted(fc.columnName, paramPrefix, fc.fieldName))
|
||
.collect(Collectors.joining(", "));
|
||
return "UPDATE `%s` SET %s WHERE `%s` = #{%s%s}".formatted(
|
||
tableName,
|
||
setClause,
|
||
meta.idFieldColumn.columnName,
|
||
paramPrefix,
|
||
meta.idFieldColumn.fieldName
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 构建软删除 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param idParam ID 参数占位符(如 "id")
|
||
* @return SQL
|
||
*/
|
||
protected String buildDeleteSQL(EntityMeta meta, String tableName, String idParam) {
|
||
TimiException.required(meta.idFieldColumn, "not found id field in %s".formatted(meta.entityClass));
|
||
TimiException.requiredTrue(meta.canDelete, "not allow soft delete for %s".formatted(meta.entityClass));
|
||
|
||
return "UPDATE `%s` SET `deleted_at` = %s WHERE `%s` = #{%s}".formatted(
|
||
tableName,
|
||
Time.now(),
|
||
meta.idFieldColumn.columnName,
|
||
idParam
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 构建批量逻辑删除 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 示例实体
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @param logic 条件连接逻辑
|
||
* @return SQL
|
||
*/
|
||
protected String buildDeleteAllByExampleSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix, Logic logic) {
|
||
TimiException.required(meta.canDelete, "not allow delete for %s".formatted(meta.entityClass));
|
||
|
||
FieldColumn deleteColumn = meta.getFieldColumnList().stream()
|
||
.filter(fc -> fc.isDeleteColumn)
|
||
.findFirst()
|
||
.orElse(null);
|
||
TimiException.required(deleteColumn, "unknown delete column, use com.imyeyu.spring.annotation.table.DeleteColumn annotation on field");
|
||
assert deleteColumn != null;
|
||
assert deleteColumn.deleteColumnType != null;
|
||
|
||
String delClause = meta.fieldColumnList.stream()
|
||
.filter(FieldColumn::isNotId)
|
||
.filter(fc -> fc.isNotEmpty(entity))
|
||
.map(fc -> "`%s` = #{%s%s}".formatted(fc.columnName, paramPrefix, fc.fieldName))
|
||
.collect(Collectors.joining(" %s ".formatted(logic)));
|
||
StringBuilder sql = new StringBuilder("UPDATE `%s` SET `%s` = ".formatted(tableName, deleteColumn.getColumnName()));
|
||
sql.append("'").append(switch (deleteColumn.deleteColumnType) {
|
||
case UNIX -> Time.now();
|
||
case DATE, DATE_TIME -> new Date();
|
||
}).append("'");
|
||
sql.append(" WHERE ").append(delClause);
|
||
return sql.toString();
|
||
}
|
||
|
||
/**
|
||
* 构建硬删除 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param idParam ID 参数占位符(如 "id")
|
||
* @return SQL
|
||
*/
|
||
protected String buildDestroySQL(EntityMeta meta, String tableName, String idParam) {
|
||
TimiException.required(meta.idFieldColumn, "not found id field in %s".formatted(meta.entityClass));
|
||
TimiException.requiredTrue(meta.canDestroy, "not allow destroy for %s".formatted(meta.entityClass));
|
||
|
||
return "DELETE FROM `%s` WHERE `%s` = #{%s}".formatted(tableName, meta.idFieldColumn.columnName, idParam);
|
||
}
|
||
|
||
/**
|
||
* 构建批量物理删除 SQL
|
||
*
|
||
* @param meta 实体元数据
|
||
* @param tableName 表名
|
||
* @param entity 示例实体
|
||
* @param paramPrefix 参数前缀(如 "entity."),空字符串表示无前缀
|
||
* @param logic 条件连接逻辑
|
||
* @return SQL
|
||
*/
|
||
protected String buildDestroyAllByExampleSQL(EntityMeta meta, String tableName, Object entity, String paramPrefix, Logic logic) {
|
||
TimiException.required(meta.canDestroy, "not allow destroy for %s".formatted(meta.entityClass));
|
||
String destroyClause = meta.fieldColumnList.stream()
|
||
.filter(FieldColumn::isNotId)
|
||
.filter(fc -> fc.isNotEmpty(entity))
|
||
.map(fc -> "`%s` = #{%s%s}".formatted(fc.columnName, paramPrefix, fc.fieldName))
|
||
.collect(Collectors.joining(" %s ".formatted(logic)));
|
||
return "DELETE FROM `%s` WHERE %s".formatted(tableName, destroyClause);
|
||
}
|
||
|
||
/**
|
||
* 实体元数据
|
||
*
|
||
* @author 夜雨
|
||
* @since 2025-02-05 23:47
|
||
*/
|
||
@Getter
|
||
protected static class EntityMeta {
|
||
|
||
/** 实体类 */
|
||
final Class<?> entityClass;
|
||
|
||
/** 表名 */
|
||
final String table;
|
||
|
||
/** 查询字段映射 */
|
||
final String selectAllClause;
|
||
|
||
/** 页面查询字段映射 */
|
||
final String selectPageClause;
|
||
|
||
/** ID 字段 */
|
||
final FieldColumn idFieldColumn;
|
||
|
||
/** 只读的列名字段名映射,Map<列名,字段名> */
|
||
final List<FieldColumn> fieldColumnList;
|
||
|
||
/** true 为可创建 */
|
||
final boolean canCreate;
|
||
|
||
/** true 为可更新 */
|
||
final boolean canUpdate;
|
||
|
||
/** true 为可删除(软删除) */
|
||
final boolean canDelete;
|
||
|
||
/** true 为可销毁(硬删除) */
|
||
final boolean canDestroy;
|
||
|
||
/**
|
||
* 创建实体元数据
|
||
*
|
||
* @param entityClass 实体类型
|
||
*/
|
||
public EntityMeta(Class<?> entityClass) {
|
||
this.entityClass = entityClass;
|
||
|
||
// 表名
|
||
while (entityClass.isAnnotationPresent(Transient.class)) {
|
||
entityClass = entityClass.getSuperclass();
|
||
}
|
||
Table table = entityClass.getAnnotation(Table.class);
|
||
if (table == null) {
|
||
this.table = Text.camelCase2underscore(entityClass.getSimpleName());
|
||
} else {
|
||
this.table = table.value();
|
||
TimiException.required(this.table, String.format("empty table annotation value for %s entity", entityClass.getName()));
|
||
}
|
||
List<Field> allFieldList = Ref.listAllFields(entityClass);
|
||
FieldColumn idFieldColumn = null;
|
||
List<FieldColumn> fieldColumnList = new ArrayList<>();
|
||
for (int i = 0; i < allFieldList.size(); i++) {
|
||
Field field = allFieldList.get(i);
|
||
if (field.isAnnotationPresent(Transient.class)) {
|
||
continue;
|
||
}
|
||
FieldColumn fieldColumn = new FieldColumn(field);
|
||
if (fieldColumn.isId) {
|
||
TimiException.requiredNull(idFieldColumn, String.format("multi id field for %s entity", entityClass.getName()));
|
||
idFieldColumn = fieldColumn;
|
||
}
|
||
fieldColumnList.add(fieldColumn);
|
||
}
|
||
this.selectAllClause = buildSelectClause(fieldColumnList, null);
|
||
this.selectPageClause = buildSelectClause(fieldColumnList, fc -> !fc.getField().isAnnotationPresent(PageIgnore.class));
|
||
this.idFieldColumn = idFieldColumn;
|
||
this.fieldColumnList = List.of(fieldColumnList.toArray(new FieldColumn[0])); // 转为只读
|
||
canCreate = Creatable.class.isAssignableFrom(entityClass);
|
||
canUpdate = Updatable.class.isAssignableFrom(entityClass);
|
||
canDelete = Deletable.class.isAssignableFrom(entityClass);
|
||
canDestroy = Destroyable.class.isAssignableFrom(entityClass);
|
||
}
|
||
|
||
private String buildSelectClause(List<FieldColumn> fieldColumnList, CallbackArgReturn<FieldColumn, Boolean> callback) {
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < fieldColumnList.size(); i++) {
|
||
FieldColumn fieldColumn = fieldColumnList.get(i);
|
||
Field field = fieldColumn.getField();
|
||
if (callback != null && !callback.handler(fieldColumn)) {
|
||
continue;
|
||
}
|
||
Column column = field.getAnnotation(Column.class);
|
||
if (column == null) {
|
||
sb.append('`').append(fieldColumn.columnName).append('`');
|
||
sb.append(',');
|
||
} else {
|
||
// 自定义映射列名
|
||
sb.append('`').append(column.value()).append('`');
|
||
sb.append(" AS `").append(fieldColumn.fieldName).append('`');
|
||
sb.append(',');
|
||
}
|
||
}
|
||
return sb.substring(0, sb.length() - 1);
|
||
}
|
||
|
||
/**
|
||
* 是否可创建
|
||
*
|
||
* @return true 为可创建
|
||
*/
|
||
public boolean canCreate() {
|
||
return canCreate;
|
||
}
|
||
|
||
/**
|
||
* 是否可更新
|
||
*
|
||
* @return true 为可更新
|
||
*/
|
||
public boolean canUpdate() {
|
||
return canUpdate;
|
||
}
|
||
|
||
/**
|
||
* 是否可删除
|
||
*
|
||
* @return true 为可删除
|
||
*/
|
||
public boolean canDelete() {
|
||
return canDelete;
|
||
}
|
||
|
||
/**
|
||
* 是否可销毁
|
||
*
|
||
* @return true 为可销毁
|
||
*/
|
||
public boolean canDestroy() {
|
||
return canDestroy;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 实体字段属性
|
||
*
|
||
* @author 夜雨
|
||
* @since 2025-02-07 09:54
|
||
*/
|
||
@Getter
|
||
protected static class FieldColumn {
|
||
|
||
/** 字段 */
|
||
final Field field;
|
||
|
||
/** 字段名 */
|
||
final String fieldName;
|
||
|
||
/** 列名 */
|
||
final String columnName;
|
||
|
||
/** true 为 ID */
|
||
final boolean isId;
|
||
|
||
/** true 为自动生成 UUID */
|
||
final boolean isAutoUUID;
|
||
|
||
final boolean isAutoUpperUUID;
|
||
|
||
final boolean isDeleteColumn;
|
||
|
||
final DeleteColumn.Type deleteColumnType;
|
||
|
||
/**
|
||
* 创建字段映射
|
||
*
|
||
* @param field 字段
|
||
*/
|
||
public FieldColumn(Field field) {
|
||
this.field = field;
|
||
|
||
fieldName = field.getName();
|
||
Column column = field.getAnnotation(Column.class);
|
||
if (column == null) {
|
||
columnName = Text.camelCase2underscore(field.getName());
|
||
} else {
|
||
columnName = column.value();
|
||
TimiException.required(columnName, "empty field:%s column annotation value for %s entity".formatted(field.getName(), field.getDeclaringClass()));
|
||
}
|
||
isId = field.isAnnotationPresent(Id.class);
|
||
isAutoUUID = field.isAnnotationPresent(AutoUUID.class);
|
||
if (isAutoUUID) {
|
||
isAutoUpperUUID = field.getAnnotation(AutoUUID.class).upper();
|
||
} else {
|
||
isAutoUpperUUID = false;
|
||
}
|
||
isDeleteColumn = field.isAnnotationPresent(DeleteColumn.class);
|
||
if (isDeleteColumn) {
|
||
deleteColumnType = field.getAnnotation(DeleteColumn.class).value();
|
||
} else {
|
||
deleteColumnType = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断字段值是否为空
|
||
*
|
||
* @param entity 实体
|
||
* @return true 为 null
|
||
*/
|
||
public boolean isNull(Object entity) {
|
||
try {
|
||
return Ref.getFieldValue(entity, field, Object.class) == null;
|
||
} catch (IllegalAccessException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断字段值是否非空
|
||
*
|
||
* @param entity 实体
|
||
* @return true 为非 null
|
||
*/
|
||
public boolean isNotNull(Object entity) {
|
||
return !isNull(entity);
|
||
}
|
||
|
||
/**
|
||
* 判断字段值是否为空
|
||
*
|
||
* @param entity 实体
|
||
* @return true 为空
|
||
*/
|
||
public boolean isEmpty(Object entity) {
|
||
try {
|
||
return TimiJava.isEmpty(Ref.getFieldValue(entity, field, Object.class));
|
||
} catch (IllegalAccessException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断字段值是否非空
|
||
*
|
||
* @param entity 实体
|
||
* @return true 为非空
|
||
*/
|
||
public boolean isNotEmpty(Object entity) {
|
||
return !isEmpty(entity);
|
||
}
|
||
|
||
/**
|
||
* 获取字段字符串值
|
||
*
|
||
* @param obj 实体
|
||
* @return 字符串值
|
||
*/
|
||
public String getAsString(Object obj) {
|
||
try {
|
||
return field.get(obj).toString();
|
||
} catch (IllegalAccessException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 是否非 ID 字段
|
||
*
|
||
* @return true 为非 ID 字段
|
||
*/
|
||
public boolean isNotId() {
|
||
return !isId();
|
||
}
|
||
}
|
||
}
|