2016-03-29 1 views
1

Чтобы ускорить итерацию всех строк в таблице SQL с помощью Hibernate (поскольку JPA не поддерживает потоковую передачу) , Я следил за приближением this answer. В то время как это работает хорошо, this answer говорит нам, что мы должны извлечь Session объект с помощьюВ чем разница между 'getDelegate()' и 'unwrap()' для получения сеанса Hibernate диспетчера сущностей

Session session = entityManager.unwrap(Session.class); 

вместо как это было сделано в ответ:

Session session = (Session) manager.getDelegate(); 

Hower, с этим изменением я вдруг получаю следующее исключение:

java.lang.IllegalStateException: No transactional EntityManager available 

менеджер объект autowired в поле в Spring компонента, как это:

@Component 
public class Updater { 
    @Autowired 
    private EntityManager entityManager; 

    @Transactional 
    public void update() { 
     // ... 
     Result loadedResult = loadAll() 
     // ... 
    } 

    private Result loadAll() { 
     Session session = (Session) manager.getDelegate(); 
     //Session session = entityManager.unwrap(Session.class); 

     SessionFactory sessionFactory = session.getSessionFactory(); 
     StatelessSession statelessSession = sessionFactory.openStatelessSession(); 

     // ... @linked answer ... 
    } 
} 

Как следует из названия, loadAll только считывает данные и преобразует его в какой-то Result. update делает запись в базу данных.

Отметьте, что loadAll вызван только через update. Кроме того, аннотация loadAll с @Transactional не устраняет проблему.

Я знаю, что есть несколько других вопросов относительно этой ошибки, заявив, что @Transactional будет ответом. Мой вопрос заключается в том, что разница между getDelegate и unwrap на самом деле: почему один терпит неудачу, а другой нет? (И почему не @Transactional устранить проблему?)

Я использую H2 1.4.190 и Hibernate 4.3.11.Final (через Spring Boot 1.3.2.RELEASE).

EDIT Полный минимальный пример (с декларациями пакетов и импортом). Все классы в пакете com.example:

Entity.java

@javax.persistence.Entity 
public class Entity { 

    @Id 
    @GeneratedValue 
    public int id; 

    public int value; 
} 

EntityRepository.java

public interface EntityRepository extends JpaRepository<Entity, Integer> { 
} 

Config.java:

@Configuration 
@ComponentScan 
@EnableJpaRepositories 
@EntityScan 
@EnableTransactionManagement 
public class Config { 
} 

Runner.java:

@SpringBootApplication 
public class Runner implements CommandLineRunner { 

    public static void main(String[] args) { 
     SpringApplication application = new SpringApplication(Runner.class); 
     application.setWebEnvironment(false); 
     application.run(args); 
    } 

    @Autowired 
    private Updater updater; 

    @Override 
    public void run(String... args) throws Exception { 
     updater.insert(1, 4, 2); 
     updater.update(); 

     updater.printAll(); 
    } 
} 

Updater.java:

@Component 
public class Updater { 

    @Autowired 
    private EntityRepository repository; 

    @PersistenceContext //@Autowired 
    private EntityManager entityManager; 

    public void insert(int... values) { 
     for (int value : values) { 
      Entity entity = new Entity(); 
      entity.value = value; 
      repository.save(entity); 
     } 
     repository.flush(); 
    } 

    public void update() { 
     // Call "transactioned" method through an intermediary method. 
     // The code works if 'Runner' calls 'transactionedUpdate' directly. 
     transactionedUpdate(); 
    } 

    @Transactional 
    public void transactionedUpdate() { 
     int sum = loadAll(); 

     // Set all 'value's to 'sum'. 
     List<Entity> entities = repository.findAll(); 
     for (Entity entity : entities) { 
      entity.value = sum; 
      repository.save(entity); 
     } 
     repository.flush(); 
    } 

    public int loadAll() { 
//  Session session = (Session) entityManager.getDelegate(); 
     Session session = entityManager.unwrap(Session.class); 

     SessionFactory sessionFactory = session.getSessionFactory(); 
     StatelessSession statelessSession = sessionFactory.openStatelessSession(); 

     Query query = statelessSession.createQuery("FROM com.example.Entity e"); 
     query.setFetchSize(1000); 
     query.setReadOnly(true); 
     query.setLockMode("e", LockMode.NONE); 
     ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY); 

     int sum = 0; 
     while (results.next()) { 
      Entity entity = (Entity) results.get(0); 
      sum += entity.value; 
     } 

     results.close(); 
     statelessSession.close(); 

     return sum; 
    } 

    public void printAll() { 
     List<Entity> entities = repository.findAll(); 
     for (Entity entity : entities) { 
      System.out.println(entity.id + ": " + entity.value); 
     } 
    } 
} 

применение.YML:

spring: 
    jpa: 
     open-in-view: false 
     hibernate: 
      ddl-auto: update 
      naming-strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy 
     database: H2 
     show_sql: false 
     properties: 
      hibernate.cache.use_second_level_cache: true 
      hibernate.cache.use_query_cache: false 
    datasource: 
     driver-class-name: org.h2.Driver 
     url: jdbc:h2:file:~/data-test/db;DB_CLOSE_DELAY=-1 
     name: 
     username: test 
     password: 

Стек след:

java.lang.IllegalStateException: Failed to execute CommandLineRunner 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:809) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:790) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:777) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at com.example.Runner.main(Runner.java:16) [classes/:na] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_60] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_60] 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na] 
Caused by: java.lang.IllegalStateException: No transactional EntityManager available 
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:268) ~[spring-orm-4.2.4.RELEASE.jar:4.2.4.RELEASE] 
    at com.sun.proxy.$Proxy49.unwrap(Unknown Source) ~[na:na] 
    at com.example.Updater.loadAll(Updater.java:49) ~[classes/:na] 
    at com.example.Updater.doUpdate(Updater.java:36) ~[classes/:na] 
    at com.example.Updater.update(Updater.java:31) ~[classes/:na] 
    at com.example.Updater$$FastClassBySpringCGLIB$$503dcdb8.invoke(<generated>) ~[classes/:na] 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:651) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE] 
    at com.example.Updater$$EnhancerBySpringCGLIB$$f362c2c8.update(<generated>) ~[classes/:na] 
    at com.example.Runner.run(Runner.java:25) [classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:806) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    ... 9 common frames omitted 
+0

Не используйте '@ использование Autowired' '@ PersistenceContext' при инъекции' EntityManager'. –

+0

Хотя это устраняет ошибку инспекции IntelliJ «Не удалось авто ...», она не изменяет наблюдаемое поведение. –

+0

Опубликовать полную трассировку стека. –

ответ

0

Ну, я не использую Spring, но когда я хотел, чтобы получить SessionFactory соображений производительности я просто объявил его в Stateless EJB :

@Stateless 
public class OpinionViewCache { 

    @PersistenceUnit(unitName="opee") SessionFactory sessionFactory; 
    private StatelessSession statelessSession; 

    public void setSessionFactory(SessionFactory sessionFactory) { 
     this.sessionFactory = sessionFactory; 
    } 

И получить сеанс я использовал простой вызов:

statelessSession = sessionFactory.openStatelessSession(); 

закрыт, когда мне было сделано:

statelessSession.close(); 

В противном случае код, который я использовал для тестирования из Java SE был довольно прямо вперед:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("opjpa"); 
EntityManager em = emf.createEntityManager(); 
EntityManagerImpl emImpl = (EntityManagerImpl)em; 
HibernateEntityManagerFactory factory = emImpl.getFactory(); 
SessionFactory sessionFactory = factory.getSessionFactory(); 
Смежные вопросы