问题描述
您能帮我解决以下错误吗?我正在将spring-data-jdbc与Liquibase和Azure sql数据库一起使用。我想通过 UserRepository.save
将对象保存到数据库时得到此信息java.lang.IllegalArgumentException:保存后,标识符不能为null! 在org.springframework.util.Assert.notNull(Assert.java:201) 在org.springframework.data.jdbc.core.JdbcAggregateTemplate.store(JdbcAggregateTemplate.java:343) 在org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:149) 在org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.save(SimpleJdbcRepository.java:55)
实体:
@Table("user_details")
public class UserDetails {
@Id
Long id;
@Column("email_address")
String emailAddress;
@Column("first_name")
String firstName;
@Column("last_name")
String lastName;
}
存储库:
@Repository
public interface UserRepository extends CrudRepository<UserDetails,Long> {
}
Liquibase:
<changeSet id="0001_user_details">
<createSequence sequenceName="user_details_seq" startValue="1"/>
<createTable tableName="user_details">
<column name="id" type="BIGINT" defaultValueSequenceNext="user_details_seq">
<constraints nullable="false"/>
</column>
<column name="email_address" type="VARCHAR(1024)">
<constraints nullable="true"/>
</column>
<column name="first_name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="last_name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
</createTable>
<addPrimaryKey tableName="user_details" columnNames="id"
constraintName="user_details_pk"/>
</changeSet>
生成的架构:
create sequence user_details_seq
go
create table user_details
(
id bigint
constraint DF_user_details_id default NEXT VALUE FOR [user_details_seq] not null
constraint user_details_pk
primary key,email_address varchar(1024),first_name varchar(255),last_name varchar(255),)
go
行家:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>8.4.1.jre14</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.9</version>
</dependency>
解决方法
如果我理解您的Liquibase配置正确,它将设置一个序列并将id的默认值设置为该序列的next
值。
这在原则上应该可行,因为Spring Data JDBC在插入中不包含null
id,并假定它将由数据库生成并由JDBC驱动程序返回。
最后一部分可能是哪里出了问题。
异常基本上说,在将数据插入数据库中并使用返回的JDBC驱动程序更新ID后,该ID仍为null
(如果使用原始数字类型,则为0
)。
因此,驱动程序可能不会返回该序列生成的ID。
为了解决该问题,您可以使用以下选项:
-
不使用序列,而是在客户端(例如,使用UUID)创建ID。这样做的好处是易于实施,可以很好地扩展。如果您是在实体实例化时创建ID的,则还可以在持久保存实体之前引用它。
-
使用单独的查询从序列中选择值,并使用其设置ID。这可能最好在
BeforeSaveCallback
中完成,但是也可以在创建实体的工厂方法中发生。这涉及更多,并且需要额外的数据库往返。可以通过实施一种算法来缓解这种情况,该算法是从序列中选择值乘以50,然后从本地生成50个ID。基本上是Hibernate的HiLo算法。 -
教Spring Data JDBC如何从数据库获取ID。这可能采用多种形式,并且取决于我所没有的Azure知识。如果Azure有一个
SERIAL
或AUTOINCREMENT
字段,则可以使用它代替序列,并且应该由JDBC驱动程序返回。也许在您使用的
IdGeneration
中使用其他Dialect
时有效。也许可以实施新的IdGeneration
。显然,这很复杂,并且需要Spring Data JDBC的全新发行版(或修补版本)。