linpyi
linpyi
2009-01-09 11:16
浏览 499
已采纳

搭建一个框架,如何做到事务在service层处理

想搭一个基础的框架,不想用spring和hibernate那些东西....

自己写连接池连接(连接池代码就不想多说,主要是能获取一个connection对象).

想自己写事务处理.service和dao层尽量解耦合(所有的关于业务的事务处理都在service层解决,dao层只做数据库操作)
[code="java"]
import java.io.*;
import java.util.*;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.transaction.*;
import java.sql.ResultSet;

/**基类**/
public class BaseDAL implements java.io.Serializable
{
protected java.sql.Connection myConn=null;
public java.sql.Statement stmt = null ;
protected java.sql.PreparedStatement pstmt = null;
//public java.sql.ResultSet rs = null;
private javax.sql.DataSource ds =null;
private javax.transaction.UserTransaction transaction =null;
private Context ctx =null;
public int makeTrans=0; //表示是否启动了事务,为0表示没有启动事务,1表示有启动事务
public int connCount =0; //表示数据库连接的次数,每调用一次Connection,加1,每调用一次Disconnect,减1
protected java.lang.String JNDIName;
public BaseDAL() throws Exception
{
try
{
JNDIName ="jdbc/TripDataSource";
ctx =new InitialContext();
}
catch (Exception e)
{
throw e;
}
}

public void setTransactionTimeOut(int time)
{
try
{
this.transaction.setTransactionTimeout(time);
}
catch (Exception e)
{
}
}

/**修改JNDIName**/
public void setJNDIName(String JNDIName)
{
this.JNDIName = JNDIName;
myConn = null;
}

/**事务创建**/
public void begin() throws Exception
{
try
{
if (transaction == null)
{
transaction =(javax.transaction.UserTransaction)
ctx.lookup("javax.transaction.UserTransaction");
transaction.setTransactionTimeout(120);
}
if (ds==null)
{
ds = (javax.sql.DataSource) ctx.lookup(JNDIName);
}
//获得当前的事务状态,如果不为0,表示当前没有事务在处理
int tranStatus = transaction.getStatus();
if ( tranStatus != 0)
{
//启动一个事务,记录当前有事务处理
makeTrans = 1;
transaction.begin();
}
}
catch( Exception E)
{
throw E;
}
}

/**事务提交**/
public void commit() throws Exception
{
try
{
//如果当前有提交事务
if ( makeTrans != 0)
{
transaction.commit();
makeTrans = 0;
}
}
catch(Exception E)
{
throw E;
}
}

/**事务回滚**/
public void rollback() throws Exception
{
try
{
//如果当前有提交事务
if ( makeTrans != 0)
{
transaction.rollback();
makeTrans = 0;
}
}
catch(Exception E)
{
throw E;
}
}

/**连接数据库**/
protected void connection() throws Exception
{
try
{
if (ds==null)
{
ds = (javax.sql.DataSource) ctx.lookup(JNDIName);
}

  //如果连接计数为0,连接数据库
  if (connCount==0)
  {
    myConn = ds.getConnection();
    stmt = myConn.createStatement();
  }
  connCount ++;
}
catch (Exception E)
{
  throw E;
}

}

/**断开数据库**/
protected void disConnection() throws Exception
{
try
{
connCount --;
if (connCount == 0)
{
try
{
if (stmt != null)
stmt.close();
myConn.close();
}
catch ( Exception e)
{
e.printStackTrace();
}
}
}
catch (Exception E)
{
throw E;
}
}

/**创建事务,包括数据连接**/
public void transactionBegin() throws Exception
{
begin();
connection();
}

/**事务提交,包括断开数据库**/
public void transactionCommit() throws Exception
{
disConnection();
commit();
}

/**事务回滚、包括断开数据库**/
public void transactionRollback() throws Exception
{
disConnection();
rollback();
}

public void showmessage(String str)
{
}
[/code]

上面是一个事务处理的基类,以前写的dao都是继承这个基类再实现一个接口

[code="java"]
this.transactionBegin();
/**
处理SQL,如果有多样执行的话,多条SQL涉及多张表
**/
this.stmt.executeUpdate(sql);
this.transactionCommit();

失败
this.transactionRollback();
[/code]

以前写的service是使用实现接口的dao来访问dao层

现在脑袋进入死锁状态,想不出,该如何解开这块事物,也就是把事务处理放在service..

怎么设计,该使用什么事务处理..
[b]问题补充:[/b]
如果用spring和hibernate我就自己去看书了..希望各位别在用框架回答了...
[b]问题补充:[/b]
谢谢抛出异常的爱

大概意思我明白..
[code="java"]
public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object result = null;

try {

//事务开始

begin();
result = method.invoke(this.delegate, args);

//执行原来的方法之后记录日志

commit(); //事务结束
} catch (Exception e) {

rollback();
e.printStackTrace();

}finally{

logger.error("finally运行");

}

//返回方法返回值给调用者

return result;

}

