搭建一个框架,如何做到事务在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

dao

18个回答

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

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

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

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

代理.把下面的日志换成事务处理就好.
[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]

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

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

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

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

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

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

"javax.transaction.UserTransaction");

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

[/code]

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

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

共18条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问