Hibernate更快地创建EntityManagerFactory

use*_*011 24 java sql orm hibernate jpa

在我的桌面应用程序中,新数据库经常打开.我使用Hibernate/ JPA作为ORM.问题是,创建EntityManagerFactory速度相当慢,在快速机器上花费大约5-6秒.我知道它EntityManagerFactory应该是重量级的,但对于用户希望快速打开新数据库的桌面应用程序来说这太慢了.

  1. 我可以关闭一些EntityManagerFactory功能来更快地获取实例吗?或者是否可以懒惰地创建一些EntityManagerFactory来加速cration?

  2. 在知道数据库url之前,我可以以某种方式创建EntityManagerFactory对象吗?我很乐意关闭所有可能的验证.

  3. 通过这样做,我可以集合EntityManagerFactorys供以后使用吗?

  4. 任何其他想法如何更快地创建EntityManagerFactory?

更新更多信息和JProfiler分析

桌面应用程序可以打开保存的文件.我们的应用程序文档文件格式包含1个SQLite数据库+以及ZIP文件中的一些二进制数据.打开文档时,将解压缩ZIP并使用Hibernate打开数据库.数据库都具有相同的模式,但显然有不同的数据.

看来我第一次打开文件时需要比以下时间长得多.我用JProfiler描述了第一次和第二次运行并比较了结果.

第一轮:

create EMF: 4385ms
    build EMF: 3090ms
    EJB3Configuration configure: 900ms
    EJB3Configuration <clinit>: 380ms
Run Code Online (Sandbox Code Playgroud)

calltree1.png.

第二轮:

create EMF: 1275ms
    build EMF: 970ms
    EJB3Configuration configure: 305ms
    EJB3Configuration <clinit>: not visible, probably 0ms
Run Code Online (Sandbox Code Playgroud)

compare_calltree.png.

在调用树比较中,您可以看到某些方法明显更快(DatabaseManager.作为起点):

create EMF: -3120ms
    Hibernate create EMF: -3110ms
        EJB3Configuration configure: -595ms
        EJB3Configuration <clinit>: -380ms
        build EMF: -2120ms
            buildSessionFactory: -1945ms
                secondPassCompile: -425ms
                buildSettings: -346ms
                SessionFactoryImpl.<init>: -1040ms
Run Code Online (Sandbox Code Playgroud)

热点比较现在有了有趣的结果:

截图compare_hotspot.png.

ClassLoader.loadClass: -1686ms
XMLSchemaFactory.newSchema: -184ms
ClassFile.<init>: -109ms
Run Code Online (Sandbox Code Playgroud)

我不确定是加载Hibernate类还是我的Entity类.

第一个改进是在应用程序启动后立即创建一个EMF,以初始化所有必需的类(我有一个空的db文件作为我的应用程序附带的原型).@sharakan感谢您的回答,也许DeferredConnectionProvider已经是这个问题的解决方案.

我接下来会尝试DeferredConnectionProvider!但我们可能会进一步加快速度.你有什么建议吗?

sha*_*kan 11

你应该能够通过实现你自己ConnectionProvider作为一个真实的装饰器来做到这一点ConnectionProvider.

这里的关键观察是,在创建ConnectionProvider之前不会使用EntityManager它(请参阅注释以supportsAggressiveRelease()获取警告).因此,您可以创建一个DeferredConnectionProvider类,并使用它来构造EntityManagerFactory,然后等待用户输入,并在实际创建任何EntityManager实例之前执行延迟初始化.我把它写成一个包装器ConnectionPoolImpl,但你应该能够使用任何其他实现ConnectionProvider作为基础.

public class DeferredConnectionProvider implements ConnectionProvider {

    private Properties configuredProps;
    private ConnectionProviderImpl realConnectionProvider;

    @Override
    public void configure(Properties props) throws HibernateException {
        configuredProps = props;
    }

    public void finalConfiguration(String jdbcUrl, String userName, String password) {
        configuredProps.setProperty(Environment.URL, jdbcUrl);
        configuredProps.setProperty(Environment.USER, userName);
        configuredProps.setProperty(Environment.PASS, password);

        realConnectionProvider = new ConnectionProviderImpl();
        realConnectionProvider.configure(configuredProps);
    }

    private void assertConfigured() {
        if (realConnectionProvider == null) {
            throw new IllegalStateException("Not configured yet!");
        }
    }        

    @Override
    public Connection getConnection() throws SQLException {
        assertConfigured();

        return realConnectionProvider.getConnection();
    }

    @Override
    public void closeConnection(Connection conn) throws SQLException {
        assertConfigured();

        realConnectionProvider.closeConnection(conn);
    }

    @Override
    public void close() throws HibernateException {
        assertConfigured();

        realConnectionProvider.close();
    }

    @Override
    public boolean supportsAggressiveRelease() {
        // This gets called during EntityManagerFactory construction, but it's 
        // just a flag so you should be able to either do this, or return
        // true/false depending on the actual provider.
        return new ConnectionProviderImpl().supportsAggressiveRelease();
    }
}
Run Code Online (Sandbox Code Playgroud)

一个如何使用它的粗略示例:

    // Get an EntityManagerFactory with the following property set:
    //     properties.put(Environment.CONNECTION_PROVIDER, DeferredConnectionProvider.class.getName());
    HibernateEntityManagerFactory factory = (HibernateEntityManagerFactory) entityManagerFactory;

    // ...do user input of connection info...

    SessionFactoryImpl sessionFactory = (SessionFactoryImpl) factory.getSessionFactory();
    DeferredConnectionProvider connectionProvider = (DeferredConnectionProvider) sessionFactory.getSettings()
                    .getConnectionProvider();

    connectionProvider.finalConfiguration(jdbcUrl, userName, password);
Run Code Online (Sandbox Code Playgroud)

您可以将初始设置EntityManagerFactory放在单独的线程或其他东西上,这样用户就不必等待它.然后,在指定连接信息之后,他们唯一要等待的是设置连接池,与解析对象模型相比,这应该相当快.