如何避免 Spring Data JPA/Hibernate-generated 数据库中的 ID 冲突?
How to avoid id collisions in Spring Data JPA/Hibernate-generated database?
我们使用 dockerized postgres 数据库并让休眠自动生成 tables(使用 spring.jpa.hibernate.ddl-auto: create
)用于我们的集成测试。使用像 H2 这样的东西不是一种选择,因为我们在一些地方做了一些特定于数据库的操作,例如本机 SQL 查询。
当所有实体都使用自动递增的 ID 时,有什么方法可以避免 ID 冲突?是通过偏移起始 ID,还是更好,让所有 table 都使用共享序列?
架构在 docker 容器启动时创建,table 由 Spring 数据创建 JPA/Hibernate
例子
示例使用 kotlin 语法并假定“allopen”插件应用于实体。
有时我们会遇到使用错误外键的错误,例如像这样:
@Entity
class EntityOne(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
@Entity
class EntityTwo(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
@Entity
class JoinEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
@ManyToOne
@JoinColumn(name = "entity_one_id")
var entityOne: EntityOne,
@ManyToOne
@JoinColumn(name = "entity_two_id")
var entityTwo: EntityTwo,
)
@Repository
interface JoinEntityRepository : JpaRepository<JoinEntity, Long> {
//
// Bug here! Should be "WHERE entityOne.id = :entityOneId"
//
@Query("SELECT entityTwo FROM JoinEntity WHERE entityTwo.id = :entityOneId")
fun findEntityTwoByEntityOneId(entityOneId: Long): Collection<EntityTwo>
}
这些错误在某些情况下很难发现,因为在创建 table 时,很可能存在与某个 Entity1 具有相同 ID 的 Entity2,因此查询成功但测试在线某处失败,因为当它返回一个或多个 Entity2 时,它不是预期的。
更糟糕的是,根据测试的范围,即使获取了错误的实体,它也可能会通过,或者仅当测试 运行 处于特定顺序时才会失败(由于 ids 变得“不同步” ”)。所以理想情况下,当传递错误的 id 时,它甚至应该找不到实体。但是因为数据库结构是从头开始创建的,而且 id 是自动递增的,所以它们总是从 1 开始。
我找到了解决方法。
在我的 resources/application.yml
中(在测试文件夹中,您很可能 不想 在主文件夹中这样做)我添加 spring.datasource.initialization-mode: always
和文件 data.sql
.
data.sql内容如下:
DROP SEQUENCE IF EXISTS test_shared_sequence;
CREATE SEQUENCE test_shared_sequence;
ALTER TABLE entity_one ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
ALTER TABLE entity_two ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
在 Spring 自动生成 table 之后(使用 spring.jpa.hibernate.ddl-auto: create
)它将 运行 此脚本中的任何内容,脚本将更改所有 tables 根据相同的序列自动生成 id,这意味着无论两个实体存储在哪个 table 中,都不会有相同的 id,因此在错误的 table 将始终失败。
我们使用 dockerized postgres 数据库并让休眠自动生成 tables(使用 spring.jpa.hibernate.ddl-auto: create
)用于我们的集成测试。使用像 H2 这样的东西不是一种选择,因为我们在一些地方做了一些特定于数据库的操作,例如本机 SQL 查询。
当所有实体都使用自动递增的 ID 时,有什么方法可以避免 ID 冲突?是通过偏移起始 ID,还是更好,让所有 table 都使用共享序列?
架构在 docker 容器启动时创建,table 由 Spring 数据创建 JPA/Hibernate
例子
示例使用 kotlin 语法并假定“allopen”插件应用于实体。
有时我们会遇到使用错误外键的错误,例如像这样:
@Entity
class EntityOne(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
@Entity
class EntityTwo(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
@Entity
class JoinEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
@ManyToOne
@JoinColumn(name = "entity_one_id")
var entityOne: EntityOne,
@ManyToOne
@JoinColumn(name = "entity_two_id")
var entityTwo: EntityTwo,
)
@Repository
interface JoinEntityRepository : JpaRepository<JoinEntity, Long> {
//
// Bug here! Should be "WHERE entityOne.id = :entityOneId"
//
@Query("SELECT entityTwo FROM JoinEntity WHERE entityTwo.id = :entityOneId")
fun findEntityTwoByEntityOneId(entityOneId: Long): Collection<EntityTwo>
}
这些错误在某些情况下很难发现,因为在创建 table 时,很可能存在与某个 Entity1 具有相同 ID 的 Entity2,因此查询成功但测试在线某处失败,因为当它返回一个或多个 Entity2 时,它不是预期的。
更糟糕的是,根据测试的范围,即使获取了错误的实体,它也可能会通过,或者仅当测试 运行 处于特定顺序时才会失败(由于 ids 变得“不同步” ”)。所以理想情况下,当传递错误的 id 时,它甚至应该找不到实体。但是因为数据库结构是从头开始创建的,而且 id 是自动递增的,所以它们总是从 1 开始。
我找到了解决方法。
在我的 resources/application.yml
中(在测试文件夹中,您很可能 不想 在主文件夹中这样做)我添加 spring.datasource.initialization-mode: always
和文件 data.sql
.
data.sql内容如下:
DROP SEQUENCE IF EXISTS test_shared_sequence;
CREATE SEQUENCE test_shared_sequence;
ALTER TABLE entity_one ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
ALTER TABLE entity_two ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
在 Spring 自动生成 table 之后(使用 spring.jpa.hibernate.ddl-auto: create
)它将 运行 此脚本中的任何内容,脚本将更改所有 tables 根据相同的序列自动生成 id,这意味着无论两个实体存储在哪个 table 中,都不会有相同的 id,因此在错误的 table 将始终失败。