Hibernate更快地创建EntityManagerFactory [英] Hibernate faster EntityManagerFactory creation
问题描述
在我的桌面应用程序中,新数据库经常打开。我使用 Hibernate
/ JPA
作为ORM。
问题是,创建 EntityManagerFactory
很慢,在一台快速机器上花费5-6秒。我知道 EntityManagerFactory
应该是重量级的,但是对于用户希望快速打开新数据库的桌面应用程序来说,这太慢了。
-
我可以关闭一些EntityManagerFactory功能以使实例
更快吗?或者是否可以创建一些EntityManagerFactory来加速缓存? -
我可以以某种方式创建EntityManagerFactory对象,知道数据库url之前的
?我很乐意关闭所有的
验证功能。 通过这样做,我可以将EntityManagerFactorys集中以备后用吗? / p> -
其他想法如何更快创建EntityManagerFactory?
使用更多信息和JProfiler分析进行更新
桌面应用程序可以打开保存的文件。我们的应用程序文档文件格式由1个SQLite数据库+和ZIP文件中的一些二进制数据组成。当打开一个文档时,ZIP被解压缩并且数据库用Hibernate打开。这些数据库都有相同的模式,但显然不同的数据。
似乎我第一次打开一个文件需要比以下时间长得多的时间。
我用JProfiler描述了第一次和第二次运行并比较了结果。 p $ p $ 创建EMF:4385ms
构建EMF:3090ms
EJB3Configuration configure:900ms
EJB3Configuration< clinit>:380ms
。
第二次运行:
创建EMF:1275ms
build EMF:970ms
EJB3Configuration configure:305ms
EJB3Configuration< clinit> ;:不可见,可能是0ms
。
在Call tree比较中,您可以看到有些方法明显更快(DatabaseManager。作为起点):
创建EMF:-3120ms
Hibernate创建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文件作为原型SH撇开我的申请)。 @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;
$ b $ 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);
$ b $ private void assertConfigured(){
if(realConnectionProvider == null){
throw new IllegalStateException(Not configured yet!);
}
}
@Override
public Connection getConnection()throws SQLException {
assertConfigured();
返回realConnectionProvider.getConnection();
}
@Override
public void closeConnection(Connection conn)throws SQLException {
assertConfigured();
realConnectionProvider.closeConnection(conn);
}
@Override
public void close()throws HibernateException {
assertConfigured();
realConnectionProvider.close();
$ b @Override
public boolean supportsAggressiveRelease(){
//在EntityManagerFactory构造期间被调用,但它是
//只是一个标志你应该能够做到这一点,或者根据实际的提供者返回
// true / false。
返回新的ConnectionProviderImpl()。supportsAggressiveRelease();
}
}
如何使用它的一个粗略示例: p>
//使用以下属性设置获取EntityManagerFactory:
// properties.put(Environment.CONNECTION_PROVIDER,DeferredConnectionProvider.class .getName());
HibernateEntityManagerFactory factory =(HibernateEntityManagerFactory)entityManagerFactory;
// ...做用户输入的连接信息...
SessionFactoryImpl sessionFactory =(SessionFactoryImpl)factory.getSessionFactory();
DeferredConnectionProvider connectionProvider =(DeferredConnectionProvider)sessionFactory.getSettings()
.getConnectionProvider();
connectionProvider.finalConfiguration(jdbcUrl,userName,password);
您可以将 EntityManagerFactory $ c $在一个单独的线程或某个东西上,以便用户永远不必等待它。然后,在指定连接信息后,他们唯一要等待的就是建立连接池,与解析对象模型相比,这应该相当快。
In my desktop application new databases get opened quite often. I use Hibernate
/JPA
as an ORM.
The problem is, creating the EntityManagerFactory
is quite slow, taking about 5-6 Seconds on a fast machine. I know that the EntityManagerFactory
is supposed to be heavyweight but this is just too slow for a desktop application where the user expects the new database to be opened quickly.
Can I turn off some EntityManagerFactory features to get an instance faster? Or is it possible to create some of the EntityManagerFactory lazily to speed up cration?
Can I somehow create the EntityManagerFactory object before knowing the database url? I would be happy to turn off all validation for this to be possible.
By doing so, can I pool EntityManagerFactorys for later use?
Any other idea how to create the EntityManagerFactory faster?
Update with more Information and JProfiler profiling
The desktop application can open saved files. Our application document file format constists of 1 SQLite database + and some binary data in a ZIP file. When opening a document, the ZIP gets extracted and the db is opened with Hibernate. The databases all have the same schema, but different data obviously.
It seems that the first time I open a file it takes significantly longer than the following times. I profiled the first and second run with JProfiler and compared the results.
1st Run:
create EMF: 4385ms
build EMF: 3090ms
EJB3Configuration configure: 900ms
EJB3Configuration <clinit>: 380ms
.
2nd Run:
create EMF: 1275ms
build EMF: 970ms
EJB3Configuration configure: 305ms
EJB3Configuration <clinit>: not visible, probably 0ms
.
In the Call tree comparison you can see that some methods are significantly faster (DatabaseManager. as starting point):
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
The Hot spot comparison now has the interesting results:
.
ClassLoader.loadClass: -1686ms
XMLSchemaFactory.newSchema: -184ms
ClassFile.<init>: -109ms
I am not sure if it is the loading of Hibernate classes or my Entity classes.
A first improvement would be to create an EMF as soon as the application starts just to initialize all necessary classes (I have an empty db file as a prototype already shipped with my Application). @sharakan thank you for your answer, maybe a DeferredConnectionProvider would already be a solution for this problem.
I will try the DeferredConnectionProvider next! But we might be able to speed it up even further. Do you have any more suggestions?
You should be able to do this by implementing your own ConnectionProvider
as a decorator around a real ConnectionProvider
.
The key observation here is that the ConnectionProvider
isn't used until an EntityManager
is created (see comment in supportsAggressiveRelease()
for a caveat to that). So you can create a DeferredConnectionProvider
class, and use it to construct the EntityManagerFactory
, but then wait for user input, and do the deferred initialization before actually creating any EntityManager
instances. I'm written this as a wrapper around ConnectionPoolImpl
, but you should be able to use any other implementation of ConnectionProvider
as the base.
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();
}
}
a rough example of how to use it:
// 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);
You could put the initial set up of the EntityManagerFactory
on a separate thread or something, so that the user never has to wait for it. Then the only thing they'll wait for, after specifying the connection info, is the setting up of the connection pool, which should be fairly quick compared to parsing the object model.
这篇关于Hibernate更快地创建EntityManagerFactory的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!