简体   繁体   中英

Hibernate composite id merge

I'm using Hibernate with JPA and have a relation like this:

@Entity
@Table(name = "first")
public class First {
...
@OneToMany(mappedBy = "first")
private List<Availability> availabilities;
...
}


@Entity
@Table(name = "second")
public class Second {
...
@OneToMany(mappedBy = "second")
private List<Availability> availabilities;
...
}

@Entity
@Table(name="availability")
public class Availability implements Serializable {
@Id
@ManyToOne
@JoinColumn(name = "first_id")
private First first;

@Id
@ManyToOne
@JoinColumn(name = "second_id")
private Second second;

@Column(name = "availability")
private Integer availability;

...
hashcode and equals
}

I want to manage these 3 entities separately. First and Second work fine, but when I try to merge() the third one postgresql gets a null value instead of the id's and constraint violation exception is thrown. Why? Can I even use merge on this entity to add new rows to the table?

update: The merge is something like this:

public Availability setAvailability(Availability a) {
return em.merge(a);
}

where the availability is deserialized from front-end (just to mention, the collections of "key" classes are detached in it).

You already solved your issue, but I leave here a suggestion for users that need to use multiple-columns primary keys and can't change the database.

Apparently, using multiple @Id annotations by themselves produces an inconsistency when trying to merge: you assign all fields, and then during the merge the fields annotated with @Id get assigned null values (and the merge fails). I can guess an explanation: hibernate doesn't expect there to be a non-managed object with assigned @Ids; but this is an issue only with multiple column primary keys, so it seems to be a bug.

Note: calling persist instead of merge works (but in some cases you would duplicate rows or get database constraint errors, because the primary key in this case is checked only at database level).

I solved a similar issue using @IdClass. In your old code you would have to:

  1. Create a class (for example AvailabilityId ) with fields first and second (matching exactly the fields annotated with @Id in class Availability), and overriding equals and hashCode methods
  2. No annotations are required in AvailabilityId other than @Override (by the way, if you use @Column on both Availability and AvailabilityId fields you can get monstruous errors). This has not to be an entity or a table, it is a POJO.
  3. Annotate class Availability with @IdClass(AvailabilityId.class)

That should be it. Sorry for being 3 years late, but if you got rid of your multiple column primary key you are in a better world now anyway.

I tried @EmbeddedId but it broke a couple of other requirements (easy json->object conversion and queries working both in SQL and HQL with minor adjustments).

I solved the problem by avoiding the use of Composite ID. After rethinking the problem I found out that this case I actually can use an unique constraint.

@Entity
@Table(name = "availabilities", uniqueConstraints = { @UniqueConstraint(columnNames = {
    "first_id", "second_id" }) })
@SequenceGenerator(initialValue = 1, name = "availabilities_sequence", sequenceName =     "availabilities_sequence")
public class Availability implements IAvailability, Serializable {

private static final long serialVersionUID = -2977047920673617888L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "availabilities_sequence")
@Column(name = "id")
private Integer id;

@ManyToOne
@JoinColumn(name = "first_id", nullable=false)
private First first;

@ManyToOne
@JoinColumn(name = "second_id", nullable=false)
private Second second;

@Column(name = "availability", nullable=false)
private Integer availability;

...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM