如何强制 Hibernate 添加 ID 列以插入查询?

Posted

技术标签:

【中文标题】如何强制 Hibernate 添加 ID 列以插入查询?【英文标题】:How to force Hibernate to add ID column to insert query? 【发布时间】:2016-08-26 12:20:42 【问题描述】:

我在依赖项中使用带有以下 DB 连接器的 Spring Boot 1.4.0.RELEASE

<!-- runtime dependencies -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

还有一个实体类,带有GenerationType.AUTO 策略用于ID生成(下面的代码不完整)

@Entity
@Table(name = "scanner_run")
public class ScannerRun extends BaseObject 

    private Long id;

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    public Long getId() 
        return this.id;
    

使用 H2 时插入新实体没有问题

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

Hibernate 生成 insert into scanner_run (id, completed_ts, repository_id, started_ts, success) values (null, ?, ?, ?, ?) 查询并创建新记录。

但是使用 MySQL

spring.datasource.url=jdbc:mysql://localhost/db_dev?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&connectionCollation=utf8_general_ci
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

生成的查询是 insert into scanner_run (completed_ts, repository_id, started_ts, success) values (?, ?, ?, ?) - ID 不在查询中 - 失败

其他没有什么区别,只是改application.properties换数据库。与旧版本的 Hibernate 和 MySQL 连接器相同的代码适用于相同的 MySQL 安装。 MySQL 连接器解析为mysql:mysql-connector-java:jar:5.1.39

你能发现什么不对吗? 日志中的确切消息和异常是:

2016-08-26 14:38:03.964 DEBUG 32555 --- [myScheduler-1] org.hibernate.SQL:插入scanner_run(completed_ts,repository_id,started_ts,成功)值(?,?,?,?) 2016-08-26 14:38:03.967 WARN 32555 --- [myScheduler-1] o.h.engine.jdbc.spi.SqlExceptionHelper:SQL 错误:1364,SQLState:HY000 2016-08-26 14:38:03.967 错误 32555 --- [myScheduler-1] o.h.engine.jdbc.spi.SqlExceptionHelper:字段 'id' 没有默认值 2016-08-26 14:38:03.979 错误 32555 --- [myScheduler-1] os.s.s.TaskUtils$LoggingErrorHandler :计划任务中发生意外错误。 jvr.decrex.exception.ExecutionError:无法保存 ScannerRunid=null,repository=/jv-ration/projects/jv-ration/deCrex/jvr-decrex/,startedTs=Fri Aug 26 14:38:03 CEST 2016,完成Ts=null 在 jvr.decrex.service.impl.GenericManagerImpl.insert(GenericManagerImpl.java:107) 在 jvr.decrex.scanner.service.impl.ScannerRunManagerImpl.createScan(ScannerRunManagerImpl.java:79) ………… 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 在 java.lang.Thread.run(Thread.java:745) 引起:org.springframework.orm.jpa.JpaSystemException:无法执行语句;嵌套异常是 org.hibernate.exception.GenericJDBCException:无法执行语句 在 org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333) 在 org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) ………… 在 jvr.decrex.scanner.dao.jpa.ScannerRunDaoJpa$$EnhancerBySpringCGLIB$$5e6c846a.insert() 在 jvr.decrex.service.impl.GenericManagerImpl.insert(GenericManagerImpl.java:105) ...省略了21个常用框架 引起:org.hibernate.exception.GenericJDBCException:无法执行语句 在 org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) 在 org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109) 在 org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95) ………… 在 com.sun.proxy.$Proxy126.persist(未知来源) 在 jvr.decrex.dao.jpa.GenericDaoJpa.insert(GenericDaoJpa.java:137) 在 jvr.decrex.dao.jpa.GenericDaoJpa$$FastClassBySpringCGLIB$$6605cd4e.invoke() 在 org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 在 org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ...省略了25个常用框架 原因:java.sql.SQLException:字段 'id' 没有默认值 在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078) ………… 在 com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2376) 在 com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2360) 在 org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204) ...省略了58个常用框架

我尝试使用旧版 mysql-connector-java5.1.27,它适用于旧版 Hibernate - 它会引发相同的错误

【问题讨论】:

您得到的确切错误信息是什么? 我在问题中添加了确切的错误和异常。谢谢 【参考方案1】:

您省略了数据库模式,因此需要进行大量猜测。以下是可以说的:-

AUTO 生成策略意味着 JPA 提供者可以选择任何它想要的作为策略。似乎对于 MySQL,它使用 AUTOINCREMENT 列(相当于 IDENTITY 生成策略),而对于 H2,它可能使用 SEQUENCE (猜测因为您没有提供详细信息)。也许您没有为 MySQL 的 PK 列定义 AUTOINCREMENT?但在这种情况下你不能使用 AUTO 策略,而你是。

您可以通过为您将部署到的每个数据存储提供一个 orm.xml 来处理它,然后您可以根据哪个数据存储使用不同的生成策略。

或者选择TABLE生成策略,它每次都会插入“id”列而不管数据存储。

或者选择IDENTITY(当您使用 MySQL AUTOINCREMENT 列作为 PK,H2 IDENTITY 列作为 PK 时)因为 H2 也会使用它(如果您还需要支持另一个没有这样的IDENTITY支持)。

【讨论】:

使用TABLE 策略会有所帮助,尽管我现在看到的是HHH90000015: Found use of deprecated [org.hibernate.id.MultipleHiLoPerTableGenerator] table-based id generator。将切换到TABLE 以确保它在下一次升级中继续工作

以上是关于如何强制 Hibernate 添加 ID 列以插入查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Hibernate 插入记录正确自动生成主键 ID

SQL - 添加列以选择并填写它[关闭]

在 Hibernate 中,如何自动检索父 id 并在子插入中将其用作外键?

如何在JPA / JAVA / Hibernate的两列中插入自动生成的ID

如何使用spring + hibernate将数据插入数据库?

hibernate + spring数据中的本机插入查询