为什么在事务完成后hibernate会话没有关闭

时间:2017-04-25 17:55:14

标签: mysql spring hibernate transactional

我使用Spring + hibernate进行数据库访问(mysql)。对于只读方法,我需要将基础连接状态更改为只读,以便我可以将读取操作发送到从属(在主从中)。

在下面的代码中,我使用hibernate session将底层连接更改为只在getEmployer方法中读取。这部分工作正常。 我假设在执行getEmployer方法后,会话将被关闭(因为它由@Transactional注释)。因此,当调用下面的saveEmployer方法时,将启动一个新会话。因此,connection.isReadOnly()中的saveEmployer应返回false。但是,当我调用saveEmployer方法(调用getEmployer方法后)时,connection.isReadOnly原来是true

我认为在getEmployer方法执行后会话未关闭。我的理解是,当使用@Transactional注释方法时,会话将在提交事务后关闭。我这里做错了吗?为什么connection.isReadOnly()方法中的saveEmployertrue?感谢。

@Service
class public EmployerServiceImpl implements EmployerService{
    @Inject EmployerRepository employerRepository;
    @PersistenceContext
    EntityManager em;

    @Transactional
    public void getEmployer(long id){
        Session session = em.unwrap(Session.class);
        session.doWork(new Work(){
            @Override
            public void execute(Connection connection) throws SQLException{
                connection.setReadOnly(true);
            }
        });
        ......
        this.employerRepository.get(id);
    }

    @Transactional
    public void saveEmployer(){
        Session session = em.unwrap(Session.class);
        session.doWork(new Work(){
            @Override
            public void execute(Connection connection) throws SQLException{
               log.warn("connection status = " + connection.isReadOnly());
            }
        });
        ......
        this.employerRepository.save(employer);
    }
}

以下是根上下文中的dataSource的java配置

@Bean
public DataSource springJpaDataSource()
{
    JndiDataSourceLookup lookup = new JndiDataSourceLookup();
    return lookup.getDataSource("jdbc/SpringJpa");
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
{
    Map<String, Object> properties = new Hashtable<>();
    properties.put("javax.persistence.schema-generation.database.action",
            "none");
    properties.put("hibernate.search.default.directory_provider",
            "filesystem");
    properties.put("hibernate.search.default.indexBase", "../searchIndexesApplyJob");

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

    LocalContainerEntityManagerFactoryBean factory =
            new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(adapter);
    factory.setDataSource(this.springJpaDataSource());
    factory.setPackagesToScan("com.peer.site.entities", "com.peer.site.message",
            "com.peer.site.converters");
    factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
    factory.setValidationMode(ValidationMode.NONE);
    factory.setJpaPropertyMap(properties);
    return factory;
}

@Bean
public PlatformTransactionManager jpaTransactionManager()
{
    return new JpaTransactionManager(
            this.entityManagerFactoryBean().getObject()
    );
}

2 个答案:

答案 0 :(得分:0)

使用@Transactional(readonly = true) like explained here工作更方便,而不是在原生会话中手动设置此值。

如果会话将被Spring重用,则取决于(可能是嵌套的)事务划分和/或执行调用的线程。它可以由同一个线程重复使用(存储在ThreadLocal中)。 Here您可以找到更多信息。

答案 1 :(得分:0)

我找出原因。在tomcat文档中,它说

  

池本身插入的唯一状态是defaultAutoCommit,   defaultReadOnly,defaultTransactionIsolation,defaultCatalog如果这些   已设定。这4个属性仅在创建连接时设置。   是否应该在使用过程中修改这些属性   连接,池本身不会重置它们。

会发生什么是连接被重用。但是连接池不会重置其状态。因此,如果您修改连接状态,则必须重置它,因为连接池不会重置它。

正如其他人所说,我在@Transactional注释中设置了readOnly = true,这是一种更清晰的方法。但请注意,org.springframework的版本必须是&gt; = 4.1。在4.1之前,readOnly只会更改刷新模式,但不会将连接设置为readOnly。

相关问题