在我的桌面应用程序中,新数据库经常打开。我使用Hibernate
/ JPA
作为ORM。
问题是,创建EntityManagerFactory
非常慢,在快速机器上花费大约5-6秒。我知道EntityManagerFactory
应该是重量级的,但对于用户希望快速打开新数据库的桌面应用程序来说这太慢了。
我可以关闭一些EntityManagerFactory功能来获取实例 快点?或者是否可以懒惰地创建一些EntityManagerFactory来加速cration?
我可以以某种方式创建EntityManagerFactory对象 知道数据库网址?我很乐意关闭所有 验证是可能的。
通过这样做,我可以将EntityManagerFactorys汇集起来供以后使用吗?
如何更快地创建EntityManagerFactory?
使用更多信息和JProfiler分析进行更新
桌面应用程序可以打开已保存的文件。我们的应用程序文档文件格式包含1个SQLite数据库+以及ZIP文件中的一些二进制数据。打开文档时,将解压缩ZIP并使用Hibernate打开数据库。数据库都具有相同的模式,但显然有不同的数据。
似乎我第一次打开文件所需的时间比以下时间长得多。 我用JProfiler描述了第一次和第二次运行并比较了结果。
第一次运行:
create EMF: 4385ms
build EMF: 3090ms
EJB3Configuration configure: 900ms
EJB3Configuration <clinit>: 380ms
第二轮:
create EMF: 1275ms
build EMF: 970ms
EJB3Configuration configure: 305ms
EJB3Configuration <clinit>: not visible, probably 0ms
在调用树比较中,您可以看到某些方法明显更快(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
热点比较现在有了有趣的结果:
ClassLoader.loadClass: -1686ms
XMLSchemaFactory.newSchema: -184ms
ClassFile.<init>: -109ms
我不确定是加载Hibernate类还是我的实体类。
第一个改进是在应用程序启动后立即创建一个EMF,以初始化所有必需的类(我有一个空的db文件作为我的应用程序附带的原型)。 @sharakan感谢您的回答,也许DeferredConnectionProvider已经是这个问题的解决方案。
我接下来会尝试DeferredConnectionProvider!但我们可能会进一步加快速度。你有什么建议吗?
答案 0 :(得分: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();
}
}
如何使用它的一个粗略示例:
// 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
的初始设置放在单独的线程或其他内容上,以便用户永远不必等待它。然后,在指定连接信息之后,他们唯一要等待的是设置连接池,与解析对象模型相比,这应该相当快。
答案 1 :(得分:2)
我可以关闭一些EntityManagerFactory功能来更快地获取实例吗?
不要相信。除初始化JDBC连接/池外,EMF实际上没有太多功能。
或者是否可以懒惰地创建一些EntityManagerFactory 加快速度?
当用户注意到性能受到影响时,我建议您应该向相反的方向前进 - 在用户实际需要之前主动创建EMF,而不是懒惰地创建EMF。在应用程序初始化期间(或者至少在您了解数据库时),可能在一个单独的线程中创建一次,预先创建它。在应用程序/数据库的整个存在过程中重复使用它。
我可以在知道数据库网址之前以某种方式创建EntityManagerFactory对象吗?
否 - 它创建了一个JDBC连接。
我认为更好的问题是:为什么您的应用程序动态发现数据库连接URL?您是说您的数据库是在运行中创建/提供的,并且无法提前预测连接参数。真的要避免。
通过这样做,我可以将EntityManagerFactorys汇集起来供以后使用吗?
不,你不能集合EMF。这是你可以联合的连接。
如何更快地创建EntityManagerFactory?
我同意 - 对于EMF的初始化,6秒太慢了。
我怀疑它与您选择的数据库技术有关,而不是JPA / JDBC / JVM。我的猜测是,当你连接时,你的数据库可能正在初始化。你在使用Access吗?你用的是什么数据库?
您是否正在连接远程数据库?通过广域网?网络速度/延迟是否良好?
客户端PC性能是否有限?
编辑:在评论后添加
在真正的ConnectionProvider周围实现自己的ConnectionProvider作为装饰器不会加速用户的体验。数据库实例仍然需要初始化,EMF&amp;创建EM并且仍然需要建立JDBC连接。
选项:
(2)可能会为有限的努力提供最好的改进 另外: - 尝试在部署期间而不是在应用程序使用期间解压缩数据文件。 - 在与UI启动并行运行的启动线程中初始化EMF - 尝试启动数据库初始化作为应用程序的第一步(即使用JDBC连接到实际实例)。