我在country
dababase中有两个表city
和mysql
,并在One-to-Many
中映射为hibenrate/jpa
关系。
我这样做一个查询
select new MyDTO(country.id,country.name, city.id, city.name) from country
inner join city on city.fk_country = country.id
where .....
我使用自定义DTO
的原因是该对象城市包含其他字段(城市属性),我不想检索它们,我只想要Id和名称。
问题是我的结果中有重复的国家
1,france,1,paris
1,france,2,marseille
1,france,3,lion
....
所以我怎么能像hibernate一样将城市放入集合或其他东西。
我没有使用hibernate映射(实体Country
)因为hibernate / jpa我们无法指定要在集合中检索哪个字段。
我的国家/地区实体
@Entity
@Table(name = "country", catalog = "ao")
public class Country implements java.io.Serializable {
private Integer id;
private String name;
private String .... ;
private Integer .... ;
private Boolean .... ;
....
....
private Set<City> cities = new HashSet<City>(0);
我的城市实体
@Entity
@Table(name = "city", catalog = "ao")
public class City implements java.io.Serializable {
private Integer id;
private String name;
//other properties
private String ...;
private String ...;
private Long ...;
...
...
...
private Country country;
答案 0 :(得分:1)
您提供的JPA查询在功能上等同于以下SQL查询:
SELECT
country.id
, country.name
, city.id
, city.name
FROM
country
INNER JOIN
city
ON country.id = city.country_id;
使用INNER JOIN
时,表的顺序无关紧要,因此此查询与
SELECT
country.id
, country.name
, city.id
, city.name
FROM
city
INNER JOIN
country
ON city.country_id = country.id;
此查询转换为以自然语言
的以下查询为我提供数据库中所有城市的标识符和名称,以及各自国家/地区的标识符和名称。
鉴于此查询以及SELECT
查询的输出是元组集合的事实,您将获得预期结果,并为同一国家/地区的每个城市重复国家标识符和名称。所以,结果并不令人惊讶。
另一方面,您所寻找的是层次结构:
- Country 1
- City 1.1
- City 1.2
- City 1.3
- Country 2
- City 2.1
- City 2.2
- City 2.3
...
由于单个SELECT
查询的输出是一个表,因此您不应期望从单个查询中获取此分层输出。
您可以通过将City
表映射两次来实现您想要的效果。一个例子如下:
@Entity @Table(name = "city") class City { Long id; String name; @ManyToOne Country country; // Lots of other attributes }
@Entity @Table(name = "city") class CitySummary { Long id; String name; @ManyToOne Country country; }
@Entity @Table(name = "country")
class Country {
@OneToMany
private Set<City> cities;
@OneToMany
private Set<City> citySummaries;
}
通过此操作,您应该可以致电country.getCitySummaries()
以获取映射到某个国家/地区的城市的汇总数据。或者,您可以调用citySummaryRepository.findAll()
一次性加载所有CitySummary
个对象。然后,拨打citySummary.getCountry()
即可获取国家/地区信息。每个国家/地区只会触发一次额外的数据库查询。如果这也让您担心,请在@Fetch(FetchMode.JOIN)
属性上放置特定于Hibernate的Country country;
注释以生成连接查询。
Github上的示例代码。
答案 1 :(得分:1)
与@mohit
中的ansnwer一样为国家/地区和城市创建其他课程。将平面结构转换为国家和城市的嵌套结构,如下所示:
public class Country {
Integer idLvl1;
String nameLvl1;
public Country(Integer idLvl1, String nameLvl1) {
}
List<City> cities;
}
public class City {
Integer idLvl2;
String nameLvl2;
public City(Integer idLvl2, String nameLvl2) {
}
}
public class MyDTOConverter {
public static Collection<Country> covert(List<MyDTO> dtos){
Map<Integer, Country> countries = new LinkedHashMap<Integer, Country>();
for (MyDTO myDTO : dtos) {
//First adding the country if it doesn't exist
if (!countries.containsKey(myDTO.idLvl1)){
countries.put(myDTO.idLvl1, new Country(myDTO.idLvl1, myDTO.nameLvl1));
}
//Adding city in the existing country.
countries.get(myDTO.idLvl1).cities.add(new City(myDTO.idLvl2, myDTO.nameLvl2));
}
return countries.values();
}
}
最终的国家集合将产生所需的JSON。