一个使用低层jdbc+MVC来操作的项目中,要实现事务的控制该怎么处理?例如:有一个业务用户提交一个表单,后台就同时向表A,表B,表C中插入数据,那么在biz中处理时一个方法中就必须对事务控制了(项目中没有spring)。能想到的解决方案:在dao中不捕异常,直接把异常扔出,在biz中捕捉。如果没有异常就提交,否则回滚,这样的话就必须靠connection对象来控制,向三个表中插入数据都要基于同一个connection了。这样觉得很别扭,在biz中try-catch_fianlly.
应该怎么实现呢?不要说spring了,项目中不能加的,spring没有出来的时候前人是怎么做事务处理的?
路过的帮帮忙,很急....
应该怎么实现呢?不要说spring了,项目中不能加的,spring没有出来的时候前人是怎么做事务处理的?
路过的帮帮忙,很急....
解决方案 »
- 提问,有人使用过Jsoup吗,遇到一个选择标签的问题,进来帮看看,谢谢
- axis2 和 spring 整合,有这方面经验和知道都请进来看看
- SSH框架配置事物,Action出错,费时费力不明白,求指导!!
- JBPM
- 初学,想了解一下Struts+Hibernate+Spring
- 一个etmvc数据库操作异常
- 求教RMI in Linux问题
- 在hibernate中运行程序中要javax/transaction/Syn这个包啊!!实在不知道在哪儿可得到
- struts中日期验证的正则表达式
- 紧急求助:weblogic6.1如何设置支持web-app_2_3.dtd标准的web应用??
- 请问一个无状态的ejb的问题,我刚接触ejb,测试无状态ejb的时候有很严重的问题,实在想不明白
- 关于struts2标签的问题
不过可以考虑动态代理的你的BIZ
过滤器开始打开事物,最后提交事务就成了。不过中间还牵扯到一些查询的时候是否要开始事务,这个你自己去处理啦
网站: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
http://topic.csdn.net/u/20090829/20/6e73877e-af46-4f98-9fce-f574495b550c.html声明一下,我不认同这位帖主的做法(其采用 Runnable 接口),不过可以借鉴一下,作为一种思路也行。
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);
}
}
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 就差不多了
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();
}
}
}
}
}
只需ThreadLocal绑定Connection以保证其在线程内的唯一性
然后将事务提到业务层就可以了