JPA - 实体设计问题
Posted
技术标签:
【中文标题】JPA - 实体设计问题【英文标题】:JPA - Entity design problem 【发布时间】:2011-02-03 12:00:12 【问题描述】:我正在开发一个 Java 桌面应用程序并使用 JPA 来实现持久性。我有一个问题如下:
我有两个实体:
国家 城市国家有以下属性:
国名 (PK)城市有以下属性:
城市名称现在由于两个不同国家可以有两个同名城市,所以数据库中 City 表的主键是由CityName
和CountryName
组成的复合主键。
现在我的问题是如何在 Java 中将
City
的主键实现为Entity
@Entity
public class Country implements Serializable
private String countryName;
@Id
public String getCountryName()
return this.countryName;
@Entity
public class City implements Serializable
private CityPK cityPK;
private Country country;
@EmbeddedId
public CityPK getCityPK()
return this.cityPK;
@Embeddable
public class CityPK implements Serializable
public String cityName;
public String countryName;
现在我们知道从Country
到City
的关系是OneToMany
,为了在上面的代码中显示这种关系,我在country
类中添加了一个country
变量。
但是我们在City
类对象的两个地方存储了重复数据(countryName
):一个在country
对象中,另一个在cityPK
对象中。
但另一方面,两者都是必要的:
cityPK
对象中的countryName
是必要的,因为我们以这种方式实现复合主键。
country
对象中的countryName
是必要的,因为它是显示对象之间关系的标准方式。
如何解决这个问题?
【问题讨论】:
【参考方案1】:CityPK
中的countryName
应使用@Column(insertable = false, updatable = false)
标记为只读,并且countryName
s 应映射到同一列(使用name
属性):
@Entity
public class City implements Serializable
@EmbeddedId
private CityPK cityPK;
@ManyToOne
@JoinColumn(name = "countryName")
private Country country;
@Embeddable
public class CityPK implements Serializable
public String cityName;
@Column(name = "countryName", insertable = false, updatable = false)
public String countryName;
【讨论】:
这是处理此类问题的标准方法吗?这个问题在 JPA 中很常见吗? @Yatendra:是的,这是一种标准方式(如果你不使用代理键,正如彼得建议的那样)。【参考方案2】:IMO 处理此类问题的正确方法是使用生成的内部(通常为 Long
)ID 而不是自然主键 - 这消除了整个问题。当然,这需要修改您的数据库架构,但从您的帖子中我认为这是可能的。
@Entity
public class City implements Serializable
private Long id;
private String name;
private Country country;
@Id
@GeneratedValue
@Column(name = "CITY_ID")
public Long getId()
return this.id;
private void setId(Long id)
this.id = id;
// more getters, setters and annotations
【讨论】:
那我觉得不能在equals方法中使用id
进行相等。对吗?
@Yatendra 为什么不呢?两个同名但不同国家的城市必须有不同的ID。您只需要确保您不会在表中两次插入同一国家的同一城市。这可以由 DB 触发器或 Hibernate 拦截器强制执行。
您不能在equals()
中使用id
,因为id
是由数据库生成的。并且在对象被持久化之前将不可用。请参阅 Gavin King 的“Java Persistence with Hibernate”,参见关于业务键和代理键的讨论。
@Ram,仅当您需要在两个尚未持久化的实体之间进行比较时才会出现问题(恕我直言,这不是很常见,但我可能错了)。在这种情况下,如果id
是null
,则可以扩展equals
以比较其他重要属性。
éter,如果我们执行 new City()
并将其添加到 Set
等,将会有问题。因为ID可以改变。我也不确定,但书上是这么说的。以上是关于JPA - 实体设计问题的主要内容,如果未能解决你的问题,请参考以下文章