定制DTO和重复

时间:2015-11-15 13:15:41

标签: hibernate jpa

我在country dababase中有两个表citymysql,并在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;

2 个答案:

答案 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。