public static  void main(String arg[]) throws SQLException, InterruptedException{   
    MixUpper mx = (MixUpper) new DynaProxyHello().bind(new Tmalple());   
    mx.sonMother();   
    mx.updateFather();   
}   

[/code]

这样当mx.sonMonther()的时候就执行一次事务.
mx.updateFather()的时候又执行一次事务,如果updateFather的时候失败,不会回滚回mx.sonMonther()
还是说我理解错误
[b]问题补充:[/b]
如果涉及到多张表操作..

因为我想,每张表都有自己的dao处理方式,不会耦合在一起....

如何用一个代理类实现多个代理方法
[b]问题补充:[/b]
尝试了下代理,好象没什么效果..可能是我写错了
代理类
[code="java"]
import javax.transaction.UserTransaction;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import javax.naming.Context;

public class DynaProxyTransaction
implements InvocationHandler {
private Object object;

public Object bind(Object obj) {

    this.object = obj;
    return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),
                                  this.object.getClass().getInterfaces(), this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws
    Throwable {
    Object result = null;
    InitialContext ic = null;
    UserTransaction ut = null;
    ic = getContext();
    ut = getUserTransaction();
    try {
        System.out.println("事务开始");
        ut.begin();
        // JVM通过这条语句执行原来的方法(反射机制)
        result = method.invoke(this.object, args);
        ut.commit();
        System.out.println("事务提交");
    }
    catch (Exception e) {
        System.out.println("事务回滚");
        ut.rollback();
        e.printStackTrace();
    }
    // 返回方法返回值给调用者
    return result;
}

private InitialContext getContext() {
    InitialContext ic = null;
    try {
        ic = new InitialContext();
    }
    catch (NamingException e) {
        e.printStackTrace();
    }
    return ic;

}

private UserTransaction getUserTransaction() {
    UserTransaction ut = null;
    try {
        ut = (UserTransaction) getContext().lookup(
            "javax.transaction.UserTransaction");
    }
    catch (NamingException e) {
        e.printStackTrace();
    }
    return ut;

}

}
[/code]

dao层
[code="java"]
public class AdminDALImpl
implements AdminDAL {
Connection con = null;
private Context ctx = null;
private javax.sql.DataSource ds = null;
public AdminDALImpl() {
String JNDIName = "***";
try {
ctx = new InitialContext();
ds = (javax.sql.DataSource) ctx.lookup(JNDIName);
try {
con = ds.getConnection();
}
catch (SQLException ex1) {
ex1.getMessage();
}
}
catch (NamingException ex) {
ex.getMessage();
}

}

public void saveBean(String name, String pwd) throws Exception {
    java.sql.Statement st = null;
    String sql = "insert into t_admin (name,pwd) values ('" + name
        + "','" + pwd + "')";
    try {
        st = con.createStatement();
        st.executeUpdate(sql);
    }
    catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        throw new Exception(e.getMessage());
    }
    finally {
        con.close();
        st.close();
    }
}

}
[/code]

