Spring Boot中JPA实体设为抽象类遵循哪些设计原则与模式?
问题背景
我接手了一个基于Spring Boot的软件,其中每个实体至少对应两种数据类型。示例如下:
DTO:
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class MyEntityDTO { Long id; String name; String description; String type; Long parentId; String parentSerialNumber; }
对应的JPA实体(表):
@DiscriminatorColumn(name = "type") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "my_entities_table") @Entity @NoArgsConstructor @Data @SuperBuilder @FieldDefaults(level = AccessLevel.PRIVATE) public abstract class MyEntity extends IdBasedEntity { @ManyToOne(fetch = FetchType.EAGER) @JsonIgnore Parent parent; @Column(name = "type", insertable = false, updatable = false, nullable = false) String type; @Column(nullable = false) String externalId; @Column(nullable = false) String myentityName; @Column String description; }
我的问题是:将JPA实体设为抽象类符合哪些设计原则和/或设计模式?我对此感到困惑,因为我发现无法在JUnit中Mock该实体,我认为这样设计必然存在合理的原因。
解答
一、符合的设计模式
- 单表继承模式:这是JPA官方支持的继承映射方案,抽象类
MyEntity作为所有具体实体的基类,定义共享字段,通过type字段区分不同子类实体,将所有类型的数据存储在同一张表中。抽象类的核心作用是避免直接实例化——它本身不对应具体业务实体,只是子类的通用模板。 - 模板方法模式:当前代码虽未定义抽象方法,但抽象类的结构预留了扩展空间。后续可在
MyEntity中加入通用业务逻辑模板(比如统一的校验、转换逻辑),由子类实现特定行为细节,完全契合模板方法的设计思路。
二、遵循的设计原则
- 单一职责原则:抽象父类只封装所有实体共有的属性与逻辑,子类专注于各自特有的业务属性和行为,每个类的职责边界清晰明确。
- 开闭原则:新增实体类型时,只需创建对应子类并实现特有逻辑,无需修改抽象父类和现有代码,满足对扩展开放、对修改关闭的要求。
- 里氏替换原则:所有子类都能无缝替换抽象父类的位置使用,比如Repository可将
MyEntity作为泛型类型,统一处理所有子类实体的CRUD操作。 - 依赖倒置原则:上层业务代码可依赖抽象的
MyEntity而非具体子类,降低模块间耦合度,例如业务层处理实体时,无需关心具体是哪种类型的MyEntity。
三、关于JUnit Mock的问题
抽象类完全可以被Mock,比如使用Mockito时,直接通过Mockito.mock(MyEntity.class)就能生成代理对象。如果遇到Mock失败,大概率是以下原因:
- 抽象类中的方法被标记为
final:示例中@Data生成的方法默认不是final,但如果手动给方法加了final修饰,Mockito无法Mock final方法,需要配合Mockito-inline扩展解决。 - Mock方式错误:如果是Spring环境下的测试,可使用
@MockBean注解Mock抽象实体;普通JUnit测试要确保引入了正确的Mockito依赖且版本兼容。
内容的提问来源于stack exchange,提问作者user22592482
相关产品推荐
相关产品推荐

