Java 如何在JPA中用一个类实现循环依赖

Java 如何在JPA中用一个类实现循环依赖,java,jpa,spring-data-jpa,metamodel,cyclic,Java,Jpa,Spring Data Jpa,Metamodel,Cyclic,我在java/spring中遇到以下问题 我试图用一个更具体的案例最大限度地简化我的问题。 总而言之,我们有一个阶级国家,每个国家都与许多城市相连。每个城市都通过一条中级公路与另一个城市相连 我以这种方式实现了它(请参见下文),但这并不能让我满意,因为我们在Road对象中有一个冗余,它通过城市和国家属性(例如,假设我们不关心重复项,即Road Paris Lyon和Road Lyon Paris)链接到国家 再精确一点,我们没有跨国公司。全国所有的道路和城市都属于同一个国家 公共类国家/地区实

我在java/spring中遇到以下问题

我试图用一个更具体的案例最大限度地简化我的问题。 总而言之,我们有一个阶级国家,每个国家都与许多城市相连。每个城市都通过一条中级公路与另一个城市相连

我以这种方式实现了它(请参见下文),但这并不能让我满意,因为我们在Road对象中有一个冗余,它通过城市和国家属性(例如,假设我们不关心重复项,即Road Paris Lyon和Road Lyon Paris)链接到国家

再精确一点,我们没有跨国公司。全国所有的道路和城市都属于同一个国家

公共类国家/地区实现可序列化{
...
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy=“country”)
私人名单城市;
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy=“country”)
私人道路;
}
公共类城市实现了可序列化{
...
@多通(可选=假)
@JoinColumn(name=“countryId”,referencedColumnName=“id”)
私人国家;
}
公共类道路实现可序列化{
//由相应的3项countryId、cityFromId和cityToId确定的唯一性
...
@多通(可选=假)
@JoinColumn(name=“countryId”,referencedColumnName=“id”)
私人国家;
@多通(可选=假)
@JoinColumn(name=“cityFromId”,referencedColumnName=“id”)
私人城市从;
@多通(可选=假)
@JoinColumn(name=“cityToId”,referencedColumnName=“id”)
私人城市城市;
}

为了避免重复引用,我们可以想象在Road类中去掉对Country类的引用,但这意味着在class City中添加@OneToMany或Road的引用。由于道路连接2个城市(见下文),因此我们将采用循环参考

公共类国家/地区实现可序列化{
...
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy=“country”)
私人名单城市;
}
公共类城市实现了可序列化{
...
@多通(可选=假)
@JoinColumn(name=“countryId”,referencedColumnName=“id”)
私人国家;
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy=“cityFrom”)
私人名单城市福禄路;
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy=“cityTo”)
私人列表城市路;
}
公共类道路实现可序列化{
//由相应的3项countryId、cityFromId和cityToId确定的唯一性
...
@多通(可选=假)
@JoinColumn(name=“cityFromId”,referencedColumnName=“id”)
私人城市从;
@多通(可选=假)
@JoinColumn(name=“cityToId”,referencedColumnName=“id”)
私人城市城市;
}

你能帮我找到一个更好的方法来制作我的模型吗


非常感谢

我认为您对实际模型的概念不太清楚。道路应该是一个独立的实体,而不仅仅是两个城市之间的连接。例如,一条道路可以穿过许多城市。此外,一个城市可以有许多道路。你所描述的是城市可以连接到许多城市

但你想描述的是道路和城市之间存在着一种manytomy关系

一个只有单向映射的简单JPA模型如下所示

