gpt4 book ai didi

java - 使用 Spring JPA/Hibernate 的条件插入

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:57:36 24 4
gpt4 key购买 nike

我正在处理一个在集群环境中运行的项目,其中有许多节点和一个数据库。该项目使用 Spring-data-JPA (1.9.0) 和 Hibernate (5.0.1)。我在解决如何防止重复行问题时遇到问题。

为了举例,这里有一个简单的表格

@Entity
@Table(name = "scheduled_updates")
public class ScheduledUpdateData {
public enum UpdateType {
TYPE_A,
TYPE_B
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private UUID id;

@Column(name = "type", nullable = false)
@Enumerated(EnumType.STRING)
private UpdateType type;

@Column(name = "source", nullable = false)
private UUID source;
}

重要的部分是有一个 UNIQUE(type, source) 约束。

当然,还有匹配示例存储库:

@Repository
public class ScheduledUpdateRepository implements JpaRepository<ScheduledUpdateData, UUID> {
ScheduledUpdateData findOneByTypeAndSource(final UpdateType type, final UUID source);

//...
}

这个例子的想法是,系统的某些部分可以插入行以安排定期运行的东西,在所述运行之间任意次数。当实际运行任何东西时,它不必担心对同一事物进行两次操作。

如何编写一个有条件地插入到该表中的服务方法?我尝试过的一些不起作用的方法是:

  1. Find > Act - 服务方法将使用存储库查看条目是否已存在,然后根据需要更新找到的条目或保存新条目。这是行不通的。
  2. 尝试插入 > 如果失败则更新 - 服务方法将尝试插入,捕获由于唯一约束导致的异常,然后执行更新。这是行不通的,因为事务已经处于回滚状态,无法在其中进行进一步的操作。
  3. 带有“INSERT INTO ... WHERE NOT EXISTS ...”的 native 查询* - 存储库有一个新的 native 查询:

    @Repository
    public class ScheduledUpdateRepository implements JpaRepository<ScheduledUpdateData, UUID> {
    // ...

    @Modifying
    @Query(nativeQuery = true, value = "INSERT INTO scheduled_updates (type, source)" +
    " SELECT :type, :src" +
    " WHERE NOT EXISTS (SELECT * FROM scheduled_updates WHERE type = :type AND source = :src)")
    void insertUniquely(@Param("type") final String type, @Param("src") final UUID source);
    }

    不幸的是,这也不起作用,因为 Hibernate 似乎首先执行 WHERE 子句使用的 SELECT - 这意味着最终会尝试多次插入,导致违反唯一约束。

我绝对不知道 JTA、JPA 或 Hibernate 的许多优点。关于如何跨多个 JVM 插入具有唯一约束(除了主键)的表有什么建议吗?

编辑 2016-02-02

使用 Postgres (2.3) 作为数据库,尝试使用隔离级别 SERIALIZABLE - 遗憾的是,这本身仍然会导致违反约束的异常。

最佳答案

您试图确保一次只有 1 个节点可以执行此操作。执行此操作的最佳(或至少与数据库无关)方法是使用“锁定”表。该表将只有一行,并将充当信号量以确保串行访问。

确保这个方法被包裹在一个事务中

// this line will block if any other thread already has a lock
// until that thread's transaction commits
Lock lock = entityManager.find(Lock.class, Lock.ID, LockModeType.PESSIMISTIC_WRITE);

// just some change to the row, it doesn't matter what
lock.setDateUpdated(new Timestamp(System.currentTimeMillis()));
entityManager.merge(lock);
entityManager.flush();

// find your entity by unique constraint
// if it exists, update it
// if it doesn't, insert it

关于java - 使用 Spring JPA/Hibernate 的条件插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35070655/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com