7

I am developing a Java Desktop Application and using JPA for persistence. I have a problem mentioned below:

I have two entities:

  • Country
  • City

Country has the following attribute:

  • CountryName (PK)

City has the following attribute:

  • CityName

Now as there can be two cities with same name in two different countries, the primaryKey for City table in the datbase is a composite primary key composed of CityName and CountryName.

Now my question is How to implement the primary key of the City as an Entity in 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;
   }

Now as we know that the relationship from Country to City is OneToMany and to show this relationship in the above code, I have added a country variable in City class.

But then we have duplicate data(countryName) stored in two places in the City class' object: one in the country object and other in the cityPK object.

But on the other hand, both are necessary:

  • countryName in cityPK object is necessary because we implement composite primary keys in this way.

  • countryName in country object is necessary because it is the standard way of showing relashionship between objects.

How to get around this problem?

Amit
  • 33,847
  • 91
  • 226
  • 299

2 Answers2

7

countryName in CityPK should be marked read-only using @Column(insertable = false, updatable = false) and both countryNames should be mapped to the same column (using name property):

  @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;
   }
Péter Török
  • 114,404
  • 31
  • 268
  • 329
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • Is this a standard way of dealing this type of problem? Is this problem common in JPA? – Amit Apr 02 '10 at 07:19
  • @Yatendra: Yes, it's a standard way (if you don't use surrogate keys, as Peter suggests). – axtavt Apr 02 '10 at 13:25
3

IMO the proper way to deal with such issues would be to use a generated internal (typically Long) ID instead of a natural primary key - this eliminates the whole problem. Of course, this requires a modification of your DB schema, but from your post I assume that this is possible.

@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
}
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • Then I think that I can't use `id` in the equals method for equality. Correct? – Amit Apr 02 '10 at 13:47
  • @Yatendra Why not? Two cities with the same name but different country must have different IDs. You need only ensure that you don't insert the same city of the same country twice in the table. This could be enforced by a DB trigger, or by a Hibernate interceptor. – Péter Török Apr 02 '10 at 14:32
  • You cannot use `id` in the `equals()` because `id` is generated by database. And won't be available till the object is persisted. Refer to Gavin King's 'Java Persistence with Hibernate', see discussion on Business key and surrogate key. – Ram Jun 17 '11 at 07:17
  • @Ram, that is a problem only if you need to do comparisons between two not-yet-persisted entities (IMHO this is not very common, but I may be wrong). And in this case, `equals` can be extended to compare other significant properties if `id` is `null`. – Péter Török Jun 17 '11 at 09:01
  • éter, if we do a `new City()` and add that to a `Set` etc. it will be problematic. As the ID can change. I am not sure either but that is what the book said. – Ram Jun 17 '11 at 09:53