Hibernate 复合键的JPA自动增量
假设我想建模一个问题跟踪者,我的模型由两个实体组成:存储库和问题。存储库可能包含几个问题。因此,您可以大致获得以下JPA类:Hibernate 复合键的JPA自动增量,hibernate,postgresql,jpa,Hibernate,Postgresql,Jpa,假设我想建模一个问题跟踪者,我的模型由两个实体组成:存储库和问题。存储库可能包含几个问题。因此,您可以大致获得以下JPA类: @Entity @Table(name="repository") public class Repository { @Id @GeneratedValue private Integer id; private String name; // Getters and setters } 对于问题类: @Entity @
@Entity
@Table(name="repository")
public class Repository {
@Id
@GeneratedValue
private Integer id;
private String name;
// Getters and setters
}
对于问题类:
@Entity
@Table(name="issue")
@IdClass(Issue.IssuePk)
public class Issue {
static class IssuePk implements Serializable {
private Repository repository;
private Integer issueNumber;
// Getters, setters, equals and hashcode
}
@Id
@ManyToOne
private Repository repository;
@Id
private Integer issueNumber;
// Getters and setters
}
现在,我希望以类似于自动递增的方式生成问题编号,但随后生成存储库的本地编号。我看到了各种各样的选择,但我不确定什么是最好的
在实例化问题时手动计算Id
不使用任何自动生成,只是在持久化之前设置值。因此:查询存储库中的问题,计算最大问题数量(如果有),增加、设置并持久化。这让人感觉有点容易出错,因为若要实例化代码其他部分的问题,就需要记住这一点
Issue issue = new Issue();
issue.setRepository(repository);
// For example, assuming this is right for the context now:
issue.setIssueNumber(repository.getIssues().size() + 1);
(显然,可以重构此问题以消除此问题编号生成的重复数据,但这并不能防止null
issueNumber问题在EntityManger上被持久化
)
JPA生命周期事件
使用JPA生命周期事件,挂接@PrePersist
并在那里执行相同的操作。这样做的好处是它是自动调用的,并且不必在代码库中重复
// On the issue entity
@PrePersist
void setIssueNumberOnPersist() {
if(getIssueNumber() == null) {
setIssueNumber(getRepository().getIssues().size() + 1);
}
}
然而,这似乎与JPA的一项限制相冲突:
为避免与触发实体生命周期事件(仍在进行中)的原始数据库操作发生冲突,回调方法不应调用EntityManager或查询方法,也不应访问任何其他实体对象
使用数据库触发器
通过issue表上的触发器设置issueId值,并让JPA在插入后更新其值。这种方法的一个缺点是,如果切换数据库,必须修补此触发器
我现在不太喜欢编写触发器,但我认为大致应该是:
before insert on issue
select max(issue_id) as val from issue where repository = issue.repository
issue_id = val + 1
end
或者使用缓存的值:
before insert on issue
select next_issue_id as val from repository where id = issue.repository_id
issue_id = val
update repository set next_issue_id = val + 1 where id = issue.repository_id
end
使用HibernateIdentifierGenerator
似乎标识生成器也可以完成此工作。然而,它需要通过查询与数据库交互,我认为这会破坏与不同数据库和模式更改的兼容性
public class IssueIdGenerator implements IdentifierGenerator {
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Connection connection = session.connection();
try {
PreparedStatement ps = connection
.prepareStatement("... query ...");
// Calculate the next issueId
return issueId;
} catch (SQLException e) {
log.error(e);
throw new HibernateException(
"Unable to generate Stock Code Sequence");
}
return null;
}
}
我错过了一个选择
如果我错过了一个选择,我很高兴听到
最佳选项是什么?另一个选项是缓存附加到存储库的问题数量
因此,您可以创建一个字段issueCount
,该字段将通过从数据库获取金额在构造函数中初始化,然后在创建问题时相应地递增
请记住,这必须同步,因为您不希望以相同的id出现两个问题
根据各种stackoverflow问题和论坛帖子,这似乎是一个已知的问题,Hibernate建议开发人员在他们的项目中创建逻辑,而不是依赖Hibernate的@GeneratedValue