Java Hibernate可以使用MySQL的“重复密钥更新”语法吗?
MySQL支持插入。。。在重复密钥更新时。。。语法,它允许您盲目地插入到数据库中,并在现有记录存在时回退到更新现有记录 当您希望快速事务隔离,并且希望更新的值依赖于数据库中已有的值时,这非常有用 举一个人为的例子,假设您想计算一篇文章在博客上的浏览次数。使用此语法的一种方法可能是:Java Hibernate可以使用MySQL的“重复密钥更新”语法吗?,java,mysql,hibernate,orm,Java,Mysql,Hibernate,Orm,MySQL支持插入。。。在重复密钥更新时。。。语法,它允许您盲目地插入到数据库中,并在现有记录存在时回退到更新现有记录 当您希望快速事务隔离,并且希望更新的值依赖于数据库中已有的值时,这非常有用 举一个人为的例子,假设您想计算一篇文章在博客上的浏览次数。使用此语法的一种方法可能是: INSERT INTO story_count (id, view_count) VALUES (12345, 1) ON DUPLICATE KEY UPDATE set view_count = view_c
INSERT INTO story_count (id, view_count) VALUES (12345, 1)
ON DUPLICATE KEY UPDATE set view_count = view_count + 1
这将比启动交易和处理新故事登上头版时不可避免的异常更加高效
我们如何使用Hibernate实现同样的目标
首先,Hibernate的HQL解析器将抛出异常,因为它不理解特定于数据库的关键字。事实上,HQL不喜欢任何显式插入,除非它是一个插入。。。选择
其次,Hibernate将SQL限制为仅选择。如果尝试调用session.createSQLQuerysql.executeUpdate,Hibernate将引发异常
第三,在这种情况下,Hibernate的saveOrUpdate不适合。您的测试将通过,但如果每秒有一个以上的访问者,那么您的生产将失败
我真的要颠覆Hibernate吗 您看过Hibernate注释了吗
@Entity
@Table(name="story_count")
@SQLInsert(sql="INSERT INTO story_count(id, view_count) VALUES (?, ?)
ON DUPLICATE KEY UPDATE view_count = view_count + 1" )
public class StoryCount
这是一个老问题,但我有一个类似的问题,我想我会添加到这个主题。我需要向现有的无状态会话审核日志编写器添加日志。现有的实现使用的是无状态会话,因为标准会话实现的缓存行为是不必要的开销,我们不希望hibernate侦听器触发审核日志写入。这个实现是为了在没有交互的情况下实现尽可能高的写性能 但是,新的日志类型需要使用insert else update类型的行为,我们打算使用事务时间作为标记类型的行为来更新现有的日志条目。在无状态会话中,不提供saveOrUpdate,因此我们需要手动实现insert-else更新 根据这些要求: 您可以使用mysql插入。。。通过hibernate持久化对象的自定义sql插入执行重复密钥更新行为。您可以通过上述答案中的注释或通过hibernate xml映射中的sql insert实体定义自定义sql insert子句,例如:
<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>
我花了相当长的时间在hibernate代码中找到了这个问题——我在网上找不到任何有解决方案的主题 如果您使用的是Grails,我发现这个解决方案不需要将域类移动到JAVA世界中,也不需要使用@SQLInsert注释: 创建自定义Hibernate配置 重写PersistentClass映射 使用ON DUPLICATE键将自定义INSERT sql添加到所需的持久类中。 例如,如果您有一个名为Person的域对象,并且希望在重复密钥更新时插入,您将创建如下配置:
public class MyCustomConfiguration extends GrailsAnnotationConfiguration {
public MyCustomConfiguration() {
super();
classes = new HashMap<String, PersistentClass>() {
@Override
public PersistentClass put(String key, PersistentClass value) {
if (Person.class.getName().equalsIgnoreCase(key)) {
value.setCustomSQLInsert("insert into person (version, created_by_id, date_created, last_updated, name) values (?, ?, ?, ?, ?) on duplicate key update id=LAST_INSERT_ID(id)", true, ExecuteUpdateResultCheckStyle.COUNT);
}
return super.put(key, value);
}
};
}
注意使用LAST_INSERT_ID时要小心,因为如果执行更新而不是INSERT,则无法正确设置,除非在语句中明确设置,例如ID=LAST_INSERT_ID。我还没有检查GORM从哪里获得ID,但我假设它在某处使用了LAST_INSERT_ID
希望这能有所帮助。如果您想在mysql中使用自动递增id字段,该怎么办?我收到了一些奇怪的错误,关于数据库没有返回一个ID,重复键上的什么会更新一些_值=?看起来,在哪里?是否应更新某个_值的实际新值?此解决方案是否已实际测试?我尝试了它,但遇到了一些困难,因为id字段在引用对象中不同步,导致保存另一个对象时出现外键约束错误。我想这可以在特定的场景中使用。请注意,Hibernate使用的列顺序与Entity类中定义它们的顺序不同。是如何解释的。如果我不想将view_count增加1,而是增加另一个作为参数传入的数字,该怎么办?您不能通过指定check属性来完成同样的事情吗?见例句:。。。或者@SQLInsertsql=…,check=none上述注释的更正应如下所示:@SQLInsertsql=…,check=ResultCheckStyle.none另请参阅此调查:
public class MyCustomConfiguration extends GrailsAnnotationConfiguration {
public MyCustomConfiguration() {
super();
classes = new HashMap<String, PersistentClass>() {
@Override
public PersistentClass put(String key, PersistentClass value) {
if (Person.class.getName().equalsIgnoreCase(key)) {
value.setCustomSQLInsert("insert into person (version, created_by_id, date_created, last_updated, name) values (?, ?, ?, ?, ?) on duplicate key update id=LAST_INSERT_ID(id)", true, ExecuteUpdateResultCheckStyle.COUNT);
}
return super.put(key, value);
}
};
}
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
configClass = 'MyCustomConfiguration'
}