Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Hibernate 第二个事务覆盖第一个事务的更改_Hibernate_Spring Data Jpa_Spring Data_Spring Data Rest_Spring Transactions - Fatal编程技术网

Hibernate 第二个事务覆盖第一个事务的更改

Hibernate 第二个事务覆盖第一个事务的更改,hibernate,spring-data-jpa,spring-data,spring-data-rest,spring-transactions,Hibernate,Spring Data Jpa,Spring Data,Spring Data Rest,Spring Transactions,我在一个项目中工作,我们将Spring数据Rest与HATEOAS、Spring JPA和Hibernate一起使用,目前我们遇到了一个无法修复的奇怪问题: 角度前端将两个HTTP请求传输到后端。两个请求都希望在同一行的不同列中添加关系,因为这必须在两个请求中完成。 创建两个事务A和B,Hibernate在每个事务中执行一次选择,以获得有问题的行。 Hibernate获取将在每个事务中链接的实体。 Hibernate在事务A中更新所选行一次。事务B稍后更新该行,并将事务A中设置的值重置为事务开始

我在一个项目中工作,我们将Spring数据Rest与HATEOAS、Spring JPA和Hibernate一起使用,目前我们遇到了一个无法修复的奇怪问题:

角度前端将两个HTTP请求传输到后端。两个请求都希望在同一行的不同列中添加关系,因为这必须在两个请求中完成。 创建两个事务A和B,Hibernate在每个事务中执行一次选择,以获得有问题的行。 Hibernate获取将在每个事务中链接的实体。 Hibernate在事务A中更新所选行一次。事务B稍后更新该行,并将事务A中设置的值重置为事务开始时执行的初始选择的值。因此,我们正在丢失事务A的所有信息。 问题在于,这似乎不是确定性的。它只是偶尔发生,然后在接下来的十次中正常工作

我们无法通过添加锁定来解决此问题乐观锁定只会导致事务B中的ObjectOptimisticLockingFailureException,悲观锁定也没有效果。我们正在为实体使用DynamicUpdate。我们尝试在事务上设置传播,但这并没有改变任何事情。 现在我们没有主意了,谷歌搜索也没有产生任何结果

应更新的实体如下所示:

@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Data
@JsonIgnoreProperties(value = { "history" }, allowGetters = true)
@DynamicUpdate
@SelectBeforeUpdate
@Entity
@Table(name = "parameters")
public class Parameter
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "parameter_id", unique = true)
    private Long id;

    @NonNull
    @Column(name = "name", nullable = false, unique = true)
    private String key;

    @ManyToOne(fetch = FetchType.EAGER,
            cascade = {
                    CascadeType.REFRESH,
                    CascadeType.MERGE,
                    CascadeType.PERSIST,
                    CascadeType.DETACH
            }
    )
    private Component component;

    @OneToOne(fetch = FetchType.EAGER,
            cascade = {
                    CascadeType.REFRESH,
                    CascadeType.MERGE,
                    CascadeType.PERSIST,
                    CascadeType.DETACH
            }
    )
    private ParameterTypes type;

    @OneToMany(
            mappedBy = "parameter",
            fetch = FetchType.LAZY,
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    @OrderBy("history_creation_timestamp DESC")
    private List<History> history = new LinkedList<>();

    @CreationTimestamp
    @Column(name = "parameter_creation_timestamp")
    private LocalDateTime creationTimestamp;

    @UpdateTimestamp
    @Column(name = "parameter_update_timestamp")
    private LocalDateTime updateTimestamp;
}
saveParameter(): void {
    this.editMode = false;
    const saveObservs: Observable<any>[] = [];
    if (this.componentControl.dirty) {
    saveObservs.push(this.parameter.addRelation('component', this.componentControl.value));
    }

    if (this.typeControl.dirty) {
    saveObservs.push(this.parameter.addRelation('type', this.typeControl.value));
    }

    forkJoin(
    saveObservs
    ).pipe(
    catchError(err => of(err))
    ).subscribe(val => console.log(val));
}
componentControl和typeControl都是FormControl实例

addRelation函数如下所示:

Resource.prototype.addRelation = function (relation, resource) {
    if (this.existRelationLink(relation)) {
        var header = ResourceHelper.headers.append('Content-Type', 'text/uri-list');
        return ResourceHelper.getHttp().put(ResourceHelper.getProxy(this.getRelationLinkHref(relation)), resource._links.self.href, { headers: header });
    }
    else {
        return observableThrowError('no relation found');
    }
};

函数的上下文可以找到

不幸的是,我不知道Angular 7,但我感觉saveObservs.push是异步工作的。 这意味着,有时第二个请求在第一个请求完成之前到达API,从DB加载未修改的记录,然后仅在修改第二个引用的情况下将其写回

所以第二次推应该等待第一次推的结果

但是,如果您试图同时从两个客户机修改同一对象,则无法解决此问题

您应该将version属性添加到实体中,如果您试图修改具有过时版本的对象,Spring Data Rest将自动响应错误

  @Version
  private Long version;

前端代码是什么样子的?我添加了通信的前端部分。希望这会有所帮助。我怀疑这个问题正如下面的答案所建议的那样。你能补充更多关于为什么你需要提出两个独立请求的信息吗?据我调查,这个问题是由于使用HAL和HATEOAS造成的。当您想要链接两个实体时,必须调用addRelation函数,该函数不允许多个资源作为参数。此外,您不能简单地调用参数实体上的update来设置这两个关系。有点遗憾,因为我真的很喜欢HATEOAS方法。所以这是前端库的限制,而不是SDR?我觉得你问的是你试图解决的问题,而不是实际的问题。谢谢你的回答。您对异步调用的理解是正确的。我希望在后端解决这个问题。前端修复将导致一些if-else'ing,但遗憾的是,这似乎是唯一的解决方案。添加一个版本并依靠乐观锁定是在更新过程中了解问题并在前端做出正确反应的最佳方式。有时软件开发会有点令人沮丧-