@Entity
public class Country {
    @Id
    private String name;

@Entity
public class City {
    @Id
    private String name;    
    @ManyToOne
    private Country country;
    @ManyToMany
    private Set<Road> roads;

@Entity
public class Road {
    @Id
    private String name;    

我认为你对实际模型的概念不太清楚。道路应该是一个独立的实体,而不仅仅是两个城市之间的连接。例如,一条道路可以穿过许多城市。此外,一个城市可以有许多道路。你所描述的是城市可以连接到许多城市

但你想描述的是道路和城市之间存在着一种manytomy关系

一个只有单向映射的简单JPA模型如下所示

@Entity
public class Country {
    @Id
    private String name;

@Entity
public class City {
    @Id
    private String name;    
    @ManyToOne
    private Country country;
    @ManyToMany
    private Set<Road> roads;

@Entity
public class Road {
    @Id
    private String name;    

你好,你介意用英语写吗?对不起,我忘了。我翻译!但它并没有保留我留言开头的“你好”…没有什么建议。(1) 一条路能从一个国家开始,到另一个国家结束吗?如果是,您的第一款车型无法处理此类情况。(2) 您不需要总是在两个方向上映射“关系”。一些方向,特别是一些
one-to-many
很少以面向对象的方式使用。只需映射“多对一”的一面就足够了。如果您需要获取这些“多”实体,您可以使用query来获取它们,而不是获取
one
,然后导航到这些“多”实体以获取它们。您好。我更新了我的车票,使之精确地表明道路和城市属于同一个国家。感谢(2)的提示,我将尝试此实现!你好,你介意用英语写吗?对不起,我忘了。我翻译!但它并没有保留我留言开头的“你好”…没有什么建议。(1) 一条路能从一个国家开始,到另一个国家结束吗?如果是,您的第一款车型无法处理此类情况。(2) 您不需要总是在两个方向上映射“关系”。一些方向,特别是一些
one-to-many
很少以面向对象的方式使用。只需映射“多对一”的一面就足够了。如果您需要获取这些“多”实体,您可以使用query来获取它们,而不是获取
one
,然后导航到这些“多”实体以获取它们。您好。我更新了我的车票,使之精确地表明道路和城市属于同一个国家。感谢(2)的提示,我将尝试此实现!
@Entity
public class Country {
    @Id
    private String name;

@Entity
public class City {
    @Id
    private String name;    
    @ManyToOne
    private Country country;
    @ManyToMany
    private Set<Road> roads;

@Entity
public class Road {
    @Id
    private String name;    
tx.begin();
Country france = new Country("France");
City paris = new City("Paris");
paris.setCountry(france);
City nice = new City("Nice");
nice.setCountry(france);
City normandy = new City("Normandy");
normandy.setCountry(france);
Road r1 = new Road("R1");
Road r2 = new Road("R2");
Set<Road> roads = new HashSet<>();
roads.add(r1);
roads.add(r2);
paris.setRoads(roads);
roads = new HashSet<>();
roads.add(r2);
normandy.setRoads(roads);
roads = new HashSet<>();
roads.add(r1);
nice.setRoads(roads);

em.persist(france);
em.persist(paris);
em.persist(nice);
em.persist(normandy);
em.persist(r1);
em.persist(r2);

tx.commit();
TypedQuery<City> roadsForCityQuery = em.createQuery("select c from City c join fetch c.roads join fetch c.country where c = :city", City.class);

City normandyRoads = roadsForCityQuery.setParameter("city", normandy).getSingleResult();    
System.out.println("normandyRoads = " + normandyRoads);

City parisRoads = roadsForCityQuery.setParameter("city", paris).getSingleResult();
System.out.println("parisRoads = " + parisRoads);

TypedQuery<City> citiesForRoadQuery = em.createQuery("select distinct c from City c join fetch c.roads road join fetch c.country where road = :road", City.class);

List<City> r1Cities = citiesForRoadQuery.setParameter("road", r1).getResultList();  
System.out.println("r1Cities = " + r1Cities);

List<City> r2Cities = citiesForRoadQuery.setParameter("road", r2).getResultList();  
System.out.println("r2Cities = " + r2Cities);
normandyRoads = City(name=Normandy, roads=[Road(name=R2)])
parisRoads = City(name=Paris, roads=[Road(name=R1), Road(name=R2)])
r1Cities = [City(name=Nice, roads=[Road(name=R1)]), City(name=Paris, roads=[Road(name=R1), Road(name=R2)])]
r2Cities = [City(name=Normandy, roads=[Road(name=R2)]), City(name=Paris, roads=[Road(name=R1), Road(name=R2)])]