Spring Boot接口LEFT JOIN查询仅返回单条记录问题排查
我来帮你拆解下这个LEFT JOIN只返回1条记录的问题,核心原因其实出在实体类和查询结果的映射不匹配上,咱们一步步来看:
为什么会只返回1条记录?
你用原生SQL写了SELECT * FROM model as m LEFT JOIN role_model as rm on(m.m_id =rm.m_id),这条SQL本身是没问题的,数据库层面确实会返回3条记录,但你的Repository方法返回的是List<Model>——而Model实体只对应model表的字段,查询结果里却包含了role_model的所有字段。
这里有两个关键冲突点:
- 两张表都有
name字段,SELECT *会返回两个name列,JPA在映射到Model实体时,会用后面的role_model.name覆盖前面的model.name; - 对于未匹配到
role_model的记录,role_model的所有字段都是NULL,JPA无法把这些额外的NULL字段映射到Model实体中,导致映射异常,最终只保留了能正确匹配(有role_model数据)的那条记录。
另外,你的Controller层返回类型是String,直接返回List<Model>会被Spring默认转换成字符串,这也可能导致显示上的误导,但核心问题还是Repository的映射逻辑。
怎么修改才能得到预期的3条记录?
根据你的业务需求,我给你三个可行的方案,你可以按需选择:
方案一:用DTO接收联合查询结果(最推荐)
如果需要同时获取model和role_model的字段,最好的方式是创建一个专门的DTO(数据传输对象)来接收联合查询的结果,避免实体类和查询结果的冲突。
- 先创建DTO类:
// 包含两张表的所需字段,注意字段名尽量避免冲突 public class ModelRoleDTO { private Integer mId; private String modelName; private Integer roleModelId; private String roleModelName; private Integer status; // 构造方法的参数顺序必须和SQL查询的字段顺序完全一致 public ModelRoleDTO(Integer mId, String modelName, Integer roleModelId, String roleModelName, Integer status) { this.mId = mId; this.modelName = modelName; this.roleModelId = roleModelId; this.roleModelName = roleModelName; this.status = status; } // 别忘了添加getter方法,方便后续序列化返回 public Integer getmId() { return mId; } public String getModelName() { return modelName; } // 其他字段的getter同理 }
- 修改Repository的查询方法,明确指定查询字段(避免同名冲突)并返回DTO:
@Query(value = "SELECT m.m_id, m.name, rm.id, rm.name, rm.status " + "FROM model as m LEFT JOIN role_model as rm ON m.m_id = rm.m_id", nativeQuery = true) List<ModelRoleDTO> listJoined();
- 最后修复Controller的返回类型,返回列表而不是字符串:
@GetMapping("/test") public List<ModelRoleDTO> test(){ return service.listJoined(); }
方案二:配置实体类的关联关系(适合需要使用实体类的场景)
如果你希望继续使用Model和RoleModel实体类,可以通过JPA的关联注解来处理LEFT JOIN的映射。
- 在
Model实体中添加关联关系(假设是一对一关系,根据你的业务调整):
@Entity @Table(name = "model") public class Model { @Id @Column(name = "m_id") private Integer mId; private String name; // 关联RoleModel,fetch类型按需选择 @OneToOne(mappedBy = "model", fetch = FetchType.EAGER) private RoleModel roleModel; // getter和setter }
- 在
RoleModel实体中添加反向关联:
@Entity @Table(name = "role_model") public class RoleModel { @Id private Integer id; @Column(name = "m_id") private Integer mId; private String name; private Integer status; // 关联Model @OneToOne @JoinColumn(name = "m_id", insertable = false, updatable = false) private Model model; // getter和setter }
- 修改Repository的查询方法为JPQL(不需要原生SQL):
@Query("SELECT m FROM Model m LEFT JOIN FETCH m.roleModel") List<Model> listJoined();
这样查询出来的Model列表会包含所有3条记录,未匹配的roleModel属性会是NULL。
方案三:只查询model表的字段(适合不需要role_model字段的场景)
如果你不需要role_model的字段,只是想确认LEFT JOIN的结果数量,可以修改原生SQL,只查询model表的字段:
@Query(value = "SELECT m.* FROM model as m LEFT JOIN role_model as rm ON m.m_id = rm.m_id", nativeQuery = true) List<Model> listJoined();
这样JPA可以正确映射所有3条model记录,因为查询结果只有model的字段,没有额外的冲突字段。
最后验证
不管用哪个方案,修改后启动项目调用/test接口,应该就能得到你预期的3条记录了。
内容的提问来源于stack exchange,提问作者SAR

