2

我正在使用 Spring/Hibernate 完成 JPA 方式,org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean并使用 spring xml、persistence.xml 和 JPA 2 注释进行配置。

从功能上讲,它很好并且可以正确地持久化。但是,我需要尽快存储具有双向 OneToMany 和大量 B 集合的实体 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 行调用一次。

http://abramsm.wordpress.com/2008/04/23/hibernate-batch-processing-why-you-may-not-be-using-it-even-if-you-think-you-are/

这是否意味着我需要将坚持分解为

entityManager.persist(instanceOfA);
instanceOfA.addB(instanceOfB);
entityManager.persist(instanceofB);

每 50 次调用添加一次刷新清除persist()?

有更清洁的方法吗?(我的实际对象层次结构有大约 7 层关系,如 A 和 B)

我正在考虑使用 JDBC 进行插入,但我讨厌编写行映射器 :)

我听说过,org.hibernate.StatelessSession但没有任何方法可以从 JPA 实体管理器中获取它,而无需在某些时候强制转换为 SessionFactory - 再次不是很干净。

提前致谢!

4

2 回答 2

4

我在我的一个项目中遇到了同样的问题。我正在使用带有identityID 生成器的 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 次插入后调用刷新”技巧也有效。这是一个示例代码(使用 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;
}
于 2012-10-02T10:00:30.203 回答
2

使用纯 JDBC 进行批量/大型插入。不要为此使用任何 ORM 框架。

于 2012-10-04T04:29:28.333 回答