我正在使用 Spring/Hibernate 以 JPA 方式使用 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
并使用 spring xml、persistence.xml 和 JPA 2 注释进行配置。
在功能上它很好并且正确地坚持。但是,我需要尽快存储具有大量 B 的双向 OneToMany 的实体 A。
我在 persistence.xml 中使用各种选项来尝试加快插入速度并减少内存使用(应用程序写入与读取一样多)
<property name="hibernate.id.new_generator_mappings" value="true" />
<property name="hibernate.jdbc.batch_size" value="50" />
<property name="hibernate.order_inserts" value="true" />
<property name="hibernate.order_updates" value="true" />
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
并且使用
完成持久化entityManager.persist(instanceOfA)
编辑附加信息:
每个实体都有一个生成的 id,如下所示:
@Id
@Column(name="ID")
@GeneratedValue(strategy=GenerationType.AUTO, generator="SEQUENCE_GENERATOR")
@SequenceGenerator(name="SEQUENCE_GENERATOR", sequenceName="MY_SEQUENCE", allocationSize=50)
private Long id;
与Oracle序列相关
CREATE SEQUENCE MY_SEQUENCE MINVALUE 1 MAXVALUE 999999999999999999999999999 START WITH 1 INCREMENT BY 50 NOCYCLE NOCACHE NOORDER;
当我运行带有 show sql 的代码时,我可以看到很多插入语句花费了相当长的时间。
我读到我需要调用 entityManager.flush(); entityManager.clear();
每插入 50 行。
这是否意味着我需要将 persist 分解为
entityManager.persist(instanceOfA);
instanceOfA.addB(instanceOfB);
entityManager.persist(instanceofB);
每 50 次调用 persist()
添加一次刷新清除?
有没有更简洁的方法? (我的实际对象层次结构有大约 7 层关系,如 A 和 B)
我正在考虑使用 JDBC 进行插入,但我讨厌编写行映射器:)
我听说过 org.hibernate.StatelessSession
,但没有任何方法可以从 JPA 实体管理器获取它,而无需在某些时候强制转换为 SessionFactory - 同样不是很干净。
提前致谢!
最佳答案
我在我的一个项目中遇到了同样的问题。我使用带有 identity
ID 生成器的 MySQL 后端的 Hibernate。这样做的问题是,Hibernate 需要为每个保存的实体访问数据库一次,以便为它实际获取一个 ID。我切换到 increment
生成器并看到了立竿见影的好处(所有插入都得到了批处理)。
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
@Column(name = "id", nullable = false)
private long id;
increment
生成器在内存中生成 ID,不需要访问数据库。我猜测 sequence
生成器也需要访问数据库,因为它是在数据库中定义的。使用 increment
的缺点是,Hibernate 应该具有对数据库的独占插入访问权限,并且它可能在集群设置中失败。
我使用的另一个技巧是将 rewriteBatchedStatements=true
附加到 JDBC URL。这是 MySQL 特有的,但我认为 Oracle 可能有类似的指令。
而且“每插入 n 次后调用 flush”的技巧也有效。这是一个示例代码(使用 google-guava 类):
public List<T> saveInBatches(final Iterable<? extends T> entities, final int batchSize) {
return ImmutableList.copyOf(
Iterables.concat(
Iterables.transform(
Iterables.partition(entities, batchSize),
new Function<List<? extends T>, Iterable<? extends T>>() {
@Override
public Iterable<? extends T> apply(final List<? extends T> input) {
List<T> saved = save(input); flush(); return saved;
}})));
}
public List<T> save(Iterable<? extends T> entities) {
List<T> result = new ArrayList<T>();
for (T entity : entities) {
entityManager.persist(entity);
result.add(entity);
}
return result;
}
关于performance - 如何在使用 Spring EntityManager Hibernate 持久化大型集合时提高性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12687836/