警告!!! 省略长篇大论
MySQL 5.6.39
mysql:mysql-connector-java:5.1.27
org.hibernate.common:hibernate-commons-annotations:4.0.5.Final
org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final
org.hibernate:hibernate-core:4.3.6.Final
org.hibernate:hibernate-entitymanager:4.3.6.Final
org.hibernate:hibernate-validator:5.0.3.Final
HTTP方法:POST,API路径:/reader
实体"reader"引擎:innoDB
id
name
total_pages_read
类映射:
@Entity
@Table(name = "reader")
public class Reader{
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "total_pages_read")
private Long total_pages_read;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "reader", orphanRemoval = true)
private Set<Book_read> book_reads;
...
}
我在一个读者写服务类中使用了createEntity()和recalculateTotalPageRead()方法:
@Service
public class ReaderWritePlatformServiceJpaRepositoryImpl{
private final ReaderRepositoryWrapper readerRepositoryWrapper;
...
@Transactional
public Long createEntity(final Long id, final String name, final Long total_pages_read){
try {
final Reader reader = new Reader(id, name, total_pages_read);
this.readerRepositoryWrapper.saveAndFlush(reader);
return 1l;
} catch (final Exception e) {
return 0l;
}
}
...
}
HTTP请求方式:POST,API路径:/bookread
实体名称为"book_read",数据库引擎为innoDB。
id
reader_id
book_title
number_of_pages
类映射:
@Entity
@Table(name = "book_read")
public class Book_read{
@Column(name = "id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reader_id")
private Reader reader;
@Column(name = "book_title")
private String book_title;
@Column(name = "number_of_pages")
private Long number_of_pages;
...
}
我在一个Book_read写服务类中使用createEntity()
和recalculateTotalPageRead()
方法:
@Service
public class Book_readWritePlatformServiceJpaRepositoryImpl{
private final ReaderRepositoryWrapper readerRepositoryWrapper;
private final Book_readRepositoryWrapper bookReadRepositoryWrapper;
...
@Transactional
public Long createEntity(final Long id, final Long reader_id, final String book_title, final Long number_of_pages){
try {
final Reader reader = this.readerRepositoryWrapper.findOneWithNotFoundDetection(reader_id);
final Book_read book_read = new Book_read(id, reader, book_title, number_of_pages);
this.bookReadRepositoryWrapper.saveAndFlush(book_read);
this.recalculateTotalPageRead(reader);
return 1l;
} catch (final Exception e) {
return 0l;
}
}
private void recalculateTotalPageRead(final Reader reader){
Long total_pages_read = Long.valueOf(0);
Set<Book_read> book_reads = reader.getBook_reads();
for (Book_read book_read : book_reads){
total_pages_read += book_read.getNumber_of_pages();
}
reader.setTotal_pages_read(total_pages_read);
this.readerRepositoryWrapper.saveAndFlush(reader);
}
...
}
当我尝试创建这两个实体时:
样例 "reader" :
id | name | total_pages_read
-----------------------------------
1 | Foo Reader | 0(by default)
示例 "book_read": 通过2个独立的POST方法调用
id | reader_id | book_title | number_of_pages
---------------------------------------------
1 | 1 | Foo Book | 2
2 | 1 | Bar Book | 3
在创建上述示例的"book_read"之后,期望对实体"reader"进行更改:
读者示例:
id | name | total_pages_read
-----------------------------------
1 | Foo Reader | 5
然而,从我的经验来看,在同时创建这两个 "book_read" 记录时,会发生 3 种情况:
情况1(正常):
- 第一个 "book_read" 创建完成
- 将 "reader" ID 为 1 的任何现有 "book_read" 添加到列表 "book_reads" 中。"book_reads" 的大小为 1。
- 将列表中每个 "book_read" 的 number_of_pages 添加到 "reader" ID 为 1 的 total_pages_read 中。当前的 total_pages_read 为 2。
- 开始创建第二个 "book_read"
- 第二个 "book_read" 创建完成
- 将 "reader" ID 为 1 的任何现有 "book_read" 添加到列表 "book_reads" 中。"book_reads" 的大小为 2。
- 将列表中每个 "book_read" 的 number_of_pages 添加到 "reader" ID 为 1 的 total_pages_read 中。
- 最终结果:total_pages_read = 5。
情况2(正常):
- (事务1) 开始创建第一个 "book_read"
- (事务2) 开始创建第二个 "book_read"
- (事务1) 完成创建第一个 "book_read"
- (事务2) 完成创建第二个 "book_read"
- (事务1) 将"reader"id为1的现有"book_read"添加到名为"book_reads"的列表中。 "book_reads"大小= 1。
- (事务2) 将"reader"id为1的现有"book_read"添加到名为"book_reads"的列表中。 "book_reads"大小= 1。
- (事务1) 将列表中每个"book_read"的number_of_pages添加到"reader" id 1的total_pages_read中。 当前的total_pages_read=2。
- (事务2) 将列表中每个"book_read"的number_of_pages添加到"reader" id 1的total_pages_read中。发生死锁异常。
- 重试(事务2)开始创建第二个"book_read"
- (事务2) 完成创建第二个"book_read"
- (事务2) 将"reader"id为1的现有"book_read"添加到名为"book_reads"的列表中。"book_reads"大小= 2。
- 将列表中每个"book_read"的number_of_pages添加到"reader" id 1的total_pages_read中。
- 最终结果: total_pages_read = 5。
Case 3 (不OK):
- 第1个 "book_read" 创建完成。
- 获取id为1的"reader"现有的任何"book_read",将其存入一个名为"book_reads"的列表中。"book_reads"的大小为1。
- 将列表中每个"book_read"的number_of_pages添加到id 1的"reader"的total_pages_read中。当前total_pages_read=2。
- 开始创建第2个 "book_read"。
- 第2个 "book_read"创建完成。
- 获取id为1的"reader"现有的任何"book_read",将其存入一个名为"book_reads"的列表中。"book_reads"的大小为1。
- 将列表中每个"book_read"的number_of_pages添加到id 1的"reader"的total_pages_read中。当前total_pages_read=3。未检测到死锁。
- 最终结果: total_pages_read = 3。
我该如何解决第3个案例?
干杯! 愉快的编程:D
已解决!
在数据模型上实现乐观锁定。
@Entity
@Table(name = "reader")
public class Reader{
@Version
@Column(name = "version")
private int version;
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "total_pages_read")
private Long total_pages_read;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "reader", orphanRemoval = true)
private Set<Book_read> book_reads;
...
}
createEntity()
方法上忘记了添加@Transactional
注解。我已经将它们添加到代码片段中了。谢谢你的指出。顺便说一下,我真的不明白你上一个关于更新机制的问题。附言:我对Java EE还是很新,感谢你的包容 :D - thyzz