让我们说我想建模一个问题跟踪器,我的模型由两个实体组成:Repository和Issue。存储库可能包含几个问题。所以你得到大约以下的JPA类:
@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
}
现在我想要以类似自动增量的方式生成问题编号,然后在存储库本地生成。我看到了各种选择,但我不确定什么是最好的。
不使用任何自动生成,只需在持久化之前设置值。因此:查询存储库的问题,计算最大发行号(如果有),增量,设置和持久。这感觉有点容易出错,因为如果你要从代码的其他部分实例化问题,你需要牢记这一点。
Issue issue = new Issue();
issue.setRepository(repository);
// For example, assuming this is right for the context now:
issue.setIssueNumber(repository.getIssues().size() + 1);
(显然,可以对此进行重构,以便对此问题编号生成进行重复数据删除,但这不会阻止null
问题persisted
上的问题EntityManger
使用JPA生命周期事件,挂钩@PrePersist
并在那里做同样的事情。这样做的好处是可以自动调用它,而不是在代码库中复制。
// On the issue entity
@PrePersist
void setIssueNumberOnPersist() {
if(getIssueNumber() == null) {
setIssueNumber(getRepository().getIssues().size() + 1);
}
}
然而,它似乎与JPA限制之一相冲突:
为避免与触发实体生命周期事件(仍在进行中)的原始数据库操作发生冲突,回调方法不应调用EntityManager或Query方法,也不应访问任何其他实体对象。
通过问题表上的触发器设置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
IdentifierGenerator
似乎IdentifierGenerator
也可以做这项工作。但是,它需要通过查询与数据库进行交互,我认为这会破坏与不同数据库和架构更改的兼容性。
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;
}
}
如果我错过了一个选项,我很高兴听到!
什么是最佳选择?
答案 0 :(得分:1)
另一种选择是缓存附加到存储库的问题数量。
因此,您创建一个字段issueCount
,它将在构造函数中初始化,方法是从数据库中提取数量,然后在创建问题时相应地递增。
请记住,这必须同步,因为您不希望最终遇到具有相同ID的2个问题。
根据各种stackoverflow问题和论坛帖子,这似乎是一个已知问题,Hibernate建议开发人员在他们的项目中创建逻辑,而不是依赖于Hibernate的@GeneratedValue
。