简体   繁体   English

如何使用多对多关系中的现有记录并如何避免唯一性约束冲突(休眠)

[英]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. 这时,应用程序检查唯一约束异常,并在捕获到新的现有Vehicle对象时,将其替换为通过“ registration”列从db查询的对象。 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. 该解决方案有效,但是似乎有点笨拙,因为必须为每个Vehicle记录创建一个单独的会话。

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 . 然后,在PersonVehicle中都需要一个version列。

In your application you already have the following problem: 1. User A loads a Person record. 在您的应用程序中,您已经遇到以下问题:1.用户A加载个人记录。 2. User B loads the same Person record. 2.用户B加载同一个人记录。 3. User A modifies the telephone number and saves the Person record. 3.用户A修改电话号码并保存个人记录。 4. User B modifies the Address and also saves the Person record. 4.用户B修改地址并保存“个人”记录。 => 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的修改(电话号码更改)被覆盖,记录中包含旧的电话号码,没有人知道此问题。

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. 当存在版本列时,在第4步中,Hibernate会同时发现记录已被修改并引发异常。 This exception must be caught and User B must be told to reload the record and redo his address change. 必须捕获此异常,并且必须告知用户B重新加载记录并重做其地址更改。 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. 这迫使用户B进行一些额外的工作(因为这种情况很少发生,所以不多),但是不会丢失任何信息,并且数据库包含正确的信息。

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). 在Hibernate级别上,没有简单的解决方案,因为应用程序逻辑必须处理这种情况(例如,通知用户)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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