java - Hibernate 更快的 EntityManagerFactory 创建

标签 java sql hibernate jpa orm

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

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

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

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

  4. 还有其他想法如何更快地创建 EntityManagerFactory 吗?

更新更多信息和 JProfiler 分析

桌面应用程序可以打开保存的文件。我们的应用程序文档文件格式由 1 个 SQLite 数据库 + 和 ZIP 文件中的一些二进制数据组成。打开文档时,会提取 ZIP 并使用 Hibernate 打开数据库。数据库都具有相同的架构,但显然不同的数据。

我第一次打开文件的时间似乎比接下来的时间长得多。 我使用 JProfiler 分析了第一次和第二次运行并比较了结果。

第一次运行:

create EMF: 4385ms
    build EMF: 3090ms
    EJB3Configuration configure: 900ms
    EJB3Configuration <clinit>: 380ms

calltree1.png .

第二次运行:

create EMF: 1275ms
    build EMF: 970ms
    EJB3Configuration configure: 305ms
    EJB3Configuration <clinit>: not visible, probably 0ms

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

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

screenshot compare_hotspot.png .

ClassLoader.loadClass: -1686ms
XMLSchemaFactory.newSchema: -184ms
ClassFile.<init>: -109ms

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

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

接下来我将尝试 DeferredConnectionProvider!但我们也许可以进一步加快速度。您还有什么建议吗?

最佳答案

您应该能够通过将自己的 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();
    }
}

如何使用它的粗略示例:

    // 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);

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

关于java - Hibernate 更快的 EntityManagerFactory 创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15036233/

相关文章:

java - 如何选择Lucene中WordDelimiterFilter使用的分隔符?

java - Objective-C 中是否允许嵌套同步块(synchronized block)?

java - 如何在我的程序中将 double 格式化为整数?棋盘米传奇

mysql - 很多小的 mysql 表或一张大表

sql - "last_login"列中的空值违反了非空约束

java - 带 ACL 的 Jade 代理协议(protocol)

sql - 存储子查询结果以在整个查询中重复使用

hibernate - Hibernate中如何将事务绑定(bind)到多个线程?

java - Hibernate连接错误: NullPointerException at org. hibernate.hql.ast.HqlSqlWalker.createFromJoinElement

java - 检查hibernate中的事务是否成功