快捷搜索: 王者荣耀 脱发

SpringBoot jpa EntityManager的使用

一、问题记录

最近需要配置jpa多数据源,按照网上的方法,配置config类,里面有个EntityManager方法

/**
     * 配置EntityManager
     * 
     * @param builder
     * @return
     */
    @Primary
    @Bean(name = "smartlandEntityManager")
    public EntityManager smartlandEntityManager(EntityManagerFactoryBuilder builder) {
        return smartlandEntityManagerFactory(builder).getObject().createEntityManager();
    }复制代码

使用时我是这样写

@Resource(name = "smartlandEntityManager")
    private EntityManager entityManager;复制代码

结果发现查询结果一直有缓存,后面查找了资料想了下,发现这么写有几个问题:

1、EntityManager 是线程不安全的,不应该创建这种单例的bean去管理

2、jpa的一级缓存是针对于EntityManager,即同个EntityManager实例,如果不进行事务提交,那么查询的结果会有缓存,而我们平时使用@transaction开启事务,实际上开启的事务是注册到spring容器上下文创建的EntityManager实例(下面会说明),并不是我在config类中创建的EntityManager实例,所以使用config类中的EntityManager实例去查询数据会一直有缓存。

二、正确的使用方式

不需要在config类中创建EntityManager的Bean方法,直接使用

@PersistenceContext(unitName = "smartlandPersistenceUnit")
    private EntityManager entityManager;复制代码

三、简单说明

    若我们正常使用jpa的JpaRepository,你可以看到一次完整的request请求会有两个entityManager。第一个EntityManager由OpenEntityManagerInViewInterceptor在请求到达时创建,跟请求线程绑定,主要用于处理jpa的懒加载,第二个EntityManager是在进入Dao层时创建,用于真正的连接操作数据库;退出Dao层后,第二个EntityManager会把那些需要懒加载的数据放到第一个EntityManager,然后第二个EntityManager关闭。
    若是采用@resource的方式使用EntityManage,如下图可以明显看出实际查询使用的entityManager和事务提交到的entityManager不是同一个实例对象,因此会出现我遇到的缓存问题。
    若采用@PersistenceContext的方式使用entityManager,得到的实际上是一个共享的entityManager代理对象,他会在真正要调用entityManager时,从容器上下文获取entityManager实例(获取方法见PersistenceElement类)
    这个entityManager绑定当前请求线程的,用如下图可以看到实际查询使用的和事务提交的是同一个entityManager,而且每次请求的entityManager实例都是不同的。
经验分享 程序员 微信小程序 职场和发展