2016-03-29 11 views
1

Beschleunigung der Iteration aller Zeilen in einer SQL-Tabelle mithilfe von Hibernate (JPA unterstützt kein Streaming) Ich folgte dem Ansatz this answer. Während dies gut funktioniert, sagt this answer uns, dass wir die Session Objekt abrufen solltenWas ist der Unterschied zwischen 'getDelegate()' und 'unwrap()' zum Abrufen der Hibernate-Sitzung eines Entity Managers

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

statt der Art und Weise unter Verwendung von in der Antwort Geographie:

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

Hower, mit dieser Änderung bekomme ich plötzlich die folgende Ausnahme:

java.lang.IllegalStateException: No transactional EntityManager available 

Das Unternehmen Manager in ein Feld in einer Federkomponente wie diese autowired ist:

@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 ... 
    } 
} 

Wie der Name schon sagt, liest loadAll nur Daten und wandelt sie in eine Result. update schreibt in die Datenbank.

Beachten Sie, dass loadAll nur über update aufgerufen wird. Auch das Kommentieren loadAll mit @Transactional behebt das Problem nicht.

Ich weiß, dass es mehrere andere Fragen bezüglich dieses Fehlers gibt, @Transactional die Antwort zu sein. Meine Frage ist, was der Unterschied zwischen getDelegate und unwrap wirklich ist: Warum scheitert das eine und das andere nicht? (Und warum nicht @Transactional das Problem beheben?)

Ich benutze H2 1.4.190 und Hibernate 4.3.11.Final (durch Spring Boot 1.3.2.RELEASE).

EDIT Vollständiges minimales Beispiel (mit Paketdeklarationen und Importe weggelassen). Alle Klassen sind im Paket 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); 
     } 
    } 
} 

Anwendung.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: 

Stapel Spur:

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

nicht '@ Autowired' Verwendung Verwenden Sie '@ PersistenceContext', wenn ein' EntityManager' injiziert wird. –

+0

Während dies den IntelliJ-Fehler "Konnte nicht automatisch ..." beseitigt, ändert sich das beobachtete Verhalten nicht. –

+0

Veröffentlichen Sie den vollständigen Stack-Trace. –

Antwort

0

Nun, ich bin nicht mit Frühling, aber wenn ich die Session aus Performance-Gründen bekommen wollte, erklärte ich es einfach in einem Stateless EJB :

@Stateless 
public class OpinionViewCache { 

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

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

Und Sitzung zu bekommen habe ich einen einfachen Anruf:

statelessSession = sessionFactory.openStatelessSession(); 

es geschlossen, wenn ich fertig war:

statelessSession.close(); 

Ansonsten ist der Code, den ich für das Testen von Java SE verwendet wurde, war ziemlich geradlinig:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("opjpa"); 
EntityManager em = emf.createEntityManager(); 
EntityManagerImpl emImpl = (EntityManagerImpl)em; 
HibernateEntityManagerFactory factory = emImpl.getFactory(); 
SessionFactory sessionFactory = factory.getSessionFactory(); 
Verwandte Themen