Java 尝试同时保存到两个数据库时,当前线程找不到会话错误

Java 尝试同时保存到两个数据库时,当前线程找不到会话错误,java,spring,hibernate,jpa,transactions,Java,Spring,Hibernate,Jpa,Transactions,嗨,我试图同时保存到两个数据库中,但总是出错 Exception in thread "main" org.hibernate.HibernateException: No Session found for current thread 这是我的密码: @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false) public void save(Arsena

嗨,我试图同时保存到两个数据库中,但总是出错

Exception in thread "main" org.hibernate.HibernateException: No Session found for current thread
这是我的密码:

@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false)
public void save(ArsenalPlayer domain1, ArsenalPlayer2 domain2)
        throws Exception {

    dao1.save(domain1);
    dao2.save(domain2);

}
  • dao1使用连接到datasource1的sessionFactory
  • dao2使用连接到datasource2的sessionFactory
这是我的配置

数据源

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/arsenal" />
    <property name="user" value="root" />
    <property name="password" value="ahmids" />
</bean>
DAO2

<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/arsenal2" />
    <property name="user" value="root" />
    <property name="password" value="ahmids" />
</bean>
<bean id="sessionFactory2"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource2" />

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">update</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <!-- <prop key="hibernate.current_session_context_class">thread</prop> --> 
        </props>
    </property>

    <property name="annotatedClasses">
        <list>
            <value>com.gongfu4.bean.ArsenalPlayer2</value>
        </list>
    </property>
</bean>
@Autowired
SessionFactory sessionFactory2;

public void save(ArsenalPlayer2 domain) {
    sessionFactory2.getCurrentSession().merge(domain);
}

我的配置有问题吗?

您当前的配置不支持全局事务(XA),因此无法在两个不同的数据库上跨一个事务

因此,您需要两个Hibernate事务管理器,每个会话工厂一个。然后,您需要指示事务服务应该使用哪个事务管理器

因此,不是:

@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false)
public void save(ArsenalPlayer domain1, ArsenalPlayer2 domain2)
    throws Exception {
    dao1.save(domain1);
    dao2.save(domain2);
}
你应该:

<bean id="txManager1" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory1" />
</bean>

<bean id="txManager2" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory2" />
</bean>

@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager1")
public void save(ArsenalPlayer domain1)
    throws Exception {
    dao1.save(domain1);
}

@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager2")
public void save(ArsenalPlayer domain2)
    throws Exception {
    dao2.save(domain2);
}

@事务性(rollboor=Exception.class,propagation=propagation.REQUIRES_NEW,readOnly=false,value=“txManager1”)
公共无效保存(ArsenalPlayer域1)
抛出异常{
dao1.save(域1);
}
@事务性(rollboor=Exception.class,propagation=propagation.REQUIRES_NEW,readOnly=false,value=“txManager2”)
公共无效保存(ArsenalPlayer域2)
抛出异常{
dao2.save(域2);
}

您不能像那样使用这两个事务管理器。您有两个数据源、两个事务管理器,根据您的代码,我理解您希望在同一事务中执行两个保存操作。问题是“来自哪个事务管理器的事务?”

按照代码和配置的方式,Spring将使用“默认”事务管理器,因为默认情况下,
将搜索id为“transactionManager”的事务管理器bean,而具有此id的bean是用于数据源
数据源的bean。但是您的代码不工作,这是预期的行为。Spring将使用
sessionFactory
dao1.save(domain1)打开一个Hibernate会话调用将成功,因为这是正确数据源的正确Hibernate会话。但是当
dao2.save时(domain2)方法时,您将从
dao1
调用中获得相同的会话,但用于第二个数据库的db操作

在我看来,你有两个选择:

  • 使用JTA事务管理器来协调这两个数据源。对于JTA,两个保存操作将是原子的。如果一个失败,那么两个都将回滚

  • 在两个不同的事务中执行两个
    保存(域)
    操作,正确配置
    @Transactional
    注释以使用正确的事务管理器。在这种情况下,两个保存操作将不是原子的。如果一次保存失败,则只回滚该次保存。请参见以下Spring参考文档中的豁免:

  • 
    ...
    ...
    
    使用openSession()而不是currentSession()导致异常的行在哪里?我有两个事务管理器,一个id=“transactionManager”,另一个id=“myTx”,每个事务都注入了difference sessionFactory,从您的回答来看,如果一个事务失败,它将回滚失败的事务,不是都是数据库,顺便说一句,对不起我的英语不好,如果你不使用JTA就会这样。JTA使全局事务能够跨越多个数据源。但是你需要一个JTA事务管理器,比如JEE应用服务器提供给你的那个,或者一个独立的事务管理器,比如Bitronix、Atomikos。是的,我想用一个事务方法保存到两个数据库,所以如果一个操作无法保存,两个数据库都将回滚,dao1保存操作没有任何问题,但是当它转到dao2时,我总是收到一个“找不到当前线程的会话”错误。很抱歉,我的英语不好,所以您需要使用选项1-使用JTA事务管理器。请看一篇使用Atomikos(JTA提供商)协调两个独立数据源的博客文章。请注意,您需要在配置中做很多更改:从数据源定义(需要是XA)开始,然后更改会话工厂,最后定义Atomikos配置。因此,我必须使用JTA事务来处理多个数据库。感谢您提供的信息和链接。我会接受你的回答。谢谢
    @Autowired
    SessionFactory sessionFactory2;
    
    public void save(ArsenalPlayer2 domain) {
        sessionFactory2.getCurrentSession().merge(domain);
    }
    
    @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false)
    public void save(ArsenalPlayer domain1, ArsenalPlayer2 domain2)
        throws Exception {
        dao1.save(domain1);
        dao2.save(domain2);
    }
    
    <bean id="txManager1" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory1" />
    </bean>
    
    <bean id="txManager2" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory2" />
    </bean>
    
    @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager1")
    public void save(ArsenalPlayer domain1)
        throws Exception {
        dao1.save(domain1);
    }
    
    @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager2")
    public void save(ArsenalPlayer domain2)
        throws Exception {
        dao2.save(domain2);
    }
    
        public class TransactionalService {
    
        @Transactional("order")
        public void setSomething(String name) { ... }
    
        @Transactional("account")
        public void doSomething() { ... }
        }
    
        <tx:annotation-driven/>
    
        <bean id="transactionManager1" class="org.springframework.jdbc.DataSourceTransactionManager">
            ...
            <qualifier value="order"/>
        </bean>
    
        <bean id="transactionManager2" class="org.springframework.jdbc.DataSourceTransactionManager">
            ...
            <qualifier value="account"/>
        </bean>