2016-03-29 75 views
1

爲了加速使用Hibernate迭代SQL表中的所有行(因爲JPA不支持流式處理) ,我遵循this answer的方法。雖然這工作得很好,this answer告訴我們,我們應該檢索使用的'getDelegate()'和'unwrap()'之間有什麼區別,用於獲取實體管理器的Hibernate會話

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

相反,它是在回答完成的方式Session對象:

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

豪爾,這種變化我突然得到了以下情況除外:

java.lang.IllegalStateException: No transactional EntityManager available 

實體管理器這樣的自動裝配成一個字段在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只讀取數據並將其轉換爲一些Resultupdate確實寫入數據庫。

請注意,loadAll只能通過update調用。另外,使用@Transactional註釋loadAll不能解決問題。

我知道還有幾個關於這個錯誤的其他問題,說明@Transactional是答案。我的問題是getDelegateunwrap之間的區別是什麼:爲什麼一個失敗,另一個不是? (爲什麼@Transactional沒有解決這個問題?)

我使用H2 1.4.190和Hibernate 4.3.11.Final(通過Spring Boot 1.3.2.RELEASE)。

編輯完整的最小示例(包含聲明和導入忽略)。所有的類都在包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); 
     } 
    } 
} 

應用。陽明海運

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'使用'注入'EntityManager'時@ @ PersistenceContext'。 –

+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(); 
相關問題