一个使用低层jdbc+MVC来操作的项目中,要实现事务的控制该怎么处理?例如:有一个业务用户提交一个表单,后台就同时向表A,表B,表C中插入数据,那么在biz中处理时一个方法中就必须对事务控制了(项目中没有spring)。能想到的解决方案:在dao中不捕异常,直接把异常扔出,在biz中捕捉。如果没有异常就提交,否则回滚,这样的话就必须靠connection对象来控制,向三个表中插入数据都要基于同一个connection了。这样觉得很别扭,在biz中try-catch_fianlly.
    应该怎么实现呢?不要说spring了,项目中不能加的,spring没有出来的时候前人是怎么做事务处理的?
    路过的帮帮忙,很急....

解决方案 »

  1.   

    如果只有jdbc,好像是只能使用Connection的事务处理。这样就只能在Dao层中处理,如果在biz中处理,要绕过Dao层。
      

  2.   

    在业务层控制事务,即在业务方法执行完后commit,调用dao可以将connection作为参数传过去
      

  3.   

    spring没出来前就是你那么做的
    不过可以考虑动态代理的你的BIZ
      

  4.   

    spring的openSessionInView也是使用过滤器实现的啊。
    过滤器开始打开事物,最后提交事务就成了。不过中间还牵扯到一些查询的时候是否要开始事务,这个你自己去处理啦
      

  5.   

    spring没有出现以前我们都是通过jdbc来控制事物的,所以你要看Connection这个类的帮助文档。楼上的估计都是从spring开始的,就没有写过jdbc的事物管理。
      

  6.   

    http://blog.csdn.net/friendliu/archive/2008/07/01/2602455.aspx去看看
      

  7.   

       有一个业务处理,现在确定的是提交一个表单,正常走流程的话会一次向8张不同的表中插入数据。而项目是低层的jdbc来做的...所以想知道低层事务的管理
      

  8.   

    在 ThreadLocal 绑定 Connection,在 DAO 中从 ThreadLocal 中获得 Connection使用动态代理,将业务逻辑方法代理,在代理中获得 Connection 并置于 ThreadLocal 中,并在代理中处理事务,这样的话这一业务逻辑调用所有的 DAO 方法均纳入到了一个事务之中。楼主提的问题是 J2EE 设计模式之一——事务上下文模式对于事务上下文模式介绍最详尽的书位于《J2EE 设计模式》一书中:
    网站:http://www.oreilly.com.cn/book.php?bn=7-5083-3099-4下面这些帖子有同样的问题,可以参考一下:jdbc如何实现事务处理?(例如先进行读取在进行修改能在一个事务里)
    http://topic.csdn.net/u/20090323/17/29237aab-ee3e-4e16-b9c4-53856425022c.html请教一下,在纯的JDBC中,如何项目采用分层了DAO层,服务层和SERVLET控制层,,那么怎么在servlet中启用JDBC的事务?
    http://topic.csdn.net/u/20080705/18/d894b5e5-8b7b-4560-af16-e4b9d8405681.html将connection存放在Threadlocal里和数据库连接池的区别
    http://topic.csdn.net/u/20080731/22/9af1fa06-2c82-4790-bee0-0274a66f3ae9.html大家来讨论下业务层(BO)该做什么事?该怎样做事?
    http://topic.csdn.net/u/20090528/21/160f4629-8985-4a05-89ed-7b4bdcf9bb0f.html多个线程共享一个connection的问题
    http://topic.csdn.net/u/20071128/12/6f085b57-a1b5-4100-a036-f43ef5ab3f8c.html
      

  9.   

    这里还有一个帖子,虽然涉及 JPA 的,但在 J2SE 环境下 JPA 与 JDBC 没啥区别:EJB3之JPA程序结构,完美的异常处理
    http://topic.csdn.net/u/20090829/20/6e73877e-af46-4f98-9fce-f574495b550c.html声明一下,我不认同这位帖主的做法(其采用 Runnable 接口),不过可以借鉴一下,作为一种思路也行。
      

  10.   

    恩,对,现在都用spring了,都不知道jdbc底层对事务的处理了,学习学习..
      

  11.   

    我们系统中也要用到,不过我们是使用 JPA 的,但是 JPA 在 J2SE 环境中跟 JDBC 没啥区别,下面这些是我的关键类和代码:JpaEntityManager 管理类,从该类产生 EntityManager 对象,管理当前线程中绑定的对象:
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;import net.blogjava.frankiegao123.log.Log;
    import net.blogjava.frankiegao123.log.Logger;/**
     * JPA EntityManager 管理工具<br />
     *
     * 从 EntityManagerFactory 获得 EntityManager 对象并绑定到当前线程中,以便进行事务管理
     *
     * @author frankiegao123
     * 2009-10-30 下午12:16:41
     */
    public class JpaEntityManager {    private static Log log = Logger.getLog(JpaEntityManager.class);    private static EntityManagerFactory factory;    /**
         * 本地线程容器
         */
        private static ThreadLocal<EntityManager> entityManagerThreadBind = new ThreadLocal<EntityManager>();    static {
            try {
                // 初始化 EntityManagerFactory
                factory = Persistence.createEntityManagerFactory(Config.getInstance().getJpaUnitName());
            } catch(Exception e) {
                log.error("EntityManagerFactory init failed", e);
            }
        }    private JpaEntityManager() {
        }    /**
         * 从当前线程中获得已经绑定在线程上的 EntityManager 对象
         * @return
         * @author frankiegao123
         * 2009-10-30 下午12:35:15
         */
        public static EntityManager getEntityManager(){
            EntityManager entityManager = findThreadEntityManager();
            if(entityManager == null) {
                // 没有绑定时,抛出异常
                throw new ThreadEntityManagerException("EntityManager cannot be found from current thread");
            }
            log.info("find EntityManager: {0} from current thread", entityManager);
            return entityManager;
        }    /**
         * 关闭当前线程中的 EntityManager 对象,同时清空当前线程中 EntityManager,便于下次重新绑定执行事务
         *
         * @param entityManager
         * @author frankiegao123
         * 2009-10-30 下午12:37:11
         */
        static void closeEntityManager(EntityManager entityManager) {
            EntityManager em = findThreadEntityManager();
            log.debug("current thread EntityManager: {0}, parameter EntityManager: {1}", em, entityManager);
            if(em == null) {
                log.warn("EntityManager object in current thread is null, Using method invoke parameter EntityManager: {0}", entityManager);
                em = entityManager;
            }
            em.close();
            log.debug("EntityManager object close success");
            entityManagerThreadBind.set(null);
            log.info("current EntityManager object has been closed and the object in ThreadLocal has been set null");
        }    /**
         * 获得新的 EntityManager 对象,并绑定到当前线程中
         *
         * @return
         * @author frankiegao123
         * 2009-10-30 下午12:38:15
         */
        static EntityManager bindThreadEntityManager() {
            EntityManager preEntityManager = findThreadEntityManager();
            if(preEntityManager != null) {
                // 当前线程中还存在之前没有被处理的 EntityManager 对象
                log.warn("pre EntityManager not be closed, EntityManager: {0}", preEntityManager);
                closeEntityManager(preEntityManager);
            }
            EntityManager entityManager = factory.createEntityManager();
            setEntityManager(entityManager);
            log.debug("EntityManager object was binded to current thread, EntityManager: {0}", entityManager);
            return entityManager;
        }    /**
         * 从本地线程中获得 EntityManager 对象
         * @return
         * @author frankiegao123
         * 2009-10-30 下午12:18:44
         */
        private static EntityManager findThreadEntityManager() {
            return entityManagerThreadBind.get();
        }    private static void setEntityManager(EntityManager entityManager) {
            entityManagerThreadBind.set(entityManager);
        }
    }TransactionProxy 使用 JDK 的 Proxy 实现 AOP 用于给业务类加上事务处理(这代码比较多,放在楼下)下面这些是业务事务代理工厂类 ServiceFactory,业务接口 Service, 实现类 GaoService,DAO 类 GaoTestDao,以及 JpaBaseDao 的代码:public class ServiceFactory {    private static Service service = null;
        
        static {
            service = TransactionProxy.delegate(Service.class, new GaoService());
        }
        
        public static Service getService() {
            return service;
        }
    }public interface Service {
        public GaoTest process(Lottery lottery);
    }class GaoService implements Service {
        
        private static int k = 3000;
        
        private static Log log = Logger.getLog(GaoService.class);
        
        public GaoTest process(Lottery lottery) {
            GaoTest gt = new GaoTest();
            gt.setName(lottery.getCode());
            gt.setTime(new Date());      
            GaoTestDao dao = new GaoTestDao();
            dao.persist(gt);        
            GaoTest t = dao.find("c229e808-f1ce-4bab-9af0-9637e3c2c487");
            if(t != null) {
                t.setName("Gao" + (k++));
            }        
            dao.merge(t);
            return gt;
        }
    }public class GaoTestDao extends JpaBaseDao<GaoTest> {
        @SuppressWarnings("unchecked")
        public List<GaoTest> findGaoTestByName(String name) {
            String jpql = "SELECT t FROM GaoTest t WHERE t.name = :name";
            EntityManager em = JpaEntityManager.getEntityManager();
            Query q = em.createQuery(jpql).setParameter("name", name);
            return q.getResultList();
        }
    }import java.io.Serializable;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;import javax.persistence.EntityManager;public class JpaBaseDao<T> implements BaseDao<T> {
        
        private Class<T> entityClass;
        
        @SuppressWarnings("unchecked")
        public JpaBaseDao() {
            Type genType = getClass().getGenericSuperclass();
            Type[] params = ((ParameterizedType)genType).getActualTypeArguments();
            entityClass = (Class)params[0];
        }    public T find(Serializable id) {
            EntityManager em = JpaEntityManager.getEntityManager();
            return em.find(entityClass, id);
        }    @SuppressWarnings("unchecked")
        public List<T> getAll() {
            EntityManager em = JpaEntityManager.getEntityManager();
            String jpql = "SELECT t FROM " + entityClass.getName() + " t"; 
            return em.createQuery(jpql).getResultList();
        }    public void merge(T t) {
            EntityManager em = JpaEntityManager.getEntityManager();
            em.merge(t);
        }    public void persist(T t) {
            EntityManager em = JpaEntityManager.getEntityManager();
            em.persist(t);
            em.flush();
        }    public void remove(T t) {
            EntityManager em = JpaEntityManager.getEntityManager();
            em.remove(t);        
        }
    }
      

  12.   

    import java.lang.reflect.AnnotatedElement;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;import javax.persistence.EntityManager;
    import javax.persistence.EntityTransaction;import net.blogjava.frankiegao123.log.Log;
    import net.blogjava.frankiegao123.log.Logger;/**
     * 事务代理
     * @author frankiegao123
     * 2009-10-30 上午10:25:57
     */
    public class TransactionProxy {    private static Log log = Logger.getLog(TransactionProxy.class);    @SuppressWarnings("unchecked")
        public static <T> T delegate(Class<T> clazz, Object obj) {
            if(!isRequiredTransaction(obj.getClass())) {
                log.debug("[{0}] never transaction", obj.getClass().getSimpleName());
                return (T)obj;
            }
            log.debug("[{0}] require transaction", obj.getClass().getSimpleName());
            // 返回代理对象
            return (T)Proxy.newProxyInstance(
                        obj.getClass().getClassLoader(),
                        obj.getClass().getInterfaces(),
                        new XAInvocationHandle(obj)
                    );
        }    /**
         * 检查某一对象是否需要使用事务
         * @param obj
         * @return
         * @author frankiegao123
         * 2009-10-30 上午10:26:58
         */
        private static boolean isRequiredTransaction(AnnotatedElement obj) {
            TransactionAttribute type = obj.getAnnotation(TransactionAttribute.class);
            if((type == null) || TransactionAttributeType.REQUIRED.equals(type.value())) {
                return true;
            }
            return false;
        }    private static void logArguments(Object obj, Method method, Object[] args) {
            if(log.isDebugEnabled()) {
                if(args == null || args.length == 0) {
                    log.debug("[{0}#{1}] invoke params is empty", obj.getClass().getSimpleName(), method.getName());
                    return;
                }
                StringBuilder sb = new StringBuilder();
                for(int i = 0; i < args.length; i++) {
                    if(i > 0) {
                        sb.append(", ");
                    }
                    sb.append(args[i].getClass().getSimpleName()).append(": ").append(args[i]);
                }
                log.debug("[{0}#{1}] invoke params: ", obj.getClass().getSimpleName(), method.getName(), sb.toString());
            }
        }    /**
         * 使用 JDK 动态代理织入事务处理
         * @author frankiegao123
         * 2009-10-30 上午11:56:47
         */
        private static class XAInvocationHandle implements InvocationHandler {        private Object delegate;        public XAInvocationHandle(Object delegate) {
                this.delegate = delegate;
            }        public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                logArguments(delegate, method, args);            if(isRequiredTransaction(method)) {
                    // 需要事务处理
                    log.debug("[{0}#{1}] method require transaction", delegate.getClass().getSimpleName(), method.getName());
                    return invokeWithTransactionRequired(proxy, method, args);
                } else {
                    // 不需要事务处理
                    log.debug("[{0}#{1}] method never transaction", delegate.getClass().getSimpleName(), method.getName());
                    return invokeWithTransactionNever(proxy, method, args);
                }
            }        private Object invokeWithTransactionNever(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj = null;
                EntityManager em = null;
                try {
                    em = JpaEntityManager.bindThreadEntityManager();
                    obj = method.invoke(delegate, args);
                } catch(Throwable e) {
                    log.error("[invokeWithTransactionNever] invoke error", e);
                    throw e;
                } finally {
                    if(em != null) {
                        JpaEntityManager.closeEntityManager(em);
                    }
                }
                return obj;
            }        private Object invokeWithTransactionRequired(Object proxy, Method method, Object[] args) throws Throwable {
                EntityManager em = null;
                EntityTransaction transaction = null;
                Object obj = null;
                try {
                    em = JpaEntityManager.bindThreadEntityManager();
                    transaction = em.getTransaction();
                    transaction.begin();
                    log.info("[{0}#{1}] EntityManager: {2}", delegate.getClass().getSimpleName(), method.getName(), em);
                    log.debug("[{0}#{1}] transaction begin, isActive: {2}", delegate.getClass().getSimpleName(), method.getName(), transaction.isActive());
                    obj = method.invoke(delegate, args);
                    log.debug("[{0}#{1}] transaction commit start, isActive: {2}", delegate.getClass().getSimpleName(), method.getName(), transaction.isActive());
                    transaction.commit();
                    if(log.isDebugEnabled()) {
                        log.debug("[{0}#{1}] transaction commit end, isActive: {2}", delegate.getClass().getSimpleName(), method.getName(), transaction.isActive());
                    }else if(log.isInfoEnabled()){
                        log.info("[{0}#{1}] transaction commit success", delegate.getClass().getSimpleName(), method.getName());
                    }
                } catch (Throwable e) {
                    if(transaction != null && transaction.isActive()) {
                        log.error("[{0}#{1}] transaction rollback start, isActive: {2}", delegate.getClass().getSimpleName(), method.getName(), transaction.isActive());
                        transaction.rollback();
                        log.error("[{0}#{1}] transaction rollback ok, isActive: {2}", delegate.getClass().getSimpleName(), method.getName(), transaction.isActive());
                    } else {
                        log.error("[{0}#{1}] transaction is null or transaction is not active", delegate.getClass().getSimpleName(), method.getName());
                    }
                    log.error("[invokeWithTransactionRequired] invoke error", e);
                    throw e;
                } finally {
                    if(em != null) {
                        JpaEntityManager.closeEntityManager(em);
                    }
                }
                return obj;
            }
        }
    }如果是 JDBC 的话,只要把 EntityManager 改成 Connection 就差不多了
      

  13.   

    下面这个是测试类,根据测试代码的 DEBUG 日志输出结果,以及手工加入异常时,JPA 操作均在事务管理之下:public class TransactionTest {    public static void main(String[] args) {
            for(int i = 0; i < 10; i++) {
                new MultiThreadTest().start();
            }
        }    private static class MultiThreadTest extends Thread {
            public void run() {
                for(int i = 0; i < 10; i++) {
                    try {
                        Lottery lottery = new Lottery();
                        lottery.setCode(Thread.currentThread().getName() + "_" + i);
                        Service service = ServiceFactory.getService();
                        GaoTest t = service.process(lottery);
                        System.out.println(t);
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }        
        }
    }
      

  14.   

    如果业务不是太复杂或者多标同时操作的机会不是太多的话没必要那么复杂
    只需ThreadLocal绑定Connection以保证其在线程内的唯一性
    然后将事务提到业务层就可以了