我有一些代码来执行一个UPSERT,也称为合并。我想清理这段代码,具体来说,我想远离异常处理,并为这样一个简单的操作减少代码的总体冗长性和纯粹的复杂性。要求是插入每个项目,除非它已经存在:
public void batchInsert(IncomingItem[] items) {
try(Session session = sessionFactory.openSession()) {
batchInsert(session, items);
}
catch(PersistenceException e) {
if(e.getCause() instanceof ConstraintViolationException) {
logger.warn("attempting to recover from constraint violation");
DateTimeFormatter dbFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
items = Arrays.stream(items).filter(item -> {
int n = db.queryForObject("select count(*) from rets where source = ? and systemid = ? and updtdate = ?::timestamp",
Integer.class,
item.getSource().name(), item.getSystemID(),
dbFormat.format(item.getUpdtDateObj()));
if(n != 0) {
logger.warn("REMOVED DUPLICATE: " +
item.getSource() + " " + item.getSystemID() + " " + item.getUpdtDate());
return false;
}
else {
return true; // keep
}
}).toArray(IncomingItem[]::new);
try(Session session = sessionFactory.openSession()) {
batchInsert(session, items);
}
}
}
}
最初对s0的搜索是不令人满意的:
@SQLIn
如何使用HiberNate来模仿更新插入行为?与上面的问题非常相似,答案相似
- HiberNate"ON DUPLICATE KEY"逻辑同上,回答提到
合并()
单线程时可以 - 使用HiberNate批量插入或更新?类似的问题,但选择的答案是脱轨的,使用存储过程
- 防止使用JPA违反唯一约束的最佳方法再次非常天真,面向单线程的问答
这是一个死胡同,因为我真的不理解这条评论,尽管它听起来像是一个聪明的解决方案,而且提到了“实际相同的SQL语句”。
另一个有希望的方法是:Hibernate和 Spring 在提交到 DB 之前修改查询
冲突时不执行任何操作/重复密钥更新
两个主要的开源数据库都支持将幂等性向下推送到数据库的机制。下面的例子使用PostgreSQL语法,但可以很容易地适应MySQL。
通过遵循Hibernate和Spring中的思路在提交到DB之前修改查询,挂钩到Hibernate的查询生成,以及我如何在Hibernate中配置StatementInspector?,我实现了:
import org.hibernate.resource.jdbc.spi.StatementInspector;
@SuppressWarnings("serial")
public class IdempotentInspector implements StatementInspector {
@Override
public String inspect(String sql) {
if(sql.startsWith("insert into rets")) {
sql += " ON CONFLICT DO NOTHING";
}
return sql;
}
}
具有属性
<prop key="hibernate.session_factory.statement_inspector">com.myapp.IdempotentInspector</prop>
不幸的是,当遇到重复项时,这会导致以下错误:
原因:org . spring framework . ORM . hibernate 5 . hibernateoptimisticlockingfailure异常:批处理更新从更新[0]返回了意外的行数;实际行数:0;预期:1;嵌套异常为org . hibernate . stalestateexception:批处理更新从更新[0]返回了意外的行数;实际行数:0;预期:1
这是有道理的,如果您考虑一下在幕后发生了什么:
ON CONFLICT DO NOY
导致插入零行,但需要插入一次。
是否有一种解决方案能够实现线程安全的无异常并发幂等插入,并且不需要手动定义整个SQL插入语句以由Hibernate执行?
值得一提的是,我认为将重复数据检查向下推到数据库的方法是找到正确解决方案的途径。
澄清由< code>batchInsert
方法使用的< code>IncomingItem对象来自记录不可变的系统。在这种特殊情况下,< code>ON CONFLICT DO NOTHING的行为与UPSERT相同,尽管可能会丢失第n次更新。
我假设根据您的帖子,源、系统id和更新是一个唯一的键。基于此。我会的
一些伪代码:
public void batchInsert(IncomingItem[] items) {
//get all IncomingItem from the DB
List<IncomingItem> incomingItems = //DB query findAll;
List<IncomingItem> incomingItemsToSave = new ArrayList<>();
//check your duplicates!
for(IncomingItem incomingItem : incomingItems){
Arrays.stream(items).filter(item -> {
//compare unique key
// ... code here ...
if(!same unique key){
incomingItemsToSave.add(item);
}
});
}
try(Session session = sessionFactory.openSession()) {
batchInsert(session, incomingItemsToSave);
}
catch(PersistenceException e) {
}
}
注意,“幂等”与“冲突忽略”不同。后者可能会导致忽略对数据库的第二次写入,即使在插入失败时它实际上应该进行更新。
是否有一种解决方案可以启用html" target="_blank">线程安全的无异常并发幂等插入
我想说的是,如果没有RDBMS的具体支持,这在理论上可能是不可能的,尤其是“并发”部分。原因是数据不会实际写入,甚至可能“可见”,直到事务提交。那么,如果在事务 A 中确定记录不存在并且完成 INSERT
会发生什么。即使该 INSERT
对其他事务立即且原子可见,并发事务 B 也会确定它应该执行 UPDATE。
现在,如果以后的事务 A 遇到导致其回滚的问题怎么办?事务 A 中的插入
数据消失,事务 B 的 UPDATE
将找不到任何要更新的记录。
这就是为什么“并发”部分通常不工作的一个原因,因为不是所有的RDBMSs都支持某种原子的< code>UPSERT(或“on conflict ignore”)。
但是,您似乎不介意丢失对同一记录的第二次写入(更新),因为您正在谈论幂等性,这意味着潜在的UPDATE
实际上不会修改记录的数据,如果它已经存在。在这种情况下,“冲突忽略”确实等同于幂等性。
一个(显而易见?“解决方案”是使用一些显式锁(在数据库中)进行互斥,即事务 A 获取锁,执行其操作,然后再次释放它。事务 B 尝试获取锁,但在事务 A 完成之前将被阻止。但是,这将减少或阻止并发性,尤其是在一个事务中处理大量记录时。另外,由于RDBMS不知道锁与其保护的记录之间的关系,因此锁只是建议性的,每个客户端都必须使用相同的锁定方案。
你说你想“将幂等性向下推送到数据库”。如果这不是一个严格的要求,你可以只控制 Java 代码中的并发性;例如,通过使用一些支持并发的集合,其中代码以原子方式检查并插入它将要写入 RDBMS 的每个数据项的 ID。如果 ID 已在集合中,请跳过该项,否则插入到数据库中。
简短回答 - Hibernate不支持开箱即用(正如Hibernate大师在这篇博文中证实的那样)。也许你可以使用你已经描述的机制在某些情况下让它在某种程度上工作,但为此目的,直接使用本机查询对我来说似乎是最直接的方法。
更长的答案是,考虑到我猜 Hibernate 的所有方面,很难支持它,例如:
@Audit
实体呢,它们是创建还是更新,如果更新了什么?即使Hibernate以某种方式支持它,我不确定如果有太多的警告需要注意和考虑,我是否会使用该功能。
所以,我遵循的经验法则是:
我正在使用Spring和Hibernate,我得到了这个例外。以下是我试图获得的:我有User和UserSettings类,它们以OneToMany和ManyToOne注释为界,如下所示: 现在,我想为用户添加设置,我是这样做的: 问题来了:UserSettings中的用户持有旧设置和新设置的集合(设置,我刚刚创建并想要添加),但旧设置持有用户的集合,其中没有新设置。我想这就是为什么我会遇到异常,
我在elasticsearch插件中遇到了异常: java.security.AccessControlExcture:访问拒绝(org.elasticsearch.ThreadPersionorg.elasticsearch.ThreadPersion)java.security.AccessControlContext.check权限(AccessControlContext.java:472
我有一张这样定义的桌子 我如何定义排除约束,以便只有一行具有特定
我知道自动布局链基本上由3个不同的过程组成。 更新约束 布局视图(这里是我们计算框架的地方) 显示 我并不完全清楚的是 和 之间的内在区别。来自苹果文档: 设置需要布局 当您想要调整视图子视图的布局时,请在应用程序的主线程上调用此方法。此方法记下请求并立即返回。由于此方法不会强制立即更新,而是等待下一个更新周期,因此您可以使用它来使多个视图的布局失效,然后再更新这些视图中的任何一个。此行为允许您将
问题内容: 我试着通过网络搜索,但是徒劳。有没有一种方法可以使用hibernate来执行幂等更新。 一种用例是使用HTTP PUT通过REST API更新数据库中的特定字段。因此,例如,如果我有一个包含列: _ Id,Name,Phone,UpdateDate*_ 的数据库 _ ,_ 并且我多次用相同的值更新(特定 ID的 ) Phone ( 电话) 字段,那么只有我的第一个操作必须更新 Phon
我刚开始使用hibernate编程,请帮帮我,我在执行下面的文件时遇到了问题。我使用的是MySQL数据库。我的错误是 SLF4J:未能加载类“org.slf4j.impl.StatibloggerBinder”。slf4j:默认为无操作(NOP)记录器实现slf4j:有关更多细节,请参见http://www.slf4j.org/codes.html#staticloggerbinder。Hiber