请教一个关于多数据源的分布式事务问题?

06-04-03 mindfloating
              

现实中有两个数据库位于不同的数据库服务主机上,因为有些业务操作涉及到对这两个数据库的数据插入等,而且它们应该包含在一个事务中

完成。我现在做了一个例子模拟跨数据源的分布式事务管理,但最后不能对数据库中的表进行写操作,查询就可以。我下面详细描述一下我这

个例子程序 ,看看我的做法有什么问题嘛?

1、后台数据库 SQL Server 2000 + sp3,我创建了2个数据库来模拟两个数据源,每个数据库上一张表,需要对这两张表完成分别执行一个插入操

作,并放在一个事务中,即要么两张表都插入了新的数据,要么都别插入。

2、持久层用Hibernate映射(下面是详细映射配置文件)

-------数据源 1 的hibernate.cfg.xml----------------

<?xml version="1.0" encoding="utf-8" ?>

<!DOCTYPE hibernate-configuration

PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>

<session-factory >

<property name="connection.datasource">TestDB</property>

<property name="transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>

<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.WeblogicTransactionManagerLookup</property>

<property name="dialect">net.sf.hibernate.dialect.SQLServerDialect</property>

<property name="hibernate.connection.provider_class">net.sf.hibernate.connection.DatasourceConnectionProvider</property>

<property name="hibernate.jndi.class">weblogic.jndi.WLInitialContextFactory</property>

<property name="hibernate.jdbc.fetch_size">100</property>

<property name="hibernate.jdbc.batch_size">50</property>

<property name="show_sql">true</property>

<mapping resource="test/Addressbook.hbm.xml" />

</session-factory>

</hibernate-configuration>

-------数据源 1 的hibernate.cfg.xml----------------

-------数据源 2 的hibernate.cfg.xml----------------

<?xml version="1.0" encoding="utf-8" ?>

<!DOCTYPE hibernate-configuration

PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>

<session-factory >

<property name="connection.datasource">txyyDB</property>

<property name="transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>

<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.WeblogicTransactionManagerLookup</property>

<property name="dialect">net.sf.hibernate.dialect.SQLServerDialect</property>

<property name="hibernate.connection.provider_class">net.sf.hibernate.connection.DatasourceConnectionProvider</property>

<property name="hibernate.jndi.class">weblogic.jndi.WLInitialContextFactory</property>

<property name="hibernate.jdbc.fetch_size">100</property>

<property name="hibernate.jdbc.batch_size">50</property>

<property name="show_sql">true</property>

<mapping resource="test/Txyy.hbm.xml" />

</session-factory>

</hibernate-configuration>

-------数据源 2 的hibernate.cfg.xml----------------

3、应用服务器采用Weblogic 8.1

在服务器中分别配置了2个连接池,分别对应两个数据源,数据库驱动采用了XA的SQL驱动。

4、一个例子程序,如下:

-----------例子程序源代码-----------------------

package test;

import net.sf.hibernate.*;

import net.sf.hibernate.cfg.Configuration;

import java.io.*;

import java.util.*;

import util.NamingContext;

import javax.transaction.*;

import javax.naming.*;

public class MyTest {

public static SessionFactory sf1

public static SessionFactory sf2;

static{

try{

Configuration config1 = new Configuration().configure("/hfcfg/hibernate.cfg.xml"); // 创建数据源1的Config对象

Configuration config2 = new Configuration().configure("/cfg/hibernate.cfg.xml"); // 创建数据源2的Config对象

// 根据配置创建对应2个数据源的SessionFactory对象

sf1= config.buildSessionFactory();

sf2 = config1.buildSessionFactory();

}catch(Exception e){e.printStackTrace();}

}

MyTest(){}

// 主要的业务测试方法,分别对两个数据源中的两张表执行插入操作,并置于同一事务下

public List test() throws Exception{

Context ctx = util.NamingContext.getInitialContext();

UserTransaction tx =(UserTransaction) ctx.lookup("javax.transaction.UserTransaction");

if(tx != null) {

System.out.println("获取UserTransaction实例 = " + tx.toString() + "--" + tx.getClass());

}

// 事务开始

tx.begin();

Session s1 = sf1.openSession();

Session s2 = sf2.openSession();

List addressList = null;

try{

addressList = session.find("from Addressbook as c order by c.name asc");

addressbook ar = new Addressbook();

ar.setAddress("中山大学");

ar.setName("hf");

ar.setPhone("111");

s1.save(ar);

Txyy ty = new Txyy();

ty.setYyms("test");

s2.save(ty);

for (Iterator it = addressList .iterator(); it.hasNext(); ) {

System.out.println(((Addressbook) it.next()).getName());

}

tx.commit(); // 事务提交

}catch (Exception e) {

if (tx != null) {

System.out.println("||||------事务回滚了--------||||");

tx.rollback();

}

throw e;

}finally {

s1.close();

s2.close();

}

return addressList;

}

}

