Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/351.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
Java Hibernate可以使用MySQL的“重复密钥更新”语法吗?_Java_Mysql_Hibernate_Orm - Fatal编程技术网

Java Hibernate可以使用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

MySQL支持插入。。。在重复密钥更新时。。。语法,它允许您盲目地插入到数据库中,并在现有记录存在时回退到更新现有记录

当您希望快速事务隔离,并且希望更新的值依赖于数据库中已有的值时,这非常有用

举一个人为的例子,假设您想计算一篇文章在博客上的浏览次数。使用此语法的一种方法可能是:

 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'
}