提问人:Cloud 提问时间:7/11/2023 更新时间:7/11/2023 访问量:125
使用瞬态对象指定用于保存 ManyToOne 关系的 ID
Using transient objects to specify ids for saving ManyToOne relations
问:
我想通过将大量Spring JPA实体批量导入数据库来保存它们。 某些实体与其他实体具有 ManyToOne 关系。 由于存在大量数据,我不希望跟踪内存中所有相关实体,以便将其设置为其他实体的 ManyToOne 关系。我只有关系的 id,而不是持久化的实体。
无论如何,我都遇到过几次以下建议作为设置关系的解决方案:
{
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
...
public void setAuthorId(Long authorId) {
Author author = new Author();
author.setId(authorId);
this.author = author;
}
因此,使用瞬态对象作为占位符,将一个实体与另一个实体相关联。(假设相关对象被保存为传递给 saveAll() 调用的其他对象之一)
我在官方 Spring 文档中根本没有看到这种方法的参考。
这是否被认为是仅基于 id 保存关系的受支持方式,还是您说这只是一个肮脏的黑客?
答:
使用瞬态对象作为占位符
我的团队在生产中使用这种方法,经常使用 Hibernate(没有 Spring Data)。没有任何问题。
缺点
Spring-data 使用内部存储库方法。Hibernate 将生成额外的(不必要的)数据库查询,以更新瞬态对象状态。merge()
save()
SELECT
您可以将自定义存储库与方法一起使用,以避免额外的查询。描述如下:persist()/update()
SELECT
使用 getReferenceById() (自 2.7 起) / getReference() 方法
我们经常使用这种方法。如果获取引用并使用相同的持久性上下文(范围)保存实体,则不会导致任何其他数据库查询。@Transactional
缺点
我们需要一个存储库来使用方法。例如,不能在实体的瞬态实用方法中使用此方法,如 .getReferenceById()
setAuthorId()
使用无状态会话
Hibernate 的 StatelessSession – 它是什么以及如何使用它
在 JdbcTemplate 中使用自定义存储库
要插入大量记录,最好将低级批处理与 SQL 一起使用。我们在生产中经常使用这种方法。如果您使用 MySQL,请不要忘记添加到连接 URL。rewriteBatchedStatements=true
@Repository
class NumbersRepositoryImpl implements NumbersRepositoryCustom {
private static final int BATCH_SIZE = 1000;
@PersistenceContext
private EntityManager entityManager;
private final JdbcTemplate template;
@Autowired
public NumbersRepositoryImpl(JdbcTemplate template) {
this.template = template;
}
@Override
public void batchSave(List<Number> numbers) {
final String sql = "INSERT INTO NUMBERS " +
"(number) " +
"VALUES (?)";
template.execute(sql, (PreparedStatementCallback<Void>) ps -> {
int recordsCount = 0;
for (Number number : numbers) {
ps.setString(1, number.getValue());
ps.addBatch();
recordsCount++;
if (recordsCount % BATCH_SIZE == 0) {
ps.executeBatch();
}
}
ps.executeBatch();
return null;
});
}
}
评论