当前位置: 首页 > news >正文

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

获取Spring Data自定义Repository中的实际类型

在Spring Data中,当您实现自定义Repository时,由于Java类型擦除的原因,泛型参数T在运行时确实会被擦除为Object类型。不过,有几种方法可以获取实际的类型信息。

你想在自定义的 Spring Data Neo4j Repository 接口中通过默认方法获取泛型 T 的实际类型,这个想法很自然,但遗憾的是,由于 Java 泛型在编译后的"类型擦除"机制,直接在接口的默认方法中可靠地获取 T 的实际类型(Class)是非常困难甚至不可行的。

Java 的泛型主要在编译阶段提供类型安全检查,编译后泛型类型信息(如 T)会被擦除(除非是继承自泛型父类或实现了泛型接口,且这些泛型类型已被具体化)。在你的 CustomNeo4jRepository<T, ID> 接口中,T 是一个类型参数。接口的默认方法中,无法直接获取实现类所指定的 T 的具体类型。

问题描述

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

Java 的泛型主要在编译阶段提供类型安全检查,编译后泛型类型信息(如 T)会被擦除(除非是继承自泛型父类或实现了泛型接口,且这些泛型类型已被具体化)。在你的 CustomNeo4jRepository<T, ID> 接口中,T 是一个类型参数。接口的默认方法中,无法直接获取实现类所指定的 T 的具体类型。

例如,你希望这样:

public interface CustomNeo4jRepository<T, ID> {default Class<T> getEntityType() {// 无法直接在此获取到 UserNode 等具体类型// 编译后 T 会被擦除为 Objectreturn ...; }
}

当 UserRepository 继承 CustomNeo4jRepository<UserNode, Long> 时,JVM 在运行时看到的仍然是 CustomNeo4jRepository<Object, Serializable>,无法感知到 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 包装器等。

解决方案[已成功]

  1. 添加自定义的注解@EnableNeo4jCustomRepository,这个注入用来为@EnableNeo4jRepositories注解添加默认值,避免开发人员直接干预它
  2. 为CustomNeo4jRepository接口添加注解@NoRepositoryBean,不让jpa使用代理建立实现类
  3. 为CustomNeo4jRepository类添加基类SimpleNeo4jRepository,让它有操作neo4j数据库的基本能力,它在上面去扩展个性化方法
  4. 开发人员在业务项目中,直接引用@EnableNeo4jCustomRepository注解即可
/*** 引入个人性仓储* @author lind* @date 2025/9/1 14:57* @since 1.0.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableNeo4jAuditing
@EnableNeo4jRepositories(repositoryBaseClass = CustomNeo4jRepositoryImpl.class)
public @interface EnableNeo4jCustomRepository {
}/*** 个性化接口规范* @author lind* @date 2025/9/1 14:57* @since 1.0.0*/
@NoRepositoryBean // 不让jpa使用代理建立实现类
public interface CustomNeo4jRepository<T, ID> {/*** 根据节点名称模糊查询并分页* @param namePattern 名称模式(如"%知%")* @param pageable 分页信息* @return 分页后的节点列表*/Page<T> findByNameLike(String namePattern, Pageable pageable);
}/*** 个性化接口实现* @author lind* @date 2025/9/1 14:57* @since 1.0.0*/
public class CustomNeo4jRepositoryImpl<T, ID> extends SimpleNeo4jRepository<T,ID>implements CustomNeo4jRepository<T, ID> {private final Neo4jOperations neo4jOperations;Neo4jEntityInformation<T, ID> entityInformation;Class<T> domainType;protected CustomNeo4jRepositoryImpl(Neo4jOperations neo4jOperations,Neo4jEntityInformation<T, ID> entityInformation) {super(neo4jOperations,entityInformation);this.neo4jOperations = neo4jOperations;this.entityInformation = entityInformation;this.domainType = entityInformation.getJavaType();}@Overridepublic Page<T> findByNameLike(String namePattern, Pageable pageable) {String cypherQuery = "MATCH (n:" + domainType.getSimpleName() +") WHERE n.userName CONTAINS $name RETURN n ORDER BY n.userName SKIP $skip LIMIT $limit";Map<String, Object> parameters = new HashMap<>();parameters.put("name", namePattern.replace("%", ""));parameters.put("skip", pageable.getOffset());parameters.put("limit", pageable.getPageSize());List<T> results = neo4jOperations.findAll(cypherQuery, parameters, domainType);// 获取总数用于分页String countQuery = "MATCH (n:" + domainType.getSimpleName() + ") WHERE n.name CONTAINS $name RETURN COUNT(n)";Long total = neo4jOperations.count(countQuery, parameters);return new PageImpl<>(results, pageable, total);}
}@SpringBootApplication
@EnableNeo4jAuditing
@EnableNeo4jCustomRepository
public class NeoApp {public static void main(String[] args) {SpringApplication.run(NeoApp.class, args);}
}
http://www.agseo.cn/news/173/

相关文章:

  • Agisoft Metashape Professional 2.2.2.21069 多视点三维建模设计
  • Linux之进程状态
  • 2. O(NlogN)的排序
  • 【Python】使用matplotlib绘图,显示中文字符。
  • React-手写支持多文件、并行上传、串行上传、分片上传、单文件上传、失败自动重试、自动上传/手动按钮上传切换
  • Linux服务器中代码仓库(gitea+drone)搭建
  • 二分查找
  • postcss-px-to-viewport-8-plugin无法转换tailwindcss样式问题
  • html中的latex数据公式展示
  • IK Multimedia TONEX MAX 1.10.2 逼真音色建模
  • 重塑云上 AI 应用“运行时”,函数计算进化之路
  • 82、SpringMVC 参数传递,浏览器和服务器之间的数据传输
  • 问卷调查数据库设计
  • Linux 系统调用详解与工作机制
  • 一客一策:Data Agent 如何重构大模型时代的智能营销?
  • MySQL函数
  • The 2025 Sichuan Provincial Collegiate Programming Contest
  • 详细介绍:Android 热点开发的相关api总结
  • 工业主板:工业自动化与智能设备的强大心脏
  • 十大经典排序算法 - lucky
  • 深度学习入门基于python
  • 2025网络赛1 C、D
  • 图像配准尝试
  • TypeScript索引访问类型详解
  • 【URP】Unity Shader Tags
  • 存储器的性能指标 计算机组成原理第三章
  • 基于Operator方式和二进制方式部署prometheus环境
  • 安全不是一个功能-而是一个地基
  • 你的错误处理一团糟-是时候修复它了-️
  • idea gitee 更新已取消 解决方案