我来也! 请教一下,在纯的JDBC中,如何项目采用分层了DAO层,服务层和SERVLET控制层,,那么怎么在servlet中启用JDBC的事务?

解决方案 »

  1.   

    此回复为自动发出,仅用于显示而已,并无任何其他特殊作用
    楼主【ghyghost】截止到2008-07-05 18:18:10的历史汇总数据(不包括此帖):
    发帖的总数量:107                      发帖的总分数:1840                     
    结贴的总数量:99                       结贴的总分数:1750                     
    无满意结贴数:27                       无满意结贴分:590                      
    未结的帖子数:8                        未结的总分数:90                       
    结贴的百分比:92.52 %               结分的百分比:95.11 %                  
    无满意结贴率:27.27 %               无满意结分率:33.71 %                  
    值得尊敬
      

  2.   


    如果服务层没有用spring,不能用AOP拦截的话,可以在filter中控制事务,但粒度有点太大了有一篇文章 “用 JOTM 向Servlet中添加事务”,不知道能否对你有所帮助
    http://tech.ccidnet.com/art/1077/20030820/60272_1.html
      

  3.   

    事务的事情应该是DAO层分内的,没必要在servlet业务逻辑层开发。
      

  4.   

    感谢lnfszl,,要实现的效果就是将事务打包在servlet中,,,但他使用了JOTM来实现有没有原生JAVA的办法实现的,感谢大家继续讨论
      

  5.   

    dgqbcht:在DAO层中??不应该,,极大的情况下,我们需要在servlet中调用的所有的服务层的东西是一个整体,如果在servlet中调用某一个服务层出错,那么整个SERVLET的所有的东西全回滚,其实这个实现就是lnfszl网友给的地址想要的答案,但他使用的是三方类实现的
      

  6.   

    那就把dao层所需的数据库连接改为由参数传入,在servlet里管理数据库连接不就行了么。
    try {
    conn = ...;
    conn.setAutoCommit(false);
    callSomeFunction(conn, other params);
    conn.commit();
    }
    catch(Exception e) {
    conn.rollback();
    }
    finally{
    conn.setAutoCommit(true);
    //下面把conn关闭回收了
    ....
    }不知道回答了你的问题没
      

  7.   

    主要是你servlet的下一层的方法的接口的问题,如果servlet的下一层(应该是你说的服务层)里所需的数据库连接要求由调用者传入,那就可以由调用者来控制事务了。
      

  8.   

    嗯,小兵的问题可以解决这个问题,就是CONNECTION由参数来做,,我再想一想,考虑一下这个设计问题
      

  9.   

    头晕的很,想的!
    小兵,总感觉不太爽的感觉就是在SERVLET中将CONN对象由参数传给服务层有些不太爽,这样层一层之间就耦合了,服务层应该调用DAO方法才对,呵呵
      

  10.   

    或者这样也行,如果你不用spring的话,在dao和servlet之间做一个服务层,解决所有的事务问题,servlet层跟这个服务层打交道就行。
      

  11.   

    应该是服务层调用dao层,服务层控制事务。
    如果只是传Connection的话,也算不上耦合。
      

  12.   

    dgqbcht   那如果有多个服务层呢,,多个服务层共同操作是一个整体流程,那么如何保证 这个整体流程完整性呢?
      

  13.   

    poscard 
    我认为不应该是服务层控制事务应该在SERVLET控制事务SERVLET中有可能调用N个服务层,他们是一个整体啊,而不是一个服务是一个整体,是多个服务层的操作是一个整体
      

  14.   

    不过,如果是在servlet里决定完成一个业务功能需要在同一个事务里调用哪些方法(而这可能本该是你的服务层做的),那确实是有耦合了。
      

  15.   

    我先回家,现在在单位,,
    回家再研究,这个问题大家1000%都遇到,只不过避开了事务,至少我现在的公司是这样,,就有一个DB.JAVA文件,里面有QUERY,UPDATE方法,传进的是STRING SQL参数,,根本没有使用事务,我感觉这样很不好,很危险,所以特来请教,先下,回家再继续!
      

  16.   

    小兵,,事常的情况下,服务层的确是控制事务的,,但如果你这样做,,服务层控制事务的粒度就太细了,,我想粗粒度,就想在SERVLET中将所有调用的服务层加事务,,这样SERVLET中所有的东西都是一个完整性了,呵呵
      

  17.   

    事务上下文模式ThreadLocal 绑定 Connection,事务代码采用动态代理业务方法,代理的结构如下:try {
        con.begin
        method.invoke(...)
        con.commit
    }catch() {
        con.rollback
    }如果完成一个业务逻辑需要调用到多个业务类中的方法的话,这样得使用门面模式把多个业务类
    集合成为一个,将这个门面进行代理。当然了,如果你想让 Connection 出现在你的业务方法中
    的话,那么动态代理这一步就可以省略掉。业务逻辑方法调用 DAO 中的 Connection 通过 ThreadLocal 得到,
      

  18.   

    给你一个纯JDBC MVC模式的写法的例子吧,分为VO,DAO,BIZ,SERVLET
    VO:public class DataVo {
    long dms_id; public long getDms_id() {
    return this.dms_id;
    } public void setDms_id(long str) {
    this.dms_id = str;
    } String dms_title; public String getDms_title() {
    return this.dms_title;
    } public void setDms_title(String str) {
    this.dms_title = str;
    } java.sql.Date dms_addtime; public java.sql.Date getDms_addtime() {
    return this.dms_addtime;
    } public void setDms_addtime(java.sql.Date str) {
    this.dms_addtime = str;
    } String dms_content; public String getDms_content() {
    return this.dms_content;
    } public void setDms_content(String str) {
    this.dms_content = str;
    } long nodeId; public long getNodeId() {
    return this.nodeId;
    } public void setNodeId(long str) {
    this.nodeId = str;
    }

    String text;

    public String getText() {
    return this.text;
    }

    public void setText(String str){
    this.text=str;
    } String parm = "dms_id,dms_title,dms_addtime,dms_content,nodeId"; public String getParm() {
    return parm;
    } public void setParm(String parm) {
    this.parm = parm;
    } String parm1 = "dms_title,dms_content,nodeId"; public String getParm1() {
    return parm1;
    } public void setParm1(String parm1) {
    this.parm1 = parm1;
    } String parm2 = "dms_title=?,dms_content=?,nodeId=? where dms_id=?"; public String getParm2() {
    return parm2;
    } public void setParm2(String parm2) {
    this.parm2 = parm2;
    }
    }
      

  19.   

    BIZ:import java.sql.Connection;
    import java.util.List;
    import java.util.Vector;import common.jiangbin.dms.DBConnection;
    import common.jiangbin.dms.SplitPageCommon;
    public class DataBiz {
    public void insert(DataVo vo) {
    DBConnection dbc = new DBConnection();
    if (dbc.getConnect()) {
    Connection conn = dbc.getConn();
    try {
    DataDao dao = new DataDao();
    dao.insert(conn, vo);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (conn != null) {
    conn.close();
    conn = null;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } else {
    System.out.println("数据库连接失败!");
    }
    } public List findbyAll(SplitPageCommon spvo,String sqlw) {
    Vector v = new Vector();
    DBConnection dbc = new DBConnection();
    if (dbc.getConnect()) {
    Connection conn = dbc.getConn();
    try {
    DataDao dao = new DataDao();
    v = dao.findbyAll(conn,spvo,sqlw);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (conn != null) {
    conn.close();
    conn = null;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } else {
    System.out.println("数据库连接失败!");
    }
    return v;
    } public DataVo findbykey(DataVo vo) {
    DataVo dvo = new DataVo();
    DBConnection dbc = new DBConnection();
    if (dbc.getConnect()) {
    Connection conn = dbc.getConn();
    try {
    DataDao dao = new DataDao();
    dvo = dao.findbykey(conn, vo);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (conn != null) {
    conn.close();
    conn = null;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } else {
    System.out.println("数据库连接失败!");
    }
    return dvo;
    } public void update(DataVo vo) {
    DBConnection dbc = new DBConnection();
    if (dbc.getConnect()) {
    Connection conn = dbc.getConn();
    try {
    DataDao dao = new DataDao();
    dao.update(conn, vo);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (conn != null) {
    conn.close();
    conn = null;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } else {
    System.out.println("数据库连接失败!");
    }
    } public void delete(DataVo vo) {
    DBConnection dbc = new DBConnection();
    if (dbc.getConnect()) {
    Connection conn = dbc.getConn();
    try {
    DataDao dao = new DataDao();
    dao.delete(conn, vo);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (conn != null) {
    conn.close();
    conn = null;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } else {
    System.out.println("数据库连接失败!");
    }
    }
    }
      

  20.   

    Servlet:import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.List;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;import common.dms.SplitPageCommon;
    public class DataServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    this.doPost(request, response);
    } public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    int _act = 1;
    String act = request.getParameter("act");
    if (act != null && !act.equals("")) {
    _act = Integer.parseInt(act);
    } else {
    _act = 1;
    }
    switch (_act) {
    case 1: // 添加资料
    this.insert(request, response);
    break;
    case 2: // 修改某条记录
    this.update(request, response);
    break;
    case 3: // 删除某一条数据
    this.delete(request, response);
    break;
    }
    }
    //1:添加资料
    public void insert(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String dms_title=request.getParameter("dms_title");
    String dms_content=request.getParameter("dms_content");
    String nodeId=request.getParameter("nodeId");
    DataVo vo=new DataVo();
    vo.setDms_title(dms_title);
    vo.setDms_content(dms_content);
    vo.setNodeId(Long.parseLong(nodeId));
    DataBiz biz=new DataBiz();
    biz.insert(vo);
    //request.getRequestDispatcher("DataDervlet?act=2&nodeId="+nodeId).forward(request, response);
    String path = request.getContextPath();
    response.sendRedirect(path+"/DataServlet?act=2&nodeId="+nodeId);
    }
    //2:修改某条记录
    public void update(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String dms_id=request.getParameter("dms_id");
    String dms_title=request.getParameter("dms_title");
    String dms_content=request.getParameter("dms_content");
    String nodeId=request.getParameter("nodeId");
    DataVo vo=new DataVo();
    vo.setDms_title(dms_title);
    vo.setDms_content(dms_content);
    vo.setNodeId(Long.parseLong(nodeId));
    vo.setDms_id(Long.parseLong(dms_id));
    DataBiz biz=new DataBiz();
    biz.update(vo);
    String path = request.getContextPath();
    response.sendRedirect(path+"/TreeServlet?act=2&type=3&nodeId="+nodeId);
    }
    //3:删除某条记录
    public void delete(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String nodeId=request.getParameter("nodeId");
    String dms_id=request.getParameter("dms_id");
    DataVo vo=new DataVo();
    vo.setDms_id(Long.parseLong(dms_id));
    DataBiz biz=new DataBiz();
    biz.delete(vo);
    String path = request.getContextPath();
    response.sendRedirect(path+"/TreeServlet?act=2&type=3&nodeId="+nodeId);
    }
    }
    这个应该就是一个比较标准的纯的JDBC MVC的写法了,事业操作全都应该在BIZ层中进行
      

  21.   

    jdbc有事务控制,一旦发生上面错误向外抛异常(不提交事物),servlet捕捉异常,根据不同的异常转到不同的页面
      

  22.   

    绝对应该在服务层。
    事务是以业务逻辑为基础的
    一个完整的业务应该对应服务层里的一个方法
    如果业务操作失败,则整个事务失败。DAO层应该保证操作的原子性,就是说DAO里的每个方法都应该是不可以分割的。所以事务是肯定没有必要加在DAO这一层的同样,对于Servlet,每一次请求对应的可能不止一个业务,打个比方,做一个删除请求,删除成功了之后肯定还要返回显示列表吧,那么这一次请求应该包含两个独立的业务逻辑。那么是不是说本来删除成功了,但在查询显示列表的时候出现在异常,就把删除的操作也回滚呢?
      

  23.   

    感谢你的回答,很有见解,----
    同样,对于Servlet,每一次请求对应的可能不止一个业务,打个比方,做一个删除请求,删除成功了之后肯定还要返回显示列表吧,那么这一次请求应该包含两个独立的业…
    ----这个例子举的比较牵强,,,如果是删除一个主贴呢,,那么跟 贴是必须要删除的,,也就是说,SERVLET中的所有的操作是一个整体,,,,另外Service层有可能一个业务功能需要调用N个方法,这种情况怎么办??这N个方法是一个整体,,,
      

  24.   

    调用多个方法,如果是这样的话,在 Servlet 中只能侵入 Connection 对象了,否则可以采用低侵入式的。下面是 Servlet 中进行修改的部分:Connection con = null;
    ConnectionManager manager = ConnectionManager.getInstance();
    try {
        con = manager.getConnection();
        con.setAutoCommit(false);
        service1.s1();
        service2.s2();
        service3.s3();
        con.commit;
    }catch(Exception e) {
        con.rollback;
    }finally{
        if(con!=null) {
            con.close();
        }
        manager.removeConnection();
    }ConnectionManager 采用 ThreadLocal<Connection> 对象作为 Connection 的存储容器。
    getConnection() 中的大致代码(仅供参考,手工写的,没有测试过):public ConnectionManager {
        private static ThreadLocal<Connection> cons = new ThreadLocal<Connection>();
        private static ConnectionManger manger = new ConnectionManager();    private ConnectionManger() {
        }    // 不使用单例的话,把下面的这些方法改成静态的也行
        public static ConnectionManager getInstance() {
             return manager;
        }    public Connection getConnection() throws SQLException {
             Connection con = cons.get();
             if(con == null) {
                 // 从 ConnectionFactory 中获得一个连接
                 con = ConnectionFactory.getConnection();
                   setConnection(con);
             }
             return con;
        }    public void removeConnection() {
            cons.remove();
        }    private void setConnection(Connection con) {
            cons.set(con);
        }
    }Service 层的代码可以不用动了。DAO 的代码得进行重构:1,Connection 从 ConnectionManager 的 getConnection 中获得;
    2,DAO 层中的 con.close 得全部去掉,就是不关,因为关掉的话,一个事务点就中止了;
    3,DAO 层中的凡是抛出 SQLException 的地方在 catch 块中,重新包装一个自定义的 RuntimeException
        再进行抛出,因为事务层放到了 Servlet 中,这里必须把异常抛出,否则在高层就没办法判断是运行正常还是
         产生异常了。之所以使用 RuntimeException 主要是考虑到 service 层中不用再去修改了,如果是直接抛出
         SQLException 的话,由于它是检查型异常必须处理的,这样得在 service 的每个方法后都 throws SQLException
        这样的话,有点太烦了,也不是很好。DAO 重构后的代码基本上如下面这样:Connection con = null;
    PreparedStatement ps = null;
    try {
        con = ConnectionManager.getConnection();
        ps = con.prepared....;
        ps.execute(....);
    }catch(SQLException e) {
        throw new WrapperSQLException(e);
    }finally {
        if(ps != null) {
            try {
                ps.close();
            }catch(SQLException e) {
                throw new WrapperSQLException(e);
            }
        }
        // 这里的 con 不能关
    }由于使用的 ThreadLocal 来存储 Connection,每个请求是独立的线程 ThreadLocal 中也只有一个 Connection
    使用这个就可以将 Connection 进行传递了,而无需将 Connection 当作参数一层一层地传递下去了。另外,使用了 ThreadLocal,也不会产生线程安全的问题。如果,你的 Servlet 中完成一个业务,只是调用一个业务层的方法的话,那就更加方便了,Servlet 中的那段代码
    只要使用动态代理将其切入到业务层对象中,直接调用业务类的代理对象就行了,这样就不需要让 Connection 侵入
    代码中了。当然了,调用多个业务方法,采用代理进行处理的话会很麻烦,因为多个方法必须纳入到一个事务中的去,涉及事务的
    传播属性,一般采用方法栈的方式进行处理,这得考虑很多的问题,类的结构需要精心设计。
      

  25.   

    否则每个 Servlet 中都得嵌入这些事务处理语句了,感觉不好。
      

  26.   

    你好哈,不要叫我前辈哦,我不是什么前辈哦 :)我也没实践做过,试试看吧,只作为讨论,也不知道有没有实际用处,呵呵就是把 30 楼的第一段代码搬到 Filter 中来doFilterConnection con = null;
    ConnectionManager manager = ConnectionManager.getInstance();
    try {
        con = manager.getConnection();
        con.setAutoCommit(false);
        chain.doFilter(request, response);
        con.commit;
    }catch(Exception e) {
        con.rollback;
    }finally{
        if(con!=null) {
            con.close();
        }
        manager.removeConnection();    
    }
      

  27.   

    说实在的,我认为这个帖子的题目和出发点很好,建议加精,能让更多的人进来
    讨论一下。想想大家大多数都用 Struts + Spring + Hibernate 吧,事务处理往 Spring
    的配置中一加好,就万事大吉了。估计很少有人会去研究为什么代码中一个
    Connection 代码都看不到,而同样有着各种方式的事务管理呢?这个帖子的标题,用到的技术是底层的 Servlet + JDBC 与任何框架没有依赖
    关系,从最底层自行实现事务管理,呵呵,难度应该不小,如果完成的话,那
    就会惊叹 Spring 中的事务管理模块的处理方式。看看楼上的回复,大多数都建议在业务层中处理事务,但是我突然想到个问题。
    下面是业务层中的方法:public void a() {
       con.setAutoCommit;
       dao.a();
       b();         // 调用 b 方法
       dao.a();
       con.commit;
    }public void b() {
       con.setAutoCommit;
       dao.b();
       dao.b();
       con.commit;
    }public void c() {
       con.setAutoCommit;
       dao.c();
       dao.c();
       con.commit;
    }注明一下,con.begin 和 con.commit 是通过动态代理实现的,并不是每个方法中手工加上去的。在 Servlet 中调用:service.a();
    service.c();这样的话,a c 根本不能处理同一事务控制下,因为事务的边界在 Servlet 层,
    并不在 Service 层中。所以在 Service 层中写事务控制语句是不对的,应该再
    更高一层,Servlet 层,或者再建一个 Facade 层,把这些方法写到 Facade 层
    中的一个方法中,在这个方法里处理事务。但是弄个 Facade 这样的话我们又会碰到事务的传播性这个问题,比如下面的代码:public void facade1() {
        con.begin
        service.a();
        service.b();
        facade2();      // 调用 facade2 方法
        con.commit;
    }public void facade2() {
        con.begin
        service.e();
        service.f();
        con.commit;
    }在 facade1 中调用了 facade2 方法,注意在 facade2 中本身有事务,这时再将其
    纳入了 facade1 中的事务,但根据事务的概念,facade2 在纳入 facade1 中,如果
    facade1 中有事务,就加入 facade1 中的事务,自己的事务就取消了,如果没有的
    话,就打开自己的事务处理,这个就是事务传播性问题。如果要从 AOP 切入事务处理的话,事务传播性是必须考虑到的问题,PS 一下,Spring
    就是这么做的。如果那样做的话,事务处理模块将会变得复杂,但是这样也有好处,那就是是不依赖
    于框架、平台和架构,能在 Web 程序或者是 Swing 程序中使用。当然了,如果是专门针对于 Web 的事务管理,就不要那么复杂了,直接在 Filter
    中进行事务处理,每个请求,服务器都开启一个线程,这样我们就可以在线程中绑定
    Connection,在 DAO 中从当前线程中获得 Connection,在 Filter 中处理事务,在
    JDK 中正好提供了 ThreadLocal 这样一个类,可以在当前的线程中绑定对象,呵呵。上面是我对这个帖子事务处理的一些看法,仅仅是凭空想象的,还没实践过,在这里
    仅仅作为讨论,希望大家都能参与一下。
      

  28.   

    不要在servlet中
    servlet一般都是做控制层的
    放到逻辑层(javabean)中
      

  29.   

    应用模板模式,将事务的处理放在父类中,该父类先继承httpservlet,所有的servlet都可继承这个父类
      

  30.   

    添加一个service层 然后事务坐在 service层 
    controller调用service
    service中为 dao的某个或者某几个方法的组合如果不用spring 那么就用filter来实现
      

  31.   

    我觉得事务的界限不应该是dao层,应该是逻辑层毕竟已个逻辑可能用到多个dao方法。而事务的控制的话,就是传递connection,可以将connection放在ThreadLocal中,这样就可以不用手动的传递了