JPA - 实体设计问题

Posted

技术标签:

【中文标题】JPA - 实体设计问题【英文标题】:JPA - Entity design problem 【发布时间】:2011-02-03 12:00:12 【问题描述】:

我正在开发一个 Java 桌面应用程序并使用 JPA 来实现持久性。我有一个问题如下:

我有两个实体:

国家 城市

国家有以下属性:

国名 (PK)

城市有以下属性:

城市名称

现在由于两个不同国家可以有两个同名城市,所以数据库中 City 表的主键是由CityNameCountryName 组成的复合主键。

现在我的问题是如何在 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;
   

现在我们知道从CountryCity 的关系是OneToMany,为了在上面的代码中显示这种关系,我在country 类中添加了一个country 变量。

但是我们在City类对象的两个地方存储了重复数据(countryName):一个在country对象中,另一个在cityPK对象中。

但另一方面,两者都是必要的:

cityPK 对象中的countryName 是必要的,因为我们以这种方式实现复合主键。

country 对象中的countryName 是必要的,因为它是显示对象之间关系的标准方式。

如何解决这个问题?

【问题讨论】:

【参考方案1】:

CityPK 中的countryName 应使用@Column(insertable = false, updatable = false) 标记为只读,并且countryNames 应映射到同一列(使用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,仅当您需要在两个尚未持久化的实体之间进行比较时才会出现问题(恕我直言,这不是很常见,但我可能错了)。在这种情况下,如果idnull,则可以扩展equals 以比较其他重要属性。 éter,如果我们执行 new City() 并将其添加到 Set 等,将会有问题。因为ID可以改变。我也不确定,但书上是这么说的。

以上是关于JPA - 实体设计问题的主要内容,如果未能解决你的问题,请参考以下文章

域对象是不是与 JPA 实体相同?

DDD | JPA - 可能涉及牺牲设计效率的设计问题

JPA的泛型DAO设计及使用

JPA 懒加载问题

从 JPA 注释的实体类自动生成数据模式

JPA 最佳实践? [关闭]