这是一个老问题,但我遇到了类似的问题,我想我会添加到这个主题中。我需要将日志添加到现有的 StatelessSession 审计日志编写器。现有实现使用 StatelessSession,因为标准会话实现的缓存行为是不必要的开销并且我们不希望我们的休眠侦听器触发审计日志写入。这个实现是为了在没有交互的情况下实现尽可能高的写入性能。
但是,新的日志类型需要使用 insert-else-update 类型的行为,我们打算使用事务时间更新现有日志条目作为“标记”行为类型。在 StatelessSession 中,不提供 saveOrUpdate(),因此我们需要手动实现 insert-else-update。
根据这些要求:
您可以通过针对休眠持久性对象的自定义 sql-insert 使用 mysql“插入 ... 重复键更新”行为。您可以通过注释(如上面的答案)或通过 sql-insert 实体定义自定义 sql-insert 子句和休眠 xml 映射,例如:
<class name="SearchAuditLog" table="search_audit_log" persister="com.marin.msdb.vo.SearchAuditLog$UpsertEntityPersister">
<composite-id name="LogKey" class="SearchAuditLog$LogKey">
<key-property
name="clientId"
column="client_id"
type="long"
/>
<key-property
name="objectType"
column="object_type"
type="int"
/>
<key-property
name="objectId"
column="object_id"
/>
</composite-id>
<property
name="transactionTime"
column="transaction_time"
type="timestamp"
not-null="true"
/>
<!-- the ordering of the properties is intentional and explicit in the upsert sql below -->
<sql-insert><![CDATA[
insert into search_audit_log (transaction_time, client_id, object_type, object_id)
values (?,?,?,?) ON DUPLICATE KEY UPDATE transaction_time=now()
]]>
</sql-insert>
原发帖人专门询问了 MySQL。当我使用 mysql 实现 insert-else-update 行为时,我在执行 sql 的“更新路径”时遇到异常。具体来说,mysql 报告在仅更新 1 行时更改了 2 行(表面上是因为删除了现有行并插入了新行)。有关该特定功能的更多详细信息,请参阅this issue。
因此,当更新返回 2 倍受休眠影响的行数时,休眠将抛出 BatchedTooManyRowsAffectedException,将回滚事务并传播异常。即使您要捕获异常并处理它,到那时事务已经回滚了。
经过一番挖掘,我发现这是休眠使用的实体持久性的问题。在我的例子中,hibernate 使用的是 SingleTableEntityPersister,它定义了一个期望,即更新的行数应该与批处理操作中定义的行数相匹配。
使这种行为起作用所需的最后调整是定义一个自定义持久化器(如上面的 xml 映射所示)。在这种情况下,我们所要做的就是扩展 SingleTableEntityPersister 并“覆盖”插入期望。例如。我只是将这个静态类添加到持久性对象上,并将其定义为休眠映射中的自定义持久性:
public static class UpsertEntityPersister extends SingleTableEntityPersister {
public UpsertEntityPersister(PersistentClass arg0, EntityRegionAccessStrategy arg1, SessionFactoryImplementor arg2, Mapping arg3) throws HibernateException {
super(arg0, arg1, arg2, arg3);
this.insertResultCheckStyles[0] = ExecuteUpdateResultCheckStyle.NONE;
}
}
花了很长时间挖掘休眠代码才能找到这个 - 我无法在网上找到任何主题与解决这个问题。