休眠-插入速度非常慢

时间:2018-08-14 11:37:05

标签: java spring hibernate

因此,我尝试使用Hibernate插入30000条记录(最终的解决方案是插入300000条或更多记录)。 我的问题是插入300条记录花费了8-9秒,这非常慢。该数据库是Oracle。我尝试了批处理,但是没有加快速度。

休眠配置:

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    ...
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.max_fetch_depth">0</prop>
            <prop key="hibernate.cache.use_query_cache">false</prop>
            <prop key="hibernate.current_session_context_class">ch.nevis.estivate.util.TransactionAwareSessionContext</prop>
            <prop key="javax.persistence.validation.mode">none</prop>
            <prop key="hibernate.validator.autoregister_listeners">false</prop>
            <prop key="hibernate.validator.apply_to_ddl">false</prop>
            <prop key="hibernate.cache.use_second_level_cache">false</prop>

            <prop key="hibernate.jdbc.batch_size">30</prop>
            <prop key="hibernate.order_inserts">true</prop>
            <prop key="hibernate.generate_statistics">true</prop>
            <prop key="hibernate.show_sql">false</prop>
        </props>
    </property>
</bean>

Java:

@Transactional
public class DataGenerator {

public void generate(int nr) {
    System.out.println("START   " + new Date());
    int q = nr / 100;
    Role role = (Role) sessionFactory.getCurrentSession().get(Role.class, new Long(4));
    Client client = (Client) sessionFactory.getCurrentSession().get(Client.class, new Long(100));
    Unit unit = (Unit) sessionFactory.getCurrentSession().get(Unit.class, new Long(100));
    TemplateCollection templateCollection = (TemplateCollection) sessionFactory.getCurrentSession().get(TemplateCollection.class, new Long(100));
    for(int i = 0; i < nr; i++) {
        User user = generateUser(i, client, templateCollection);
        sessionFactory.getCurrentSession().save(user);

        Profile profile = generateProfile(i, user, unit, client);
        sessionFactory.getCurrentSession().save(profile);

        Authorization authorization = generateAuthorization(i, profile, role);
        sessionFactory.getCurrentSession().save(authorization);

        if (i % q == 0) {
            System.out.println(i/q + " %     " + new Date());
        }
        if (i % 10 == 0) {  // the batch_size is 30 but I save 3 entities in one go
            sessionFactory.getCurrentSession().flush();
            sessionFactory.getCurrentSession().clear();
        }
    }
    System.out.println("END   " + new Date());
}

请问有什么提示吗?
也许Hibernate不适合加载许多记录?

谢谢,
V。

------------更新-------------
删除了自定义会话上下文类(即正在使用Spring的CurrentSessionContext),但没有提高速度。

------------更新2 -------------

public static void main(String[] args) {
    ApplicationContext ctx = new FileSystemXmlApplicationContext("file:/......./applicationContext-testDataGenerator.xml");
    DataGenerator dataGenerator= ctx.getBean("dataGenerator", DataGenerator.class);
    dataGenerator.generate(10000);
}

5 个答案:

答案 0 :(得分:3)

我认为您应该将冲洗部分移到循环之外,并且只执行一次。

这将加快您的代码。

batch_size 只是告诉Hibernate将多少个SQL命令组合在一起。

也许可以增加 batch_size 来加快插入阶段。

答案 1 :(得分:1)

如果需要加快插入许多记录的速度,则应尝试使用本机查询插入jdbcTemplate,以进行实体转换,从而避免了查询转换。还要批量执行(假设有1000次)

答案 2 :(得分:0)

您应该用@Transactional注释方法,以便在所有方法流中重用同一会话,否则sessionFactory将为每次getCurrentSession()调用建立一个新会话。

[编辑]

确保generate方法是从外部调用的。

创建一个Main类,使用spring向其注入DataGenerator的实例,并在注入的实例上调用generate()。这样,将应用spring transactionInterceptor并正确处理会话实例

答案 3 :(得分:0)

如前所述,在保存(甚至获取)如此大量的数据时使用休眠模式不是一个好主意(如果您处理的数据非常大,则有可能以OutOfMem结尾,因为休眠会在上下文中存储对象的多个副本,直到您的会话有效并且GC无法清除它们为止),但是如果您确实需要它,则最好使用无状态会话,这样可以防止您创建大型对象大量无用的物体。

另外,请确保您从外部调用generate()方法,如果您从同一类@Transactional调用该方法将无效。

答案 4 :(得分:0)

如果仍然很慢,请尝试执行以下操作:

List<User> users = new ArrayList<>();
List<Profile> profiles = new ArrayList();
List<Authorization> authorizations = new ArrayList<>();

for(int i = 0; i < nr; i++) {
  users.add(generateUser(i, client, templateCollection));
  profiles.add(generateProfile(i, user, unit, client));
  authorizations.add(generateAuthorization(i, profile, role));
}

users.forEach(sessionFactory.getCurrentSession()::save);
profiles.forEach(sessionFactory.getCurrentSession()::save);
authorizations.forEach(sessionFactory.getCurrentSession()::save);

不确定是否会有所帮助,但可能会有所帮助。在尝试将批处理插入多个表时,我得到了奇怪的结果(我相信,在很多情况下,像您一样执行插入操作时,批处理插入是不起作用的,例如,将entity1插入表A,将entity2插入表B,将entity3插入表A ,依此类推...,但不要在此引用我的意思