dao层
[code="java"]
public class TestDALImpl
implements TestDAL {
Connection con = null;
private Context ctx = null;
private javax.sql.DataSource ds = null;

public TestDALImpl() {
    String JNDIName = "***";
    try {
        ctx = new InitialContext();
        ds = (javax.sql.DataSource) ctx.lookup(JNDIName);
        try {
            con = ds.getConnection();
        }
        catch (SQLException ex1) {
            ex1.getMessage();
        }
    }
    catch (NamingException ex) {
        ex.getMessage();
    }

}

public void saveBean(String id, String name) throws Exception {
    java.sql.Statement st = null;
    String sql = "insert into test (id,name) values ('" +
        id
        + "','" + name + "')";
    try {
        st = con.createStatement();
        st.executeUpdate(sql);
    }
    catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        throw new Exception(e.getMessage());
    }
    finally {
        st.close();
        con.close();

    }
}

}
[/code]

服务层
[code="java"]
public class TestBusinessImpl implements TestBusiness{
public TestBusinessImpl() {
}

public void saveTest() throws Exception {
    AdminDAL adminDal = new AdminDALImpl();
    TestDAL testDal = new TestDALImpl();
        adminDal.saveBean("abcd", "abcd");
        testDal.saveBean("test","testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest");
}

}
[/code]

