【问题标题】:What is the difference between 'getDelegate()' and 'unwrap()' for getting the Hibernate session of an entity manager'getDelegate()' 和 'unwrap()' 用于获取实体管理器的 Hibernate 会话有什么区别
【发布时间】:2016-03-29 08:21:28
【问题描述】:

为了使用 Hibernate 加速 SQL 表中所有行的迭代(因为 JPA 不支持流式传输),我遵循了this answer 的方法。虽然这很有效,但this answer 告诉我们应该使用

检索Session 对象
Session session = entityManager.unwrap(Session.class);

而不是答案中的方式:

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)。

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);
        }
    }
}

application.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

【问题讨论】:

  • 注入EntityManager时不要使用@Autowired使用@PersistenceContext
  • 虽然这确实消除了 IntelliJ 的“无法自动装配...”检查错误,但它不会改变观察到的行为。
  • 发布完整的堆栈跟踪。
  • update() 方法没有用@Transactional 注释?
  • 你的方法不是@Transactional所以没有交易。这从您的 stakctrace 中也很清楚,因为 Updater 没有事务代理

标签: java spring hibernate jpa spring-transactions


【解决方案1】:

好吧,我没有使用 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();

【讨论】:

    猜你喜欢
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2011-05-08
    • 2011-02-02
    • 1970-01-01
    相关资源
    最近更新 更多