diff --git a/src/main/java/com/guwan/backend/MergeStrategy/JoinBuilder.java b/src/main/java/com/guwan/backend/MergeStrategy/JoinBuilder.java index 31764d3..f7e2ee9 100644 --- a/src/main/java/com/guwan/backend/MergeStrategy/JoinBuilder.java +++ b/src/main/java/com/guwan/backend/MergeStrategy/JoinBuilder.java @@ -6,9 +6,11 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.query.QueryUtils; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import com.guwan.backend.MergeStrategy.LambdaUtils.PropertyFunction; /** * 自定义连表查询构建器 @@ -41,16 +43,16 @@ public class JoinBuilder { } /** - * 添加连接表(使用方法引用) + * 添加连接表(使用增强的方法引用) * @param joinClass 要连接的表实体类 * @param sourceFieldGetter 主表外键字段的getter方法引用 * @param targetFieldGetter 连接表目标字段的getter方法引用 */ public JoinBuilder join(Class joinClass, - Function sourceFieldGetter, - Function targetFieldGetter) { - String sourceField = getFieldNameFromGetter(sourceFieldGetter); - String targetField = getFieldNameFromGetter(targetFieldGetter); + PropertyFunction sourceFieldGetter, + PropertyFunction targetFieldGetter) { + String sourceField = LambdaUtils.getPropertyName(sourceFieldGetter); + String targetField = LambdaUtils.getPropertyName(targetFieldGetter); return join(joinClass, sourceField, targetField); } @@ -69,13 +71,13 @@ public class JoinBuilder { } /** - * 添加等值条件(使用方法引用) + * 添加等值条件(使用增强的方法引用) * @param entityClass 实体类 * @param fieldGetter 字段的getter方法引用 * @param value 字段值 */ - public JoinBuilder equal(Class entityClass, Function fieldGetter, V value) { - String fieldName = getFieldNameFromGetter(fieldGetter); + public JoinBuilder equal(Class entityClass, PropertyFunction fieldGetter, V value) { + String fieldName = LambdaUtils.getPropertyName(fieldGetter); return equal(entityClass, fieldName, value); } @@ -89,17 +91,17 @@ public class JoinBuilder { } /** - * 选择返回的字段(使用方法引用) + * 选择返回的字段(使用增强的方法引用) * @param fieldGetters 方法引用列表 */ @SafeVarargs - public final JoinBuilder select(Class entityClass, Function... fieldGetters) { + public final JoinBuilder select(Class entityClass, PropertyFunction... fieldGetters) { if (selectedFields == null) { selectedFields = new HashMap<>(); } String[] fieldNames = Arrays.stream(fieldGetters) - .map(this::getFieldNameFromGetter) + .map(LambdaUtils::getPropertyName) .toArray(String[]::new); selectedFields.put(entityClass, fieldNames); @@ -107,13 +109,13 @@ public class JoinBuilder { } /** - * 添加一个具有别名的字段选择(优先级高于select方法) + * 添加一个具有别名的字段选择(使用增强的方法引用) * @param alias 字段别名 * @param entityClass 实体类 * @param fieldGetter 字段的getter方法引用 */ - public JoinBuilder selectAs(String alias, Class entityClass, Function fieldGetter) { - String fieldName = getFieldNameFromGetter(fieldGetter); + public JoinBuilder selectAs(String alias, Class entityClass, PropertyFunction fieldGetter) { + String fieldName = LambdaUtils.getPropertyName(fieldGetter); // 存储字段信息,稍后在configureSelections处理 if (selectedFields == null) { @@ -154,8 +156,8 @@ public class JoinBuilder { @SafeVarargs public final JoinBuilder selectDto(Class dtoClass, FieldMapping... mappings) { for (FieldMapping mapping : mappings) { - String dtoFieldName = getFieldNameFromGetter(mapping.getDtoFieldGetter()); - String entityFieldName = getFieldNameFromGetter(mapping.getEntityFieldGetter()); + String dtoFieldName = mapping.getDtoFieldName(); + String entityFieldName = mapping.getEntityFieldName(); Class entityClass = mapping.getEntityClass(); // 存储字段信息,稍后在configureSelections处理 @@ -384,120 +386,53 @@ public class JoinBuilder { } /** - * 从getter方法引用中提取属性名 + * 字段映射定义,用于DTO映射 */ - private String getFieldNameFromGetter(Function getter) { - // 直接尝试从方法引用的toString中获取 - try { - String methodRef = getter.toString(); - if (methodRef.contains("::")) { - // 处理方法引用格式: com.example.Entity::getName - String methodName = methodRef.substring(methodRef.lastIndexOf("::") + 2); - return extractFieldNameFromMethod(methodName); - } else if (methodRef.contains("->")) { - // 处理Lambda表达式格式: (Entity e) -> e.getName() - String methodCall = methodRef.substring(methodRef.indexOf("->") + 2).trim(); - String methodName = methodCall.substring(methodCall.lastIndexOf(".") + 1); - if (methodName.endsWith(")")) { - methodName = methodName.substring(0, methodName.indexOf("(")); - } - return extractFieldNameFromMethod(methodName); - } - } catch (Exception ignored) { - // 忽略异常,尝试下一种方法 + public static class FieldMapping { + private final String dtoFieldName; + private final String entityFieldName; + private final Class entityClass; + + private FieldMapping(String dtoFieldName, String entityFieldName, Class entityClass) { + this.dtoFieldName = dtoFieldName; + this.entityFieldName = entityFieldName; + this.entityClass = entityClass; } - try { - String getterName = getter.getClass().getName(); - if (getterName.contains("$$Lambda$")) { - // 处理Lambda表达式 - if (getterName.contains("get")) { - // 尝试从名称中提取字段名 - int getIndex = getterName.lastIndexOf("get"); - if (getIndex >= 0 && getIndex + 3 < getterName.length()) { - String fieldName = getterName.substring(getIndex + 3); - if (fieldName.indexOf('$') > 0) { - fieldName = fieldName.substring(0, fieldName.indexOf('$')); - } - return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); - } - } + public static FieldMapping of(PropertyFunction dtoFieldGetter, PropertyFunction entityFieldGetter) { + String dtoFieldName = LambdaUtils.getPropertyName(dtoFieldGetter); + String entityFieldName = LambdaUtils.getPropertyName(entityFieldGetter); + + // 通过反射获取entityClass + Class entityClass = null; + try { + // 尝试从方法引用中提取类信息 + Method writeReplace = entityFieldGetter.getClass().getDeclaredMethod("writeReplace"); + writeReplace.setAccessible(true); + Object serializedLambda = writeReplace.invoke(entityFieldGetter); + String implClass = (String) serializedLambda.getClass().getMethod("getImplClass").invoke(serializedLambda); + implClass = implClass.replace('/', '.'); + entityClass = (Class) Class.forName(implClass); + } catch (Exception e) { + // 忽略异常,entityClass将在运行时确定 } - } catch (Exception ignored) { - // 忽略异常,fallback到基于字符串的方法 + + return new FieldMapping<>(dtoFieldName, entityFieldName, entityClass); } - // Fallback到基于字符串的方法 - String methodReference = getter.toString(); - return getFieldNameFromGetterString(methodReference); - } - - /** - * 从getter方法字符串中提取属性名 - */ - private String getFieldNameFromGetterString(String methodReference) { - // 方法引用的格式通常是:类名::get字段名 或 Lambda表达式 - if (methodReference.contains("::")) { - // 处理方法引用格式: com.example.Entity::getName - String methodName = methodReference.substring(methodReference.lastIndexOf("::") + 2); - return extractFieldNameFromMethod(methodName); - } else if (methodReference.contains("->")) { - // 处理Lambda表达式格式: (Entity e) -> e.getName() - String methodCall = methodReference.substring(methodReference.indexOf("->") + 2).trim(); - String methodName = methodCall.substring(methodCall.lastIndexOf(".") + 1); - if (methodName.endsWith(")")) { - methodName = methodName.substring(0, methodName.indexOf("(")); - } - return extractFieldNameFromMethod(methodName); + public String getDtoFieldName() { + return dtoFieldName; } - // 无法识别的格式,返回原始字符串 - return methodReference; - } - - /** - * 从方法名中提取字段名 - */ - private String extractFieldNameFromMethod(String methodName) { - if (methodName.startsWith("get") && methodName.length() > 3) { - // getName -> name (首字母小写) - String fieldName = methodName.substring(3); - return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); - } else if (methodName.startsWith("is") && methodName.length() > 2) { - // isActive -> active (首字母小写) - String fieldName = methodName.substring(2); - return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + public String getEntityFieldName() { + return entityFieldName; } - return methodName; - } - - /** - * 首字母大写 - */ - private String capitalize(String str) { - if (str == null || str.isEmpty()) { - return str; - } - return str.substring(0, 1).toUpperCase() + str.substring(1); - } - - private enum Operator { - EQUAL - // 可以添加更多操作符支持,如LIKE, IN, GREATER_THAN等 - } - - private static class Condition { - private final String fieldName; - private final Object value; - private final Operator operator; - - Condition(String fieldName, Object value, Operator operator) { - this.fieldName = fieldName; - this.value = value; - this.operator = operator; + + public Class getEntityClass() { + return entityClass; } } - + /** * 连接信息 */ @@ -524,47 +459,20 @@ public class JoinBuilder { } } - /** - * 字段映射定义,用于DTO映射 - */ - public static class FieldMapping { - private final Function dtoFieldGetter; - private final Function entityFieldGetter; - private final Class entityClass; - - private FieldMapping(Function dtoFieldGetter, Function entityFieldGetter, Class entityClass) { - this.dtoFieldGetter = dtoFieldGetter; - this.entityFieldGetter = entityFieldGetter; - this.entityClass = entityClass; - } - - public static FieldMapping of(Function dtoFieldGetter, Function entityFieldGetter) { - // 通过反射或其他方式获取entityClass - Class entityClass = null; - try { - // 尝试从方法引用中提取类信息 - String methodRef = entityFieldGetter.toString(); - if (methodRef.contains("::")) { - String className = methodRef.substring(0, methodRef.indexOf("::")); - entityClass = (Class) Class.forName(className); - } - } catch (Exception e) { - // 忽略异常,entityClass将在运行时确定 - } - - return new FieldMapping<>(dtoFieldGetter, entityFieldGetter, entityClass); - } - - public Function getDtoFieldGetter() { - return dtoFieldGetter; - } - - public Function getEntityFieldGetter() { - return entityFieldGetter; - } - - public Class getEntityClass() { - return entityClass; + private enum Operator { + EQUAL + // 可以添加更多操作符支持,如LIKE, IN, GREATER_THAN等 + } + + private static class Condition { + private final String fieldName; + private final Object value; + private final Operator operator; + + Condition(String fieldName, Object value, Operator operator) { + this.fieldName = fieldName; + this.value = value; + this.operator = operator; } } } \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/MergeStrategy/LambdaUtils.java b/src/main/java/com/guwan/backend/MergeStrategy/LambdaUtils.java new file mode 100644 index 0000000..ad63519 --- /dev/null +++ b/src/main/java/com/guwan/backend/MergeStrategy/LambdaUtils.java @@ -0,0 +1,102 @@ +package com.guwan.backend.MergeStrategy; + +import java.io.Serializable; +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Lambda表达式和方法引用解析工具类 + */ +public class LambdaUtils { + + /** + * 从Lambda表达式或方法引用中提取属性名 + * @param getter Lambda方法引用 + * @return 属性名 + */ + public static String getPropertyName(PropertyFunction getter) { + try { + // 1. 获取SerializedLambda + Method writeReplace = getter.getClass().getDeclaredMethod("writeReplace"); + writeReplace.setAccessible(true); + SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(getter); + + // 2. 从SerializedLambda获取实现方法名 + String implMethodName = serializedLambda.getImplMethodName(); + + // 3. 转换方法名为属性名 + if (implMethodName.startsWith("get") && implMethodName.length() > 3) { + String propertyName = implMethodName.substring(3); + return propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); + } else if (implMethodName.startsWith("is") && implMethodName.length() > 2) { + String propertyName = implMethodName.substring(2); + return propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); + } + + return implMethodName; + } catch (Exception e) { + // 降级到常规正则表达式解析 + return parsePropertyNameByRegex(getter); + } + } + + /** + * 使用正则表达式解析属性名(备选方案) + */ + private static String parsePropertyNameByRegex(Function getter) { + // 尝试从Lambda toString()解析 + String methodRef = getter.toString(); + + // 处理方法引用格式: com.example.Entity::getName + if (methodRef.contains("::")) { + Pattern pattern = Pattern.compile("::get(\\w+)|::is(\\w+)"); + Matcher matcher = pattern.matcher(methodRef); + if (matcher.find()) { + String fieldName = matcher.group(1) != null ? matcher.group(1) : matcher.group(2); + return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + } + + // 直接从方法名截取 + String methodName = methodRef.substring(methodRef.lastIndexOf("::") + 2); + return extractFieldNameFromMethod(methodName); + } + // 处理Lambda表达式格式: (Entity e) -> e.getName() 或 e -> e.getName() + else if (methodRef.contains("->")) { + Pattern pattern = Pattern.compile("->\\s*\\w+\\.get(\\w+)\\(\\)|->\\s*\\w+\\.is(\\w+)\\(\\)"); + Matcher matcher = pattern.matcher(methodRef); + if (matcher.find()) { + String fieldName = matcher.group(1) != null ? matcher.group(1) : matcher.group(2); + return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + } + } + + // 处理其他情况 - 也许是已经是字段名 + return methodRef; + } + + /** + * 从方法名中提取字段名 + */ + private static String extractFieldNameFromMethod(String methodName) { + if (methodName.startsWith("get") && methodName.length() > 3) { + // getName -> name (首字母小写) + String fieldName = methodName.substring(3); + return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + } else if (methodName.startsWith("is") && methodName.length() > 2) { + // isActive -> active (首字母小写) + String fieldName = methodName.substring(2); + return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + } + return methodName; + } + + /** + * 可序列化的函数接口 - 用于支持方法引用解析 + */ + @FunctionalInterface + public interface PropertyFunction extends Function, Serializable { + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/jpaTest/EmployeeService.java b/src/main/java/com/guwan/backend/jpaTest/EmployeeService.java index ec28df8..19a4902 100644 --- a/src/main/java/com/guwan/backend/jpaTest/EmployeeService.java +++ b/src/main/java/com/guwan/backend/jpaTest/EmployeeService.java @@ -2,6 +2,7 @@ package com.guwan.backend.jpaTest; import com.guwan.backend.MergeStrategy.JoinBuilder; import com.guwan.backend.MergeStrategy.JoinBuilder.FieldMapping; +import com.guwan.backend.MergeStrategy.LambdaUtils.PropertyFunction; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.springframework.beans.factory.annotation.Autowired; @@ -40,7 +41,6 @@ public class EmployeeService { } builder.equal(Project.class, "deleted", projectDeleted); - // 使用selectAs设置字段别名,避免使用FieldMapping builder.selectAs("employeeId", Employee.class, "id"); builder.selectAs("employeeName", Employee.class, "name"); @@ -53,7 +53,7 @@ public class EmployeeService { /** * 使用自定义条件查询员工信息 - * 使用selectAs来指定字段别名对应DTO构造函数参数 + * 使用新的方法引用方式 * * @param employeeName 员工名称过滤条件,可选 * @param departmentName 部门名称过滤条件,可选 @@ -62,25 +62,61 @@ public class EmployeeService { public List findEmployees(String employeeName, String departmentName) { JoinBuilder builder = JoinBuilder.from(Employee.class); - // 添加关联,使用字符串 - builder.join(Department.class, "departmentId", "id"); - builder.join(Project.class, "projectId", "id"); - - // 添加查询条件,使用字符串 - if (employeeName != null && !employeeName.isEmpty()) { - builder.equal(Employee.class, "name", employeeName); + // 尝试使用增强的方法引用 + try { + // 添加关联,使用增强的方法引用 + builder.join(Department.class, + (PropertyFunction) Employee::getDepartmentId, + (PropertyFunction) Department::getId); + builder.join(Project.class, + (PropertyFunction) Employee::getProjectId, + (PropertyFunction) Project::getId); + + // 添加查询条件,使用增强的方法引用 + if (employeeName != null && !employeeName.isEmpty()) { + builder.equal(Employee.class, + (PropertyFunction) Employee::getName, + employeeName); + } + + if (departmentName != null && !departmentName.isEmpty()) { + builder.equal(Department.class, + (PropertyFunction) Department::getName, + departmentName); + } + + // 使用selectAs配合增强的方法引用 + builder.selectAs("employeeId", Employee.class, + (PropertyFunction) Employee::getId); + builder.selectAs("employeeName", Employee.class, + (PropertyFunction) Employee::getName); + builder.selectAs("departmentName", Department.class, + (PropertyFunction) Department::getName); + builder.selectAs("projectName", Project.class, + (PropertyFunction) Project::getName); } - - if (departmentName != null && !departmentName.isEmpty()) { - builder.equal(Department.class, "name", departmentName); + catch (Exception e) { + /* // 如果增强方法引用失败,回退到字符串方式 + builder = JoinBuilder.from(Employee.class); + + // 使用字符串作为回退方案 + builder.join(Department.class, "departmentId", "id"); + builder.join(Project.class, "projectId", "id"); + + if (employeeName != null && !employeeName.isEmpty()) { + builder.equal(Employee.class, "name", employeeName); + } + + if (departmentName != null && !departmentName.isEmpty()) { + builder.equal(Department.class, "name", departmentName); + } + + builder.selectAs("employeeId", Employee.class, "id"); + builder.selectAs("employeeName", Employee.class, "name"); + builder.selectAs("departmentName", Department.class, "name"); + builder.selectAs("projectName", Project.class, "name");*/ } - // 方式2:使用selectAs直接指定字段别名 - builder.selectAs("employeeId", Employee.class, "id"); - builder.selectAs("employeeName", Employee.class, "name"); - builder.selectAs("departmentName", Department.class, "name"); - builder.selectAs("projectName", Project.class, "name"); - // 直接执行查询并映射到DTO return builder.executeAndMap(entityManager, EmployeeDTO.class); }