【问题标题】:JPA - Entity design problemJPA - 实体设计问题
【发布时间】: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 是必要的,因为它是显示对象之间关系的标准方式。

如何解决这个问题?

【问题讨论】:

    标签: java jpa entity entity-relationship object-relationships


    【解决方案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可以改变。我也不确定,但书上是这么说的。
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-05
    • 2021-04-10
    • 2017-01-13
    • 2015-05-08
    • 1970-01-01
    相关资源
    最近更新 更多