【问题标题】:Spring Data JDBC with Azure SQL: After saving the identifier must not be null带有 Azure SQL 的 Spring Data JDBC:保存标识符后不能为空
【发布时间】:2021-02-04 01:22:43
【问题描述】:

您能帮我解决以下错误吗?我将 spring-data-jdbc 与 Liquibase 和 Azure SQL 数据库一起使用。当我想使用 UserRepository.save

将对象保存到数据库时,我得到了这个

java.lang.IllegalArgumentException: 保存后标识符不能为空! 在 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>

【问题讨论】:

  • 好像是id栏的问题。

标签: java sql-server spring-boot azure-sql-database spring-data-jdbc


【解决方案1】:

如果我理解您的 Liquibase 配置正确,它会设置一个序列并将 id 的默认值设置为该序列的 next 值。

这原则上应该可行,因为 Spring Data JDBC 在插入时不包含 null id,假设它将由数据库生成并由 JDBC 驱动程序返回。

最后一部分可能是出了问题的地方。 异常基本上是在将数据插入数据库并使用 JDBC 驱动程序返回的任何内容来更新 id 之后,id 仍然是 null(如果使用原始数字类型,则为 0)。 所以可能驱动程序没有返回序列生成的id。

为了解决问题,您有以下选择:

  1. 不要使用序列,而是在客户端创建 id,例如使用 UUID。这样做的好处是它很容易实现,应该可以很好地扩展。如果在实体实例化时创建 id,也可以在持久化实体之前引用它。

  2. 使用单独的查询从序列中选择值并使用它来设置 id。这可能最好在BeforeSaveCallback 中完成,但也可能发生在创建实体的工厂方法中。这涉及更多一些,并且需要额外的数据库往返。这可以通过实施一种算法来缓解,在该算法中,您从序列中选择值将其乘以 50,然后在本地生成 50 个 id。基本上就是Hibernate的HiLo算法。

  3. 教 Spring Data JDBC 如何从数据库中获取 id。这可能有多种形式,并且取决于我没有的 Azure 知识。如果 Azure 有 SERIALAUTOINCREMENT 字段,则可能会使用它来代替序列,并且它应该由 JDBC 驱动程序返回。

    当您正在使用的Dialect 中使用不同的IdGeneration 时,它可能会起作用。或者也许可以实现一个新的IdGeneration。这显然是相当复杂的,并且需要 Spring Data JDBC 的全新版本(或补丁版本)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-21
    • 1970-01-01
    • 2018-10-26
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    • 2020-06-15
    • 1970-01-01
    相关资源
    最近更新 更多