servlet 类
[code="java"]
public class TestServlet
extends HttpServlet {
public TestServlet() {
}

protected void doGet(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException,
    IOException {
    System.out.println("**进入servlet**");
    TestBusiness business = (TestBusiness)new DynaProxyTransaction().bind(new TestBusinessImpl());
    try {
        business.saveTest();
    }
    catch (Exception ex) {
        ex.getMessage();
    }
}

protected void doPost(HttpServletRequest request,
                      HttpServletResponse response) throws ServletException,
    IOException {
    // TODO Auto-generated method stub
    doGet(request, response);
}

[/code]

随便搭了一个平台,接口类我就没写了...
试了好多次..保存test的时候执行SQL是错误的,应该执行事务回滚.第一条好是插入进去了

sdh5724说的Anotation还没去研究

谢谢
[b]问题补充:[/b]
可能你们没看到我这样写是错误的...

我也知道没关联上.但是我不知道该如何关联上...
[b]问题补充:[/b]
既然决定发这个贴,就不怕受打击了...

但是还是请多多指教.既然不需要.为什么希望指明点,对事务处理这块碰到的比较少..
[b]问题补充:[/b]
[quote]
单一数据库, Connetion就能完成事务了, 也就是所说的数据库自身的事务管理:)
[/quote]

你是说jdbc自带的事务处理?

con.setAutoCommit(false);设置为手动提交?

那是如何让dao只执行数据库操作

难道把connection做参数传进去?
[b]问题补充:[/b]
我只是想理解这块的设计方式,如何设计是比较OK的,代码如何写才是比较好的.如何解决类似这样的问题..

抛出异常的爱让我知道可以用代理模式(很早以前就看过代理,一直没想到该用在什么地方)

sdh5724也让我知道我还有好都东西搞的乱七八糟,现在恶补中...

谢谢几位,

大家有什么意见还可以提,这并不是说要解决具体什么问题,只是一个想了解设计

思路来彻底的解开这耦合.我会准时结帖...

备注:Anotation应该是Annotation

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

18条回答 默认 最新

  • m_m1m2m3m4m5
    m_m1m2m3m4m5 2009-01-09 15:02
    已采纳

    [code="java"] MixUpper mx = (MixUpper) new DynaProxyHello().bind(new Tmalple()); [/code]

    你要把所有事务的边界定制在service的一个方法内
    这就是为什么三层架构了,
    dao不含事务,
    service的一个方法为一个事务.

    点赞 评论
  • weixin_42513366
    Candyut 2009-01-09 11:24

    建议使用spring框架,dao以注入的方式传递到service层,事物交给spring处理,在配置文件里面申明一下 比如addXXX的方法都是事务,或者用注解的方式 一个注解标签就搞定,基本对你来说是透明的.
    这个时候,在dao层可以使用spring提供的hibernate模板,简化了hibernate的使用,当然session也不用你手动去打开关闭了spring去管理

    点赞 评论
  • leonelwong
    马勒格彼得 2009-01-09 11:35

    注入是个很好的方法,我现在就是用spring处理的

    点赞 评论
  • m_m1m2m3m4m5
    m_m1m2m3m4m5 2009-01-09 13:06

    代理.把下面的日志换成事务处理就好.
    [code="java"]

    public class DynaProxyHello implements InvocationHandler {
    Logger logger ;
    /** //*
    * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象,如例子中的Hello)
    */
    private Object delegate;

    /** *//**
     * 动态生成方法被处理过后的对象 (写法固定)
     * 
     * @param delegate
     * @param proxy
     * @return
     */
    public Object bind(Object delegate) {
        logger =Logger.getLogger(delegate.getClass());
        this.delegate = delegate;
        return Proxy.newProxyInstance(
                this.delegate.getClass().getClassLoader(), 
                this.delegate.getClass().getInterfaces(), 
                this);
    }
    /** *//**
     * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
     * 此方法是动态的,不是手动调用的
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        try {
            //执行原来的方法之前记录日志
            logger.error("开始运行"+method.getName());
            //JVM通过这条语句执行原来的方法(反射机制)
            result = method.invoke(this.delegate, args);
            //执行原来的方法之后记录日志
            logger.error("结束运行"+result);
        } catch (Exception e) {
            logger.error("抛出异常");
            e.printStackTrace();
        }finally{
            logger.error("finally运行");
        }
        //返回方法返回值给调用者
        return result;
    }
    public static  void main(String arg[]) throws SQLException, InterruptedException{
        MixUpper mx = (MixUpper) new DynaProxyHello().bind(new Tmalple());
        mx.sonMother();
        mx.updateFather();
    
    }
    

    }
    [/code]

    点赞 评论
  • chhj_292
    chhj_292 2009-01-09 16:07

    使用代理调用service方法,在代理方法里加入事物管理。

    点赞 评论
  • u011537602
    不良校长 2009-01-09 23:46

    1。 你不可以在BaseDAL实现事务操作。不然, 你的DAO就会提交事务, 这是个很关键的地方, 也是你目前的做不到的。事务失败了。
    2。 现在问题来了, 事务什么时候开始启动, 事务什么时候结束。 这引出了2个事务模式: 一个是无事务模式, 一个是多个DAO事务执行。所以, 你应该是在Service里注入事务管理器。 也就是在Proxy方法执行前启动这个事务。 要不启动事务, 你可以在Service上定义 Anotation来申明。

    3。 这样做, 会造成性能问题, 比如太早启动了事务。 如果你有这么高的事务, 那么可以在DAO的方法上做Anotation, 在遇见启动事务的标记, 才开始启动事务。 结束事务, 应该还是在Service上结束事务。 你肯定会说, 这个事务管理对象获得不到了。 告诉你个技巧, 基本这样的对象是用ThreadLocal里保存的, 你要把事务对象放在ThreadLocal里, 这样, 你就能全程, 任意方法拦击方法, 随意的任意时刻建立事务对象了。 在Service执行完了, 提交事务, 或者回收对象。

    点赞 评论
  • u011537602
    不良校长 2009-01-09 23:47

    如果你想做的跟spring那么灵活, 我觉得代码并不会很少。 也很复杂的, 希望你明白我说的意思了。

    点赞 评论
  • m_m1m2m3m4m5
    m_m1m2m3m4m5 2009-01-10 11:44

    你确定 "javax.transaction.UserTransaction"事务可以控制 连接池"****"么

    [code="java"]ut = (UserTransaction) getContext().lookup(

    "javax.transaction.UserTransaction");

    ds = (javax.sql.DataSource) ctx.lookup(JNDIName);

    [/code]

    点赞 评论
  • u011537602
    不良校长 2009-01-10 11:48

    异常啊, 我也很迷惑, 他的UserTransaction是怎么控制到事务的。 不知道怎么配置的。 表面上看, 好像没有关联起来。

    点赞 评论
  • u011537602
    不良校长 2009-01-10 19:24

    不想打击你, UserTransaction 不是这么用的的。
    说实话根本不需要, UserTransaction是用来管理多个数据源的。而且需要每个数据源支持XA的结构。

    点赞 评论
  • u011537602
    不良校长 2009-01-10 20:19

    单一数据库, Connetion就能完成事务了, 也就是所说的数据库自身的事务管理:)

    点赞 评论
  • u011537602
    不良校长 2009-01-10 20:35

    我不是说了么,通过thread local 对象传递:)

    点赞 评论
  • iteye_9340
    iteye_9340 2009-01-11 13:08

    用spring框架,就可以在配置文件中spring的声明式事务处理来管理事务,在applicactionContext.xml中写下如下代码(在添加框架的时候,将此配置文件放在webroot的web-inf下面)

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 事务属性(新功能) 
        propagation="REQUIRED"(默认)事务传播行为(没有事务就传,有事务就用原来的)
        isolation="DEFAULT"   (默认)事务隔离级别(由dbms隔)
    
    -->
    <tx:advice id="mytx">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED"
                isolation="DEFAULT" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="saveorupdate" propagation="REQUIRED" />
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>
    
    
    <!-- 织入 -->
    <aop:config>
        <aop:advisor advice-ref="mytx"
            pointcut="execution(* com.service.*.*(..))" />
    </aop:config>
    

    这样子通过一个表达式对service层对事务进行统一管理。
    在这里我们要把beans标签里的一些东西替换掉:
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

    点赞 评论
  • leonelwong
    马勒格彼得 2009-01-12 09:11

    如果你坚持自己用事物的话,你能确定每次的事物都能很好的操作吗,如果遇到并发,锁表的情况,你就不好处理了

    点赞 评论
  • leonelwong
    马勒格彼得 2009-01-12 09:43

    说说我们现在的做法,看看能不能给你点启示,
    建立工程如下:
    [color=red]MODULE(操作数据库),[/color]定义接口和实现,等于是一张表中所有的操作都分为两个类,有定义的接口和具体的实现类,采用SPRING注入接口,然后调用接口类就OK了,如下:
    建立一个BEAN XML:名称如:module.xml






    [color=red]CORE(业务和事物处理),[/color]也是接口和具体的实现类,这个里面主要是业务和调用数据库中的方法,引用MODULE工程,采用SPING注入接口,如下:
    建立一个BEAN XML:名称如:core.xml






    WEB(交互层):这层主要是调用core层里面的具体实现方法,引用CORE和MODULE工程,这里有具体的spring事物处理,如下:



    省略其他配置,这里是配置数据源和事物

    ANT打包,把上面的打包成一个.WAR,这样就可以了,这样耦合就相当的低了

    点赞 评论
  • m_m1m2m3m4m5
    m_m1m2m3m4m5 2009-01-12 12:30

    如果是动态语言就不需要xml配置了.

    java打成jar包之后想要加点参数进去真难啊.
    callback方式都用着不爽......

    而xml又那么丑....
    如果能善用动态生成字节码
    java还是很有前途的.

    点赞 评论
  • wenswlifeyue
    平凡的傲娇 2009-01-12 16:05

    不是吧,我觉得应该是在业务逻辑层创建一个拦截器,在配置文件中用到面向方面的思想,把那些实现的东西放到一个切面上,这样不就实现事物在service层处理了?

    点赞 评论
  • iteye_235
    iteye_235 2009-01-13 10:11

    描述清楚问题就行了,不用发这多代码~~

    一种自己写AOP代码,对你的业务方法切面然后使用事务控制提交或者回滚。

    二种是使用注解@Annotation,比如使用Spring中的@Trsantional就是现成的例子,你找一下Spring的指南,我就不复制拷贝了。

    点赞 评论

相关推荐