JPA - проблема дизайна объекта

Я разрабатываю настольное приложение Java и использую JPA для настойчивости. У меня проблема, указанная ниже:

У меня две сущности:

  • Страна
  • Город

Страна имеет следующий атрибут:

  • CountryName (PK)

Город имеет следующий атрибут:

  • Название города

Теперь, когда может быть два города с одинаковым названием в двух разных странах, таблица primaryKey for City в базе данных представляет собой составной первичный ключ, состоящий из CityName и CountryName.

Теперь мой вопрос: как реализовать первичный ключ City как Entity в Java

   @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 в класс City.

Но тогда у нас есть повторяющиеся данные (countryName), хранящиеся в двух местах объекта City class: одно в объекте country, а другое в объекте cityPK.

Но с другой стороны, необходимы оба:

  • countryName в объекте cityPK необходимо, потому что мы реализуем составные первичные ключи таким образом.

  • countryName в country объекте необходимо, потому что это стандартный способ показать взаимосвязь между объектами.

Как обойти эту проблему?


person Yatendra    schedule 01.04.2010    source источник


Ответы (2)


countryName в CityPK следует пометить как доступный только для чтения с помощью @Column(insertable = false, updatable = false), и оба countryName должны быть сопоставлены с одним и тем же столбцом (с использованием свойства 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;
   }
person axtavt    schedule 01.04.2010
comment
Это стандартный способ решения проблем такого типа? Распространена ли эта проблема в JPA? - person Yatendra; 02.04.2010
comment
@Yatendra: Да, это стандартный способ (если вы не используете суррогатные ключи, как предлагает Питер). - person axtavt; 02.04.2010

ИМО, правильным способом решения таких проблем было бы использование сгенерированного внутреннего (обычно Long) идентификатора вместо естественного первичного ключа - это устраняет всю проблему. Конечно, это требует модификации вашей схемы БД, но из вашего сообщения я предполагаю, что это возможно.

@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
}
person Péter Török    schedule 02.04.2010
comment
Тогда я думаю, что не могу использовать id в методе равенства для равенства. Правильный? - person Yatendra; 02.04.2010
comment
@ Ятендра Почему бы и нет? Два города с одинаковым названием, но с разными странами должны иметь разные идентификаторы. Вам нужно только убедиться, что вы не вставляете один и тот же город одной и той же страны дважды в таблицу. Это может быть выполнено с помощью триггера БД или перехватчика Hibernate. - person Péter Török; 02.04.2010
comment
Вы не можете использовать id в equals(), потому что id генерируется базой данных. И не будет доступен, пока объект не будет сохранен. Обратитесь к книге Гэвина Кинга «Сохранение Java с гибернацией», см. Обсуждение бизнес-ключа и суррогатного ключа. - person Ram; 17.06.2011
comment
@ Ram, это проблема, только если вам нужно провести сравнение между двумя еще не сохраненными объектами (IMHO, это не очень распространено, но я могу ошибаться). И в этом случае equals может быть расширен для сравнения других важных свойств, если id равно null. - person Péter Török; 17.06.2011
comment
éter, если мы сделаем new City() и добавим его к Set и т. д., это будет проблематично. По ID может поменяться. Я тоже не уверен, но это то, что сказано в книге. - person Ram; 17.06.2011