Spring JPA:如何通过查询填充继承实体的非列关联成员?
咱们先把问题的核心理清楚:Action是@MappedSuperclass(不是实体类),所以没法直接用常规的JPA关联注解绑定action_params表;你之前加的@Transient会让JPA直接忽略这个字段,@JoinFormula的写法也不符合规范,自然拿不到数据。另外在实体类里@Autowired仓库本身就违背了JPA实体的设计原则,实体不该依赖Spring Bean。
下面给你几个实用的解决思路:
方案1:用实体生命周期事件+Service手动填充
这个方法很直接,等实体从数据库加载出来后,手动调用Repository查询对应的参数列表填进去:
- 先确保你有
ActionParamsRepository,里面加个根据action_id查参数的方法:
public interface ActionParamsRepository extends JpaRepository<ActionParams, String> { List<ActionParams> findByActionId(String actionId); }
- 然后在业务Service里处理查询和填充逻辑,比如针对
Chemistry的Service:
@Service public class ChemistryService { @Autowired private ChemistryRepository chemistryRepo; @Autowired private ActionParamsRepository paramsRepo; public Chemistry getChemistryById(String id) { Chemistry chemistry = chemistryRepo.findById(id).orElse(null); if (chemistry != null) { // 查对应的参数列表并赋值 chemistry.setActionParamsList(paramsRepo.findByActionId(id)); } return chemistry; } }
要是想让所有Action子类都复用这个逻辑,可以写个通用的父Service:
public abstract class ActionService<T extends Action> { @Autowired private ActionParamsRepository paramsRepo; // 让子类实现自己的Repository protected abstract JpaRepository<T, String> getRepository(); public T getById(String id) { T action = getRepository().findById(id).orElse(null); if (action != null) { action.setActionParamsList(paramsRepo.findByActionId(id)); } return action; } }
然后ChemistryService继承它就行:
@Service public class ChemistryService extends ActionService<Chemistry> { @Autowired private ChemistryRepository chemistryRepo; @Override protected JpaRepository<Chemistry, String> getRepository() { return chemistryRepo; } }
方案2:在子类实体中定义关联(最推荐)
因为Action是@MappedSuperclass,子类才是真正的@Entity,所以可以在父类定义抽象的参数列表方法,然后在每个子类里用JPA的关联注解实现:
先改父类Action:
@MappedSuperclass public abstract class Action { // 你的其他字段... // 定义抽象方法让子类实现 public abstract List<ActionParams> getActionParamsList(); public abstract void setActionParamsList(List<ActionParams> params); }
然后在Chemistry子类里实现关联:
@Entity(name="chemistry") public class Chemistry extends Action implements Serializable { // 你的其他字段... @OneToMany @JoinColumn(name = "action_id", referencedColumnName = "id") private List<ActionParams> actionParamsList; @Override public List<ActionParams> getActionParamsList() { return actionParamsList; } @Override public void setActionParamsList(List<ActionParams> actionParamsList) { this.actionParamsList = actionParamsList; } }
这样JPA会自动帮你关联查询,只要action_params表的action_id字段对应子类实体的id就行。这个方案完全符合JPA的设计规范,不用手动写查询逻辑,最省心。
方案3:自定义Repository查询,用DTO返回结果
要是不想修改实体类,可以在Repository里写自定义查询,直接返回包含参数列表的DTO:
先建个DTO类,用来封装结果:
public class ActionWithParamsDTO { private Action action; private List<ActionParams> params; // 构造函数要和查询里的字段对应 public ActionWithParamsDTO(Action action, List<ActionParams> params) { this.action = action; this.params = params; } // Getter和Setter自己补上 }
然后在ActionRepository里加自定义查询:
@NoRepositoryBean public interface ActionRepository<T extends Action> extends JpaRepository<T, String> { @Query("SELECT new com.yourpackage.ActionWithParamsDTO(a, p) FROM #{#entityName} a LEFT JOIN action_params p ON a.id = p.actionId WHERE a.id = :id") ActionWithParamsDTO findActionWithParamsById(@Param("id") String id); }
这样调用chemistryRepo.findActionWithParamsById(id)就能直接拿到包含参数的结果了。
内容的提问来源于stack exchange,提问作者Maya

