在从 class 生成的模式和从文件加载的初始数据之后插入新条目时出现 JdbcSQLIntegrityConstraintViolationException
JdbcSQLIntegrityConstraintViolationException when inserting new entry after schema generated from class and initial data loaded from file
我有一个带有 h2 数据库的 spring 引导项目。
我有一个实体 class 从中生成模式:
@NoArgsConstructor
@Entity
@Table(name = "NAMES")
public class Name {
@Id
@GeneratedValue
public Long id;
@Column(nullable = false)
public String name;
public Name(String name) {
this.name = name;
}
}
我有一个 data.sql
文件:
insert into names (id, name) values (1, 'Alex');
insert into names (id, name) values (2, 'Bob');
我的 application.properties 是:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
应用程序启动正常,我可以通过 localhost:8080/h2-console 确认数据已加载到数据库中。
但是我无法将新数据保存到 table with
//public interface NameRepository extends CrudRepository<Name,Long> {}
@RestController
@Service
public class MyController {
@Autowired
private final NameRepository nameRepository;
@PostMapping("/triggerError")
public ResponseEntity<Void> trigger() {
Name newName = new Name("Chris");
nameRepository.save(newName);
return ResponseEntity.ok().build();
}
}
错误信息是:
could not execute statement; SQL [n/a]; constraint [\"PRIMARY KEY ON PUBLIC.NAMES(ID) ( /* key:1 */ CAST(1 AS BIGINT), 'Alex')\"; SQL statement:
insert into names (name, id) values (?, ?) [23505-210]];
我假设这意味着 spring 想要在 id=1 处插入新名称,但没有意识到 id 1 和 2 已经在使用中。我想 @GeneratedValue
的正确参数可以修复它,但我不明白它们的含义以及选择哪个参数。
反复试验:
@GeneratedValue(strategy = GenerationType.AUTO)
是默认值,见上文。
@GeneratedValue(strategy = GenerationType.TABLE)
同样的错误
@GeneratedValue(strategy = GenerationType.SEQUENCE)
同样的错误
@GeneratedValue(strategy = GenerationType.IDENTITY)
不同的错误:
...
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column \"ID\"; SQL statement:\ninsert into names (id, name) values (null, ?) [23502-210]
...
could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
所以显然不是注释,而是其他东西。
我要放弃了,这是我的 MRE:https://github.com/timo-a/duckpond-spring-backend/tree/debug/saving
这是 hibernate 上的一个错误,特定于 h2 数据库。
https://hibernate.atlassian.net/browse/HHH-14985
您可以使用 id BIGINT GENERATED BY DEFAULT AS IDENTITY DEFAULT ON NULL PRIMARY KEY
列定义作为修复。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "id BIGINT GENERATED BY DEFAULT AS IDENTITY DEFAULT ON NULL PRIMARY KEY")
private Long id;
@Shankar Ghimire 部分正确。这既是 Hibernate 错误也是 data.sql 问题。将您的 Hibernate 版本更新到 5.6.5:
implementation('org.springframework.boot:spring-boot-starter-data-jpa'){
exclude group: 'org.hibernate', module: 'hibernate-core'
}
implementation 'org.hibernate:hibernate-core:5.6.5.Final'
并且由于您使用的是 h2 auto_increment,因此您应该删除 data.sql
中的 ID
insert into names (name)
values ('Alex'),('Bob');
序列不会递增,否则会导致唯一索引或主键冲突。
顺便说一下,您的存储库使用 Integer 作为您的主键,而您的实体使用 Long。由于问题被标记为 JPA,因此使用 :
public interface NameRepository extends JpaRepository<Name,Long> {}
你在一个名为“scores”的 table 中插入数据,但我认为这是一个打字错误。
我有一个带有 h2 数据库的 spring 引导项目。
我有一个实体 class 从中生成模式:
@NoArgsConstructor
@Entity
@Table(name = "NAMES")
public class Name {
@Id
@GeneratedValue
public Long id;
@Column(nullable = false)
public String name;
public Name(String name) {
this.name = name;
}
}
我有一个 data.sql
文件:
insert into names (id, name) values (1, 'Alex');
insert into names (id, name) values (2, 'Bob');
我的 application.properties 是:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
应用程序启动正常,我可以通过 localhost:8080/h2-console 确认数据已加载到数据库中。 但是我无法将新数据保存到 table with
//public interface NameRepository extends CrudRepository<Name,Long> {}
@RestController
@Service
public class MyController {
@Autowired
private final NameRepository nameRepository;
@PostMapping("/triggerError")
public ResponseEntity<Void> trigger() {
Name newName = new Name("Chris");
nameRepository.save(newName);
return ResponseEntity.ok().build();
}
}
错误信息是:
could not execute statement; SQL [n/a]; constraint [\"PRIMARY KEY ON PUBLIC.NAMES(ID) ( /* key:1 */ CAST(1 AS BIGINT), 'Alex')\"; SQL statement:
insert into names (name, id) values (?, ?) [23505-210]];
我假设这意味着 spring 想要在 id=1 处插入新名称,但没有意识到 id 1 和 2 已经在使用中。我想 @GeneratedValue
的正确参数可以修复它,但我不明白它们的含义以及选择哪个参数。
反复试验:
@GeneratedValue(strategy = GenerationType.AUTO)
是默认值,见上文。
@GeneratedValue(strategy = GenerationType.TABLE)
同样的错误
@GeneratedValue(strategy = GenerationType.SEQUENCE)
同样的错误
@GeneratedValue(strategy = GenerationType.IDENTITY)
不同的错误:
...
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column \"ID\"; SQL statement:\ninsert into names (id, name) values (null, ?) [23502-210]
...
could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
所以显然不是注释,而是其他东西。
我要放弃了,这是我的 MRE:https://github.com/timo-a/duckpond-spring-backend/tree/debug/saving
这是 hibernate 上的一个错误,特定于 h2 数据库。 https://hibernate.atlassian.net/browse/HHH-14985
您可以使用 id BIGINT GENERATED BY DEFAULT AS IDENTITY DEFAULT ON NULL PRIMARY KEY
列定义作为修复。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "id BIGINT GENERATED BY DEFAULT AS IDENTITY DEFAULT ON NULL PRIMARY KEY")
private Long id;
@Shankar Ghimire 部分正确。这既是 Hibernate 错误也是 data.sql 问题。将您的 Hibernate 版本更新到 5.6.5:
implementation('org.springframework.boot:spring-boot-starter-data-jpa'){
exclude group: 'org.hibernate', module: 'hibernate-core'
}
implementation 'org.hibernate:hibernate-core:5.6.5.Final'
并且由于您使用的是 h2 auto_increment,因此您应该删除 data.sql
中的 IDinsert into names (name)
values ('Alex'),('Bob');
序列不会递增,否则会导致唯一索引或主键冲突。
顺便说一下,您的存储库使用 Integer 作为您的主键,而您的实体使用 Long。由于问题被标记为 JPA,因此使用 :
public interface NameRepository extends JpaRepository<Name,Long> {}
你在一个名为“scores”的 table 中插入数据,但我认为这是一个打字错误。