找回密码
 立即注册
首页 业界区 安全 springboot~SpringData自定义Repository的正确方式 ...

springboot~SpringData自定义Repository的正确方式

梅克 2025-10-1 13:09:07
获取Spring Data自定义Repository中的实际类型

在Spring Data中,当您实现自定义Repository时,由于Java类型擦除的原因,泛型参数T在运行时确实会被擦除为Object类型。不过,有几种方法可以获取实际的类型信息。
你想在自定义的 Spring Data Neo4j Repository 接口中通过默认方法获取泛型 T 的实际类型,这个想法很自然,但遗憾的是,由于 Java 泛型在编译后的"类型擦除"机制,直接在接口的默认方法中可靠地获取 T 的实际类型(Class)是非常困难甚至不可行的。
Java 的泛型主要在编译阶段提供类型安全检查,编译后泛型类型信息(如 T)会被擦除(除非是继承自泛型父类或实现了泛型接口,且这些泛型类型已被具体化)。在你的 CustomNeo4jRepository 接口中,T 是一个类型参数。接口的默认方法中,无法直接获取实现类所指定的 T 的具体类型。
问题描述

直接获取泛型实际类型的挑战

Java 的泛型主要在编译阶段提供类型安全检查,编译后泛型类型信息(如 T)会被擦除(除非是继承自泛型父类或实现了泛型接口,且这些泛型类型已被具体化)。在你的 CustomNeo4jRepository 接口中,T 是一个类型参数。接口的默认方法中,无法直接获取实现类所指定的 T 的具体类型。
例如,你希望这样:
  1. public interface CustomNeo4jRepository<T, ID> {
  2.    
  3.     default Class<T> getEntityType() {
  4.         // 无法直接在此获取到 UserNode 等具体类型
  5.         // 编译后 T 会被擦除为 Object
  6.         return ...;
  7.     }
  8. }
复制代码
当 UserRepository 继承 CustomNeo4jRepository 时,JVM 在运行时看到的仍然是 CustomNeo4jRepository,无法感知到 UserNode。
即使尝试通过反射获取泛型信息(如 getClass().getGenericInterfaces()),其结果也取决于接口是如何被继承和代理的。Spring Data 通常会为 Repository 接口创建代理对象,这使得通过反射获取到的泛型信息很可能是 T 本身(一个 TypeVariable),而非具体的 UserNode 类型,因此难以直接转换为 Class。
问题回顾(为什么难)


  • Java 泛型在运行时会被擦除(type erasure),直接用 T 在运行时无法得到 Class。
  • Spring Data 在创建 Repository 时会用生成的实现类 / 代理类,进一步增加了通过 getClass() 找泛型信息的不稳定性。
先要明确一点


  • 你提到没有 Neo4jRepositoryFactory,且 Neo4jRepositoryFactoryBean 是 final,这说明你在用的是较新的 SDN 版本(例如 SDN6系列或更高),API 与老版本不同。基于此,不建议尝试继承 factory bean 或直接替换内部工厂,而应采用官方推荐的扩展点:repository fragments、repository base class(如果支持)或 AOP/Service 包装器等。
解决方案[已成功]


  • 添加自定义的注解@EnableNeo4jCustomRepository,这个注入用来为@EnableNeo4jRepositories注解添加默认值,避免开发人员直接干预它
  • 为CustomNeo4jRepository接口添加注解@NoRepositoryBean,不让jpa使用代理建立实现类
  • 为CustomNeo4jRepository类添加基类SimpleNeo4jRepository,让它有操作neo4j数据库的基本能力,它在上面去扩展个性化方法
  • 开发人员在业务项目中,直接引用@EnableNeo4jCustomRepository注解即可
  1. /**
  2. * 引入个人性仓储
  3. * @author lind
  4. * @date 2025/9/1 14:57
  5. * @since 1.0.0
  6. */
  7. @Target(ElementType.TYPE)
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @EnableNeo4jAuditing
  10. @EnableNeo4jRepositories(repositoryBaseClass = CustomNeo4jRepositoryImpl.class)
  11. public @interface EnableNeo4jCustomRepository {
  12. }
  13. /**
  14. * 个性化接口规范
  15. * @author lind
  16. * @date 2025/9/1 14:57
  17. * @since 1.0.0
  18. */
  19. @NoRepositoryBean // 不让jpa使用代理建立实现类
  20. public interface CustomNeo4jRepository<T, ID> {
  21.     /**
  22.      * 根据节点名称模糊查询并分页
  23.      * @param namePattern 名称模式(如"%知%")
  24.      * @param pageable 分页信息
  25.      * @return 分页后的节点列表
  26.      */
  27.     Page<T> findByNameLike(String namePattern, Pageable pageable);
  28. }
  29. /**
  30. * 个性化接口实现
  31. * @author lind
  32. * @date 2025/9/1 14:57
  33. * @since 1.0.0
  34. */
  35. public class CustomNeo4jRepositoryImpl<T, ID> extends SimpleNeo4jRepository<T,ID>
  36.         implements CustomNeo4jRepository<T, ID> {
  37.     private final Neo4jOperations neo4jOperations;
  38.     Neo4jEntityInformation<T, ID> entityInformation;
  39.     Class<T> domainType;
  40.     protected CustomNeo4jRepositoryImpl(Neo4jOperations neo4jOperations,
  41.                                         Neo4jEntityInformation<T, ID> entityInformation) {
  42.         super(neo4jOperations,entityInformation);
  43.         this.neo4jOperations = neo4jOperations;
  44.         this.entityInformation = entityInformation;
  45.         this.domainType = entityInformation.getJavaType();
  46.     }
  47.     @Override
  48.     public Page<T> findByNameLike(String namePattern, Pageable pageable) {
  49.         String cypherQuery = "MATCH (n:" + domainType.getSimpleName() +
  50.                 ") WHERE n.userName CONTAINS $name RETURN n ORDER BY n.userName SKIP $skip LIMIT $limit";
  51.         Map<String, Object> parameters = new HashMap<>();
  52.         parameters.put("name", namePattern.replace("%", ""));
  53.         parameters.put("skip", pageable.getOffset());
  54.         parameters.put("limit", pageable.getPageSize());
  55.         List<T> results = neo4jOperations.findAll(cypherQuery, parameters, domainType);
  56.         // 获取总数用于分页
  57.         String countQuery = "MATCH (n:" + domainType.getSimpleName() + ") WHERE n.name CONTAINS $name RETURN COUNT(n)";
  58.         Long total = neo4jOperations.count(countQuery, parameters);
  59.         return new PageImpl<>(results, pageable, total);
  60.     }
  61. }
  62. @SpringBootApplication
  63. @EnableNeo4jAuditing
  64. @EnableNeo4jCustomRepository
  65. public class NeoApp {
  66.         public static void main(String[] args) {
  67.                 SpringApplication.run(NeoApp.class, args);
  68.         }
  69. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册