【问题标题】:How to use existing records in many-to-many relation and to avoid unique constraint violation (hibernate)如何在多对多关系中使用现有记录并避免违反唯一约束(休眠)
【发布时间】:2013-01-18 10:04:23
【问题描述】:

 在多对多关系中有两个类,Person 和 Vehicle。如果创建了新的 Person 或更新了现有 Person 的记录(例如添加了 Vehicle 记录),则最好使用现有的 Vehicle 记录(如果存在)。

 问题是如何实现它。插入或更新之前的查询不是一个选项,因为有许多线程可以更新或插入。

 此时,应用程序检查唯一约束异常,当它被捕获时,新的现有车辆对象被替换为从数据库中通过“注册”列查询的对象。此解决方案有效,但它似乎有点笨拙,因为必须为每个车辆记录创建单独的会话。

有什么方法可以通过休眠注释实现所需的行为?还是完全不同的解决方案?谢谢。

@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;
  ...

【问题讨论】:

  • 在 MySQL 中是INSERT ... ON DUPLICATE KEY UPDATE ...
  • 尝试以下操作:尝试插入;如果失败,请更新。如果还有并发删除,这可能需要多轮尝试。
  • 感谢。我确定这是数据库级别的解决方案,但我宁愿留在 ORM 级别。
  • 您可以在对象级别执行更新重试循环。
  • 实际上,正如我在问题中所说,我已经在这样做了,但是我对解决方案不满意,因为对于每条车辆记录,都必须打开一个新会话,如果它失败了必须回滚并且必须创建另一个新会话以从表中查询现有记录。

标签: java hibernate many-to-many unique-constraint


【解决方案1】:

插入或更新之前的查询不是一个选项,因为有许多线程可以更新或插入。

但这是怎么做的。

如果是性能问题(我不这么认为),请考虑使用二级缓存。第一级无法处理此问题,因为它绑定到会话,并且每个线程至少需要一个会话。

然后您需要在PersonVehicle 中都有一个版本列。

在您的应用程序中,您已经遇到了以下问题: 1. 用户 A 加载了个人记录。 2. 用户 B 加载相同的人员记录。 3. 用户A修改电话号码并保存Person记录。 4. 用户 B 修改了地址,也保存了人员记录。 => 结果:用户A的修改(电话号码更改)被覆盖,记录有旧电话号码,没有人知道这个问题。

版本列避免了这个问题。当有版本列时,Hibernate 在步骤 4 中发现记录同时被修改并抛出异常。必须捕获此异常,并且必须告知用户 B 重新加载记录并重做他的地址更改。这会迫使用户 B 做一些额外的工作(不多,因为这种情况很少发生),但不会丢失任何信息,并且数据库包含正确的信息。

当第一次读取时没有找到记录,但插入时发现约束违规时,您必须这样做。你已经发现了这个错误,但你没有通知用户,你可能应该这样做。

在 Hibernate 级别对此没有简单的解决方案,因为应用程序逻辑必须处理这种情况(例如通知用户)。

【讨论】:

  • 一旦插入一个 Person ,它的名字是不变的,同样适用于 Vehicle 类中的注册。只能修改车辆列表,这是由自动线程完成的,而不是由用户完成的。
  • 当插入是由自动线程完成时,那么这种情况必须由自动线程处理。当记录已经存在时,这取决于您的应用程序逻辑。对于存在性检查,您有两种可能性:在插入之前读取或捕获唯一约束违规。如果你使用StatelessSession,那么你仍然可以在异常后使用它。
猜你喜欢
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 2014-10-16
  • 1970-01-01
  • 2014-01-12
  • 1970-01-01
  • 2016-03-16
  • 1970-01-01
相关资源
最近更新 更多