爲了加速使用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
只讀取數據並將其轉換爲一些Result
。 update
確實寫入數據庫。
請注意,loadAll
只能通過update
調用。另外,使用@Transactional
註釋loadAll
不能解決問題。
我知道還有幾個關於這個錯誤的其他問題,說明@Transactional
是答案。我的問題是getDelegate
和unwrap
之間的區別是什麼:爲什麼一個失敗,另一個不是? (爲什麼@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
不要使用'@ Autowired'使用'注入'EntityManager'時@ @ PersistenceContext'。 –
雖然這確實會刪除IntelliJ的「無法自動裝載...」檢查錯誤,但它不會改變觀察到的行爲。 –
發佈完整的堆棧跟蹤。 –