如何创建支持两表联查取数的Entity类与Repository
两表联查场景的Entity与Repository实现方案
你当前执行的是city和countrylanguage基于CountryCode字段的内连接查询,以下是Spring Data JPA栈下的可落地实现方式:
1. 编写单表映射实体类
两个表没有直接的外键主从依赖,均通过CountryCode关联国家维度表,先分别编写和表结构一一对应的实体:
- 城市表实体
City
import jakarta.persistence.*; @Entity @Table(name = "city") public class City { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "Name") private String name; @Column(name = "CountryCode") private String countryCode; @Column(name = "District") private String district; @Column(name = "Population") private Integer population; // 空参构造、全参构造、getter、setter直接用IDE生成即可 public City() {} }
- 国家语言表实体
CountryLanguagecountrylanguage以CountryCode+Language为联合主键,用内嵌主键实现映射:
import jakarta.persistence.*; import java.io.Serializable; // 联合主键类 @Embeddable public class CountryLanguageId implements Serializable { @Column(name = "CountryCode") private String countryCode; @Column(name = "Language") private String language; // 必须实现空参构造、equals、hashCode方法,直接用IDE生成即可 public CountryLanguageId() {} } @Entity @Table(name = "countrylanguage") public class CountryLanguage { @EmbeddedId private CountryLanguageId id; @Column(name = "IsOfficial") private String isOfficial; @Column(name = "Percentage") private Float percentage; // 空参构造、get/set用IDE生成即可 public CountryLanguage() {} }
2. 定义联查结果接收对象
你写的SQL返回两表全量字段,单表实体无法承接全量数据,编写VO类做结果映射:
public class CityWithLanguageVO { // 城市表字段 private Integer cityId; private String cityName; private String countryCode; private String district; private Integer cityPopulation; // 国家语言表字段 private String language; private String isOfficial; private Float languagePercentage; // 全参构造必须保留,参数顺序要和SQL查询返回的字段顺序严格一致 public CityWithLanguageVO(Integer cityId, String cityName, String countryCode, String district, Integer cityPopulation, String language, String isOfficial, Float languagePercentage) { this.cityId = cityId; this.cityName = cityName; this.countryCode = countryCode; this.district = district; this.cityPopulation = cityPopulation; this.language = language; this.isOfficial = isOfficial; this.languagePercentage = languagePercentage; } // getter、setter用IDE生成即可 }
3. 实现Repository数据访问层
先编写两个单表的基础Repository,承接单表增删改查能力:
import org.springframework.data.jpa.repository.JpaRepository; public interface CityRepository extends JpaRepository<City, Integer> { // 自定义联查方法写在这里 } public interface CountryLanguageRepository extends JpaRepository<CountryLanguage, CountryLanguageId> { }
在CityRepository中添加自定义联查方法,绑定你写的查询逻辑:
import org.springframework.data.jpa.repository.Query; import java.util.List; public interface CityRepository extends JpaRepository<City, Integer> { @Query(value = "select city.ID, city.Name, city.CountryCode, city.District, city.Population, countrylanguage.Language, countrylanguage.IsOfficial, countrylanguage.Percentage from city join countrylanguage on city.CountryCode=countrylanguage.CountryCode order by city.ID", nativeQuery = true) List<CityWithLanguageVO> listCityWithLanguage(); }
注意:不要直接在自定义查询里写
select *,两个表存在CountryCode同名字段,会出现字段重名冲突;手动指定查询字段、和VO构造器参数顺序对齐,可以完全避免映射错位、字段丢失问题。如果不想写VO,也可以用List<Map<String,Object>>作为返回值,但是类型不安全,正式项目不推荐。
如果要做对象模型层面的关联查询,也可以在City实体中通过countryCode字段配置@OneToMany关联CountryLanguage,但因为两个表没有直接指向对方主键的外键,这种配置很容易出现懒加载失效、结果笛卡尔积问题,直接写原生SQL映射VO是最稳妥的实现方式。
内容的提问来源于stack exchange,提问作者Rooban k
相关产品推荐
相关产品推荐

