【发布时间】:2021-10-24 21:29:57
【问题描述】:
我们使用 dockerized postgres 数据库并让 hibernate 为我们的集成测试自动生成表(使用 spring.jpa.hibernate.ddl-auto: create)。使用 H2 之类的东西不是一种选择,因为我们在一些地方进行了一些特定于数据库的操作,例如本机 SQL 查询。
当所有实体都使用自动递增的 id 时,有什么方法可以避免 id 冲突?通过偏移起始 id 或者更好的是让所有表使用共享序列?
Schema 是在 docker 容器启动时创建的,表是由 Spring Data 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>
}
在某些情况下很难找到这些错误,因为在创建表时,很可能有一个 Entity2 与某个 Entity1 具有相同的 id,因此查询成功但测试失败了,因为虽然它返回一个或多个 Entity2,但它不是预期的。
更糟糕的是,根据测试的范围,即使获取了错误的实体,它也可能通过,或者仅当测试以特定顺序运行时才会失败(由于 id 变得“不同步”)。所以理想情况下,当传递错误的 id 时,它甚至应该找不到实体。但是因为数据库结构是从头开始创建的,并且 id 是自动递增的,所以它们总是从 1 开始。
【问题讨论】:
-
错误是不是生成的 id 都以 1 开头。有很多,也许是大多数(?)系统有 100 多个表,id 就是这样生成的。该错误存在于您用于获取 id 的代码中。使用单个序列不会解决该问题,并且会降低性能,除非您使用较大的 CACHE 值。
-
我只是在谈论在测试中使用单个序列,以便更容易地发现错误。生产数据库仍然会以这种方式做事。这不是万无一失的解决方案,但它可以解决某些错误仅在测试按一定顺序运行时才会出现的问题,从而减少运行测试所花费的时间。
标签: postgresql hibernate spring-data-jpa spring-data