我最后写了一个测试JSP,仅仅就是调用这个MyTest类的Test方法,但服务器控制台打出的信息,没有任何异常,查询结果打印出来了,但插入

操作没有执行,数据库中表的内容未发生任何改变,一直很疑惑,不知问题出在哪里?

              

mindfloating
2006-04-03 13:49

hibernate配置文件前面一些内容没显示出来,重新贴一下。

-------数据源 1 的hibernate.cfg.xml----------------
<?xml version="1.0" encoding="utf-8" ?>
!DOCTYPE hibernate-configuration
 PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>
<session-factory >
<property name="connection.datasource">TestDB</property>
<property name="transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>
<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.WeblogicTransactionManagerLookup</property>
<property name="dialect">net.sf.hibernate.dialect.SQLServerDialect</property>
<property name="hibernate.connection.provider_class">net.sf.hibernate.connection.DatasourceConnectionProvider</property>
<property name="hibernate.jndi.class">weblogic.jndi.WLInitialContextFactory</property>
<property name="hibernate.jdbc.fetch_size">100</property>
<property name="hibernate.jdbc.batch_size">50</property>
<property name="show_sql">true</property>
<mapping resource="test/Addressbook.hbm.xml" />
</session-factory>
</hibernate-configuration>

-------数据源 1 的hibernate.cfg.xml----------------

-------数据源 2 的hibernate.cfg.xml----------------
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hibernate-configuration
 PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>
<session-factory >

<property name="connection.datasource">txyyDB</property> 
<property name="transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>
<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.WeblogicTransactionManagerLookup</property>
<property name="dialect">net.sf.hibernate.dialect.SQLServerDialect</property>  -- dialect
<property name="hibernate.connection.provider_class">net.sf.hibernate.connection.DatasourceConnectionProvider</property> 
<property name="hibernate.jndi.class">weblogic.jndi.WLInitialContextFactory</property>
<property name="hibernate.jdbc.fetch_size">100</property>
<property name="hibernate.jdbc.batch_size">50</property>
<property name="show_sql">true</property>
<mapping resource="test/Txyy.hbm.xml" />
</session-factory>
</hibernate-configuration>

-------数据源 2 的hibernate.cfg.xml----------------

<p>

-----------例子程序源代码-----------------------
package test;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
import java.io.*;
import java.util.*;
import util.NamingContext;
import javax.transaction.*;
import javax.naming.*;

public class MyTest {

    public static SessionFactory sf1

    public static SessionFactory sf2;

    static{
        try{
            Configuration config1 = new Configuration().configure("/hfcfg/hibernate.cfg.xml");   // 创建数据源1的Config对象
            Configuration config2 = new Configuration().configure("/cfg/hibernate.cfg.xml");   //  创建数据源2的Config对象

	// 根据配置创建对应2个数据源的SessionFactory对象
            sf1= config.buildSessionFactory();
            sf2 = config1.buildSessionFactory();

        }catch(Exception e){e.printStackTrace();}
    }

    MyTest(){}

    // 主要的业务测试方法,分别对两个数据源中的两张表执行插入操作,并置于同一事务下		
    public List test() throws Exception{

        Context ctx = util.NamingContext.getInitialContext();
        UserTransaction tx =(UserTransaction) ctx.lookup("javax.transaction.UserTransaction");

        if(tx != null) {
            System.out.println("获取UserTransaction实例 = " + tx.toString() + "--" + tx.getClass());
        }

        // 事务开始
        tx.begin();
        Session s1 = sf1.openSession();	
        Session s2 = sf2.openSession();

        List addressList = null;
        try{

           addressList = session.find("from Addressbook as c order by c.name asc");

            addressbook ar = new Addressbook();
            ar.setAddress("gz");
            ar.setName("hf");
            ar.setPhone("111");
            s1.save(ar);

            Txyy ty = new Txyy();
            ty.setYyms("test");
            s2.save(ty);

            for (Iterator it = addressList .iterator(); it.hasNext(); ) {
                System.out.println(((Addressbook) it.next()).getName());
            }

        tx.commit(); // 事务提交

       }catch (Exception e) {
           if (tx != null) {
                System.out.println("||||------事务回滚了--------||||");
               tx.rollback();
           }
           throw e;
       }finally {
           s1.close();
           s2.close();
       }
        return addressList;
  }
}

<p>

banq
2006-04-03 15:57

写段有效的JTA代码给你参考,前后顺序都很重要,否则就不写进入:

javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction"); 
Session s1 = sf.openSession(); ... s1.flush(); 
    s1.close(); ... 

Session s2 = sf.openSession(); ... 
    s2.flush(); 
    s2.close(); 

tx.commit(); 
<p>

mindfloating
2006-04-04 11:06

多谢banq,按你给的顺序重写了一下测试代码已经搞定,可以插进数据了~~^_^

sezelee
2010-01-06 14:35

使用jtom+spring+jta 是不错的选择

tomcat使用jta+aop

http://blog.sina.com.cn/bllms

[该贴被sezelee于2010-01-06 14:37修改过]