Я работаю над настольным приложением, использующим Java SE 7. Приложение использует несколько потоков, и в каждом потоке, который создается, DAO-класс вводится для получения доступа к моей базе данных. В качестве слоя persistence я использую EclipseLink и JPA. EntityManager вводится в мой класс DAO, используя инъекцию конструктора, и поскольку он не является потокобезопасным, я пошел на этот подход, используя такой прокси:
public PluginInstanceJpaController implements IPluginInstanceDao {
private EntityManager em;
@Injected
public PluginInstanceJpaController(Provider<EntityManager> emp) {
this.em = emp.get();
}
@Transactional
public void create(PluginInstance foo) throws Exception {
em.persist(foo);
}
}
Тем не менее, тот же экземпляр EntityManager вводится в каждый DAO. Для настройки этого я использовал JpaPersistModule, поскольку он предоставлен guice, и я уверен, что в моей настройке пока нет синглтонов.
Кто-нибудь знает, как сообщить об этом создателю нового экземпляра EntityManager при инъекции?
В другом подходе я пробовал настраиваемые поставщики для EntityManagerFactory и EntityManager и оставил JpaPersistModule вне моего бизнеса. Это привело к экземпляру EntityManager для DAO, однако @Transactional аннотированные методы не были перехвачены.
Буду признателен за любое решение этой проблемы. Спасибо, до сих пор!
--- EDIT ---
Классы DAO вводятся в Runnable, который их использует. Runnable также предоставляется через Поставщика. Моя конфигурация модуля выглядит примерно так:
public class RepositoryModule extends AbstractModule {
@Override
protected void configure() {
// DAO bindings
bind(IObjectStoreDao.class).to(ObjectStoreJpaController.class);
bind(IPluginInstanceDao.class).to(PluginInstanceJpaController.class);
}
@Provides
public PluginMonitor newMonitor(IPluginInstanceDao plugDao, IObjectStoreDao osDao) {
PluginMonitor m = new PluginMonitor();
m.setPluginInstanceDao(plugDao);
m.setObjectStoreDao(osDao);
return m;
}
}
Здесь PluginMonitor - мой Runnable. Сам инжектор создается в моем основном потоке... возможно, это была проблема?
Это очень похожая проблема: Как Guice вводит синглтоны и неселекторы в несколько потоков
В ваш DAO это должно работать.
public PluginInstanceJpaController implements IPluginInstanceDao {
private Provider<EntityManager> emProvider;
@Injected
public PluginInstanceJpaController(Provider<EntityManager> emp) {
this.em = emp;
}
@Transactional
public void create(PluginInstance foo) throws Exception {
em.get().persist(foo);
}
}
Вы должны использовать Jfast Persistence Module или создать собственный поставщик EntityManager, который вернет новый EntityManager для каждого вызова get(), также он может быть реализована с помощью ThreadLocal, чтобы гарантировать, что EntityManager будет совместно использоваться в потоке.
Я не знаком с JPA, но надеюсь, что могу помочь: -)
Если вы посмотрите источник для EntityManagerProvider, вы увидите там ThreadLocal<EntityManager>
. Поэтому по умолчанию каждый поток должен иметь свой собственный EntityManager
. Это заставляет меня думать, что проблема в другом месте. Вы уверены, что ни один модуль не устанавливает EntityManager
как одноэлементный? Как вы определяете, что все EntityManager
являются одним и тем же объектом? Каждый DAO определенно находится в своей собственной теме? Можете ли вы предоставить подробную информацию о том, как FooDao настроен в вашем модуле и как новый FooDao предоставляется для каждого потока?
Кроме того, вы должны быть в порядке, чтобы написать свой конструктор как:
@Inject
public FooDao(EntityManager emp) {
this.em = emp;
}
Guice поможет вам определить, что EntityManagerProvider
предоставляет экземпляры EntityManager
, и вызовет get()
в экземпляре EntityManagerProvider
, чтобы получить EntityManager
для вашего конструктора.