2013-02-23 6 views
23

В моем настольном приложении новые базы данных открываются довольно часто. Я использую Hibernate/JPA как ОРМ. Проблема в том, что создание EntityManagerFactory происходит довольно медленно, примерно на 5-6 секунд на быстрой машине. Я знаю, что EntityManagerFactory должен быть тяжеловесным, но это слишком медленно для настольного приложения, где пользователь ожидает, что новая база данных будет быстро открыта.Hibernate быстрее Создание EntityManagerFactory

  1. Могу ли я отключить некоторые EntityManagerFactory функции, чтобы получить экземпляр быстрее? Или можно лениво создать некоторую часть EntityManagerFactory, чтобы ускорить разбивку?

  2. Могу ли я каким-то образом создать объект EntityManagerFactory до зная адрес базы данных? Я был бы рад отключить все проверки , чтобы это было возможно.

  3. Посредством этого можно ли использовать EntityManagerFactory для последующего использования?

  4. Любая другая идея, как создать EntityManagerFactory быстрее?

Обновления с большим количеством информации и JProfiler профилирования

настольного приложения может открыть сохраненные файлы. Формат файла документа приложения составляет 1 базу данных SQLite + и некоторые двоичные данные в ZIP-файле. При открытии документа ZIP извлекается, а db открывается с помощью 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 в качестве прототипа, уже поставляемого вместе с моим приложением)[email protected] благодарит вас за ответ, возможно, DeferredConnectionProvider уже будет решением этой проблемы.

Я попробую DeferredConnectionProvider дальше! Но мы могли бы ускорить его еще больше. У вас есть еще предложения?

+1

Являются ли ваши сопоставления базы данных в XML или аннотациях? –

+0

У меня есть 'hibernate.cfg.xml' с отображениями. Классы полномочий аннотируются. Тогда, когда я знаю URL-адрес databese, я также предоставляю строку свойств при создании EnitityManagerFactory.Он построен следующим образом: 'Map properties = new HashMap (); \t properties.put ("javax.persistence.jdbc.url", JDBC.PREFIX + dbFile.getAbsolutePath()); ' Я открыт, чтобы изменить это, если я ускоряю создание EntityManagerFactory. Какие-либо предложения? – user643011

+0

, в этом случае вы получите улучшение производительности, если вы удаляетесь от аннотаций и _only_ используйте xml для определения своих сопоставлений. Сканирование классов и поиск аннотаций происходит медленнее, чем просто чтение xml –

ответ

10

Вы должны быть в состоянии сделать это, реализовав свой собственный 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 в отдельном потоке или что-то, так что пользователь не должен ждать его , Тогда единственное, что они будут ждать после указания информации о подключении, это настройка пула соединений, который должен быть довольно быстрым по сравнению с анализом объектной модели.

+0

Работал красиво! Используя 'DeferredConnectionProvider', мы устанавливаем' EntityManagerFactoryPool'. Открытие db теперь продолжается через 1 секунду. – user643011

+0

Очень круто, рад, что помогло. – sharakan

2

Можно ли отключить некоторые функции EntityManagerFactory, чтобы быстрее получить экземпляр?

Не верьте в это. У EMF действительно нет слишком много функций, кроме инициализации соединения/пула JDBC.

Или можно создать некоторые из EntityManagerFactory лениво скоростью до cration?

Вместо создания ЭДС лениво, когда пользователь заметит падение производительности, я предлагаю вам следует направиться в противоположном направлении - создать EMF заранее, прежде чем пользователь действительно нуждается. Создайте его один раз, вверх, возможно, в отдельном потоке во время инициализации приложения (или, по крайней мере, сразу же, как вы знаете о своей базе данных). Повторно используйте его во время существования вашего приложения/базы данных.

Могу ли я каким-то образом создать объект EntityManagerFactory, прежде чем знать URL-адрес базы данных?

Нет - он создает соединение JDBC.

Я думаю, что лучший вопрос: почему ваше приложение динамически обнаруживает URL-адреса подключения к базе данных? Вы говорите, что ваши базы данных создаются/становятся доступными «на лету», и нет возможности заранее предусмотреть параметры соединения. Этого следует избегать.

Таким образом, могу ли я объединить EntityManagerFactorys для последующего использования?

Нет, вы не можете объединить ЭДС. Это соединения, которые вы можете объединить.

Любая другая идея, как создать EntityManagerFactory быстрее?

Согласен - 6 секунд слишком медленно для инициализации ЭДС.

Я подозреваю, что это больше связано с выбранной вами технологией базы данных, чем JPA/JDBC/JVM. Я предполагаю, что, возможно, ваша база данных инициализируется при подключении. Вы используете Access? Какую БД вы используете?

Вы подключаетесь к базе данных удаленно? Через WAN? Хорошо ли работает скорость/латентность сети?

Являются ли клиентские ПК ограниченными по производительности?

EDIT: Добавлено после комментариев

вводящих свой собственный ConnectionProvider как декоратор вокруг реальной ConnectionProvider не ускорит работу пользователей на всех. Экземпляр базы данных еще нужно инициализировать, создать EMF & EM и соединение JDBC по-прежнему необходимо установить.

Варианта:

  1. Доля общий предустановленный экземпляр DB: кажется, не представляется возможной для бизнеса-сценария (хотя JSE технология поддерживает это, а также поддерживает дизайн клиента-сервер).
  2. Изменение в БД с более быстрым запуском: Derby (a.k.a. Java DB) входит в состав современных JVM и имеет время запуска около 1,5 секунд (холодное) и 0,7 секунды (теплые - предварительно загруженные данные).
  3. В большинстве сценариев (наиболее?) Самым быстрым решением будет загрузка данных непосредственно в Java-объекты в памяти с использованием JAXB с STAX. Впоследствии используйте данные в кэше в памяти (в частности, с использованием интеллектуальных структур, таких как карты, хэширование и arraylists). Так же, как JPA может сопоставлять классы POJO с таблицами базы данных &, поэтому JAXB может сопоставлять классы POJO с XML-схемой & с экземплярами XML doc. Если у вас очень сложные запросы, использующие логику SQL с множеством соединений и сильное использование индексов DB, ​​это было бы менее желательно.

(2), вероятно, даст лучшее улучшение для ограниченного усилия.
Дополнительно: - попробуйте разархивировать файлы данных во время развертывания, а не во время использования приложения. - инициализировать EMF в загрузочном потоке, который запускается параллельно с запуском пользовательского интерфейса. Попробуйте запустить инициализацию DB как один из первых шагов приложения (что означает подключение к реальному экземпляру с использованием JDBC).

+0

Приложение считывает формат приложения, который основывается на 1 базе данных SQLite в zip-файле, на локальном хранилище (без участия WLAN). Мы разработали его, поэтому он основан на проверенной технологии (SQLite) и легко обрабатывается. Невозможно узнать URL-адрес базы данных до того, как пользователь откроет файл. Я исправил исходный вопрос с дополнительной информацией. Спасибо за предложения! – user643011

+0

Он строит (или, по крайней мере, инициализирует с нуля) экземпляр базы данных из zip-файла один или несколько раз для каждого сеанса входа пользователя? Ну, похоже, это будет большая часть вашего 6-секундного ожидания - вероятно, мало что касается JPA. –

+0

Динамически-инициализированный-клиент-база данных-экземпляр для каждого пользователя наиболее необычен и, очевидно, имеет задержку запуска. Любая причина, по которой вы не могли совместно использовать предварительно инициализированный общий экземпляр, уже загруженный необходимыми данными? –

Смежные вопросы