简体   繁体   中英

How to use existing records in many-to-many relation and to avoid unique constraint violation (hibernate)

There are two classes, Person and Vehicle, in many-to-many relationship. If a new Person is created or a record of existing Person is updated (eg Vehicle records added) it would be desirable to use existing Vehicle record if it exists.

The question is how to achieve it. A query prior the insert or update is not an option because there are many threads which can update or insert.

At this moment the application checks unique constraint exception and when it is caught the new existing Vehicle object is replaced by one which is queried from the db by "registration" column. This solution is working however it seems to be kind of clumsy as there has to be a separate session created for each Vehicle record.

Is there any way how to achieve desired behaviour by hibernate annotations? Or completely different solution? Thanks.

@Entity
@Table(name="PERSON", uniqueConstraints = { @UniqueConstraint(columnNames = "name", name="NAME_KEY") })
public class Person  implements Serializable {
  private static final long serialVersionUID = 3507716047052335731L;

  @Id
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PersonIdSeq")
  @SequenceGenerator( name = "PersonIdSeq", sequenceName="PERSON_ID_SEQ")
  private Long id;

  @Index(name="PERSON_NAME_IDX")
  private String name;

  @ManyToMany(targetEntity=Vehicle.class, cascade={CascadeType.PERSIST, CascadeType.MERGE})
  @JoinTable(name="PERSON_VEHICLE_LNK", joinColumns=@JoinColumn(name="PERSON_ID"),inverseJoinColumns=@JoinColumn(name="VEHICLE_ID"),
     uniqueConstraints = { @UniqueConstraint(columnNames = {"PERSON_ID", "VEHICLE_ID"}, name="person_vehicle_lnk_key")})
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PersonVehicleLnkIdSeq")
  @SequenceGenerator(name = "PersonVehicleLnkIdSeq", sequenceName="PERSON_VEHICLE_LNK_ID_SEQ")
  @CollectionId(columns = @Column(name="ID"), type=@Type(type="long"), generator = "PersonVehicleLnkIdSeq")
  private List<Vehicle> vehicle = new ArrayList<>();
  ...

@Entity
@Table( name = "VEHICLE", uniqueConstraints = {@UniqueConstraint(columnNames="registration", name="REGISTRATION_KEY")}  )
public class Vehicle implements Serializable {
  private static final long serialVersionUID = -5592281235230216382L;

  @Id
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="VehicleIdSeq")
  @SequenceGenerator( name = "VehicleIdSeq", sequenceName="VEHICLE_ID_SEQ")
  private Long id;

  @Index(name="REGISTRATION_IDX")
  private String registration;
  ...

A query prior the insert or update is not an option because there are many threads which can update or insert.

But this is how to do it.

If it is a performance problem (I don't think so) then consider about using a 2nd level cache. The first level can't handle this because it is bound to a session and you need at least one session per thread.

And then you need a version column in both Person and Vehicle .

In your application you already have the following problem: 1. User A loads a Person record. 2. User B loads the same Person record. 3. User A modifies the telephone number and saves the Person record. 4. User B modifies the Address and also saves the Person record. => Result: The modification (telephone number change) of User A is overwritten, the record has the old telephone number and nobody gets informed about this problem.

A version column avoids this problem. When there is a version column, in the step 4 Hibernate finds out the record was modified in meantime and throws an exception. This exception must be caught and User B must be told to reload the record and redo his address change. This forces a little extra work from user B (not much because this case seldom happens), but no information get lost and the database contains the correct information.

The same you have to do when no record was found on first reading, but on insert a constraint violation is caught. You already catch this error, but you don't inform the user, which you probably should do.

There is no easy solution in Hibernate level for this because the application logic has to treat this case (for example with informing the user).

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