Spring Data CrudRepository的findAll()未返回继承派生实体问题
问题场景
我搭建了如下Spring应用,定义了名为Product的基础实体,通过继承派生出两个子实体(Product > Bike、Product > Wheel),各层代码如下:
控制器层代码
public abstract class ProductController<T extends Product> { @Autowired private ProductService<T> productService; @PostMapping public ResponseEntity post(@RequestBody T product) { return ResponseEntity.ok(productService.save(product)); } @GetMapping public ResponseEntity get() { return ResponseEntity.ok(productService.findAll()); } } @RestController @RequestMapping("/products/categories/bikes") public class BikeController extends ProductController<Bike> {} @RestController @RequestMapping("/products/categories/wheels") public class WheelController extends ProductController<Wheel> {}
实体类定义
@Entity @Inheritance(strategy = InheritanceType.JOINED) public abstract class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; public Product() {} //getters and setters } @Entity public class Bike extends Product { private String color; public Bike() {} //getters and setters } @Entity public class Wheel extends Product { private Double diameter; public Wheel() {} //getters and setters }
持久层Repository定义
public interface ProductRepository<T extends Product> extends CrudRepository<T, Long> {}
业务层Service定义
@Service public class ProductService<T extends Product> { @Autowired private ProductRepository<T> productRepository; public T save(T product) { return productRepository.save(product); } public Iterable<T> findAll() { return productRepository.findAll(); } }
异常现象
应用启动后会为三个实体分别生成对应的数据表,调用具体子类控制器的post方法时,可正常将对应实体写入对应的数据表中。但调用findAll()方法时,返回的实体并非来自具体子表,而是来自父类Product表。即便已在Repository中指定了对应的实体类型,该问题仍然存在。
问题产生原因
- 核心冲突来自Java泛型的类型擦除机制和Spring默认单例Bean的管理逻辑。你直接在泛型类
ProductService<T>上加了@Service注解,Spring容器只会为这个类创建一个全局唯一的单例Bean,不会为Bike、Wheel两个子类分别生成专属的Service实例。 - 运行时泛型参数
T会被擦除到上界类型Product,因此这个单例Service中自动注入的ProductRepository<T>,最终会被Spring绑定到根实体Product对应的Repository实现,和子类Controller上声明的具体泛型类型没有关联。 post接口能正常写入子表,是因为传入的请求体反序列化后本身就是具体子类的实例,Hibernate可以根据对象的实际运行类型判断要写入哪张子表;但findAll()方法没有传入任何实体参数,Repository只能按照自身绑定的根实体类型Product生成查询SQL,JOINED继承策略下查询根实体默认不会关联对应的子表,自然只能查出父表存储的基础数据,无法映射为具体子类的实例。- 额外的配置问题是你只定义了一个通用的泛型
ProductRepository接口,没有为Bike、Wheel单独声明继承自ProductRepository的子接口,Spring Data JPA本身也不会自动为每个子实体生成专属的Repository实例。
内容的提问来源于stack exchange,提问作者kastrbl4nik
相关产品推荐
相关产品推荐

