我们正努力在短时间内拯救许多孩子,冬眠不断给孩子带来乐观。下面是一个简单的例子:
University
id
name
audit_version
Student
id
name
university_id
audit_version
其中大学id可以为空。
java对象如下所示:
@Entity
@Table(name = "university")
@DynamicUpdate
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class University {
@Id
@SequenceGenerator(name = "university_id_sequence_generator", sequenceName = "university_id_sequence", allocationSize = 1)
@GeneratedValue(strategy = SEQUENCE, generator = "university_id_sequence_generator")
@EqualsAndHashCode.Exclude
private Long id;
@Column(name = "name")
private String name;
@Version
@Column(name = "audit_version")
@EqualsAndHashCode.Exclude
private Long auditVersion;
@OptimisticLock(excluded = true)
@OneToMany(mappedBy = "student")
@ToString.Exclude
private List<Student> student;
}
@Entity
@Table(name = "student")
@DynamicUpdate
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class Student {
@Id
@SequenceGenerator(name = "student_id_sequence_generator", sequenceName = "student_id_sequence", allocationSize = 1)
@GeneratedValue(strategy = SEQUENCE, generator = "student_id_sequence_generator")
@EqualsAndHashCode.Exclude
private Long id;
@Column(name = "name")
private String name;
@Version
@Column(name = "audit_version")
@EqualsAndHashCode.Exclude
private Long auditVersion;
@OptimisticLock(excluded = true)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "university_id")
@ToString.Exclude
private University university;
}
似乎当我们分配大学然后挽救学生时,如果我们在短时间内完成4个以上的任务,我们就会得到乐观的结果。hibernate似乎正在university表上创建更新版本,尽管university在db级别没有更改。
更新:保存学生的代码
Optional<University> universityInDB = universidyRepository.findById(universtityId);
universityInDB.ifPresent(university -> student.setUniversity(university);
Optional<Student> optionalExistingStudent = studentRepository.findById(student);
if (optionalExistingStudent.isPresent()) {
Student existingStudent = optionalExistingStudent.get();
if (!student.equals(existingStudent)) {
copyContentProperties(student, existingStudent);
studentToReturn = studentRepository.save(existingStudent);
} else {
studentToReturn = existingStudent;
}
} else {
studentToReturn = studentRepository.save(student);
}
private static final String[] IGNORE_PROPERTIES = {"id", "createdOn", "updatedOn", "auditVersion"};
public void copyContentProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target, Arrays.asList(IGNORE_PROPERTIES)));
}
我们尝试了以下方法 @OptimisticLock(excluded = true)
不起作用,仍然给出乐观锁例外。 @JoinColumn(name = "university_id", updatable=false)
只处理更新,因为我们不保存更新 @JoinColumn(name = "university_id", insertable=false)
工作但不保存关系,并且大学id始终为空
改变级联行为。唯一有意义的价值是 Cascade.DETACH
,但给出一个org.springframework.dao.invaliddataaccessapiusageexception:org.hibernate.transientpropertyvalueexception:object引用一个未保存的临时示例-在刷新之前保存临时示例。
我们想到了另一个解决办法,但不确定该选什么
给客户端一个409(冲突)错误
在409之后,客户机必须重试他的post。对于通过队列发送的对象,队列稍后将重试该条目。我们不希望我们的客户管理这个错误
出现optimisticlockexception后重试
它并不干净,因为当条目来自队列时,我们已经在做了,但可能是迄今为止最好的解决方案。
使之成为关系的父所有者
如果没有大量的关系,这可能是好的,但是我们有可能在100甚至1000中的情况,这将使对象变大,以便在队列中或通过rest调用发送。
悲观锁
我们的整个数据库目前处于乐观锁定状态,到目前为止,我们设法防止了这种乐观锁定的情况,我们不想仅仅因为这种情况而改变我们的整个锁定策略。可能对模型的子集强制悲观锁定,但我还没有考虑是否可以这样做。
2条答案
按热度按时间4sup72z81#
它不需要它,除非你需要它。请执行以下操作:
为了分配
University
你不必加载University
将实体添加到上下文中。因为从技术上讲,你只需要用一个合适的外键保存一个学生记录(university_id
). 所以当你有一个university_id
,您可以使用repository方法创建hibernate代理getOne()
.解释
冬眠是相当复杂的引擎盖下**当您将实体加载到上下文中时,它将创建其字段的快照副本,并跟踪您是否更改了其中任何一个字段。它做的更多。。。因此,我猜这个解决方案是最简单的,应该会有所帮助(除非您在同一会话的范围内的其他地方更改“university”对象)。很难说其他部分何时隐藏。
潜在问题
错
@OneToMany
Map应初始化集合。hibernate使用它自己的集合实现,您不应该手动设置字段。只调用如下方法
add()
或者remove()
,或clear()
```private List student; // should be ... = new ArrayList<>();
m1m5dgzv2#
如果您从hibernate启用查询日志,那么可以查看orm正在执行的查询。你可能会意识到你的orm做得太多了。
在应用程序属性或配置文件中启用
hibernate.show_sql=true
如果你的一次更新Student
成为对University
它将成为其所有包含Students
. 每件事都会有一个版本。orm和实体Map用于战略性地检索数据。它们不应用于实际定义对象关系。
您需要访问策略,并根据实体在其rest端点中的使用方式来设计实体。
您在问题中指定要保存
Student
但你注意到University
也会随着Student
更新。很可能永远不会有一个
Student
应该更新一个University
###保持身体苗条!您可以以支持这种单向关系的方式构造实体。我删除了一些注解只是为了演示结构。您应该记住,在创建实体时,您编写实体是为了了解如何检索实体。。。
这将确保对学生的更新保持目标明确。你只是给学生分配了一个大学id,从而建立了这种关系。
您通常希望尊重锁例外。在lockexception上重试只是强迫您的数据库提交,并且随着应用程序的扩展会引起更多的麻烦。
您始终可以选择使用精益实体,并创建自定义响应或消息对象,将结果压缩到一起。
orms不用于创建快捷方式
一个系统的性能结果
SELECT
在索引/外键上,获取它们的成本大致相同。。。只会引入一点额外的网络延迟。第二次访问数据库并不总是一个坏主意(通常,hibernate就是这样获取实体的)您不必编写查询,但仍需要了解检索和更新策略。
为了方便的.getchild()方法,您牺牲了数据库性能并引入了复杂性。您会发现,通过删除注解而不是添加注解,可以解决更多的性能/锁定问题。