performance - 如何在使用 Spring EntityManager Hibernate 持久化大型集合时提高性能

标签 performance spring hibernate jdbc persistence.xml

我正在使用 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 行。

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

这是否意味着我需要将 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/

相关文章:

java - 在jpa中保存操作期间将外键列设置为null

c++ - 像 C++ 一样编写高效的实体系统

algorithm - 查找具有相似产品的类别-低效率-转到

hibernate - Ubuntu 18.04 更新 hibernate 挂起

hibernate - 基于独立条件的 createAlias (GORM)

hibernate - Spring 配置错误

performance - Google Analytics 消除了我创建无 cookie 域的努力

Python:为什么这个列表理解比等效的 for 循环慢 5000 倍?

java - 升级到 Java 7 后 Spring (3.2.0) ConfigurationClassEnhancer 中出现 Stackoverflow 错误

spring - 如何在 Spock 测试中将 @Value 注释与 Spring PropertyPlaceholderConfigurer 一起使用?