一。先从jdbc来说最简单的事物处理
public void test1() throws Exception{
String url="";
Class.forName("com.mysql.jdbc.Driver");//加在驱动
Connection con=DriverManager.getConnection(url);
//事物开始。
con.setAutoCommit(false);
//执行操作。
//事物提交
con.commit();
con.setAutoCommit(true);
con.close();
}
这个相信大家都同意吧,有时看到有人会这样写。
public void test() throws Exception{
String url="";
Class.forName("com.mysql.jdbc.Driver");//加在驱动
Connection con=DriverManager.getConnection(url);
con.setAutoCommit(false);
Statement stm = con.createStatement();
//执行数据库操作。
stm = con.createStatement();
con.setAutoCommit(false);
//若不出现异常,则继续执行到try语句完,否则跳转到catch语句中
stm.addBatch("insert into student values(23,'tangbao','高数',100)");
stm.addBatch("insert into student values(24,'王定','c#',98)");
stm.addBatch("insert into student values(25,'王国云','java',90)");
stm.addBatch("insert into student values(26,'溜出','英语',89)");
stm.addBatch("insert into student values(27,'wqde','java',63)");
stm.executeBatch();
System.out.println("插入成功!");
//commit:若成功执行完所有的插入操作,则正常结束
con.commit();
System.out.println("提交成功!");
con.setAutoCommit(true);
stm.close();
con.setAutoCommit(true);
con.close();
}
原来我看到statement就蒙了,实际上他就是一条执行语句而已 只要没设定con.setAutoCommit(true)都是一条执行,一个事物,看了上面的代码相信你会明白的。
这是最基础的事物,而,spring和hibernate 以及jta的事物,都是在这个基础上进行封装的。
二,咱们先来看看datasource 接口
public interface DataSource
extends CommonDataSource, Wrapper
{
public abstract Connection getConnection()
throws SQLException;
public abstract Connection getConnection(String s, String s1)
throws SQLException;
}
这两个方法都能获得一个链接实时上他就是一种咱们得到的链接,spring有没有对着接口进行一个代理呢?,然后通过AOP将这个datasource 注入到springbeanFactory 里面的dataSource 呢,于是不管从哪里得到datasource 必然会被spring拦截。下面模拟了一下简单的实现
package com.xianfeng.common.test;
public class DatasourceHandler implements InvocationHandler {
private DataSource dataSource;
/**
* @param dataSource
*/
public DatasourceHandler(DataSource dataSource) {
super();
this.dataSource = dataSource;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("getConnection")){
if(ResourceHolder.getResource(proxy)!=null){
Connection connection = (Connection) method.invoke(this.dataSource, args);
ResourceHolder.addResource(proxy, connection);
ResourceHolder.setKey(proxy);
}
return ResourceHolder.getResource(proxy);
}else{
return method.invoke(this.dataSource, args);
}
}
}
package com.xianfeng.common.testDynamicProxy;
import java.util.HashMap;
import java.util.Map;
public class ResourceHolder {
private static ThreadLocal
上面用了java的动态代理生成了一个datasource的代理类,再调用datasource的getconnection 方法时,会得到一个connection 并把他与当前的线程绑定,动态生成的代理类也会与本线程绑定。
在这个线程需要connection 时,通过resourceHolder getResource 获得,而getResource 需要一个key,因为代理类也与线程绑定了挡在key里可以直接活动。
好处:1.实现了线程安全。2整个线程中得到的connection 都将是同一个对象,因此用 上面一提到的 知识就很容易实现 spring的事物安全了。
疑点:为啥要同时吧代理类和connection都放在线程中呢,我猜想是防止connection 为空的情况,要是空的就可以直接用代理生成,后再把新的connection 和代理类绑定,再与线程绑定。
三.hibernate 则是session控制的事物。
public void test(){
Configuration configuration =new Configuration().configure();
SessionFactory sf=configuration.buildSessionFactory();
Session ss =sf.openSession();
ss.beginTransaction();
//执行增删改差。
ss.flush();
ss.getTransaction().commit();
ss.close();
}
这跟一种的事物好像真的不一样啊,好的我们在往下看,看看他的API里他调用的是
public Transaction beginTransaction()
throws HibernateException
{
errorIfClosed();
if(rootSession != null)
log.warn("Transaction started on non-root session");
Transaction result = getTransaction();
result.begin();
return result;
}
public Transaction getTransaction()
throws HibernateException
{
errorIfClosed();
return jdbcContext.getTransaction();
}
去jdbc事物里看看他的begin();
toggleAutoCommit = jdbcContext.connection().getAutoCommit();
if(log.isDebugEnabled())
log.debug((new StringBuilder()).append("current autocommit status: ").append(toggleAutoCommit).toString());
if(toggleAutoCommit)
{
log.debug("disabling autocommit");
jdbcContext.connection().setAutoCommit(false);
}
}
看到这几行时,你瞬间懂了吧,他一样事调用的 setAutoCommit(false);那么他是怎么实现线程安全的呢 hibernate官方文档手册的实例中提供了一个好方法。
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
if(s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if(s != null) {
s.close();
}
session.set(null);
}
和二种的处理方法,大同小异了。
事物准备阶段。
protected Object doGetTransaction()
{
HibernateTransactionObject txObject = new HibernateTransactionObject(null);
//设置事物是否允许嵌套
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//这里就像用一个对象来获得当前线程的session.第一个事物来时肯定以前没有绑定过资源。
SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.getResource(getSessionFactory());
if(sessionHolder != null)
{
if(logger.isDebugEnabled())
logger.debug((new StringBuilder("Found thread-bound Session [")).append(SessionFactoryUtils.toString(sessionHolder.getSession())).append("] for Hibernate transaction").toString());
txObject.setSessionHolder(sessionHolder);
} else
if(hibernateManagedSession)
try
{
//先看当前线程有无session 没有及open一个绑定到线程中,有就把原来的拿过来。
Session session = getSessionFactory().getCurrentSession();
if(logger.isDebugEnabled())
logger.debug((new StringBuilder("Found Hibernate-managed Session [")).append(SessionFactoryUtils.toString(session)).append("] for Spring-managed transaction").toString());
txObject.setExistingSession(session);
}
catch(HibernateException ex)
{
throw new DataAccessResourceFailureException("Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
if(getDataSource() != null)
{
//第一个事物来的时候都是没有的,
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
protected void doBegin(Object transaction, TransactionDefinition definition)
{
HibernateTransactionObject txObject;
Session session;
txObject = (HibernateTransactionObject)transaction;
//第一次来的时候是没有绑定的,现在事物还没有正式开始。
if(txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction())
throw new IllegalTransactionStateException("Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.");
session = null;
//第一次来的时候是没有绑定的,这个事物是没有绑定到线程中的。
if(txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction())
{
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = entityInterceptor == null ? ((Session) (getSessionFactory().openSession())) : ((Session) (getSessionFactory().openSession(entityInterceptor)));
if(logger.isDebugEnabled())
logger.debug((new StringBuilder("Opened new Session [")).append(SessionFactoryUtils.toString(newSession)).append("] for Hibernate transaction").toString());
txObject.setSession(newSession);
}
session = txObject.getSessionHolder().getSession();
Transaction hibTx;
if(timeout != -1)
{
//这里是重点开始事物
hibTx = session.getTransaction();
hibTx.setTimeout(timeout);
hibTx.begin();
} else
{
hibTx = session.beginTransaction();
}
txObject.getSessionHolder().setTransaction(hibTx);
if(getDataSource() != null)
{
//这里才是通过当前线程得到connection,
java.sql.Connection con = session.connection();
ConnectionHolder conHolder = new ConnectionHolder(con);
if(timeout != -1)
conHolder.setTimeoutInSeconds(timeout);
if(logger.isDebugEnabled())
logger.debug((new StringBuilder("Exposing Hibernate transaction as JDBC transaction [")).append(con).append("]").toString());
//把connection绑定到当前线程
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
//设置事物的connection
txObject.setConnectionHolder(conHolder);
}
if(txObject.isNewSessionHolder())
//第一个事物肯定是新建的holder,这个holder与资源绑定再一起绑定到再次绑定到线程中来。
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
//将sessionHolder设置成状态为已经事物开启了
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
}
此时事物已经开始启动了,以后在DAO,或Facade中调用方法,要获得session和connection都将是一个对象,而这个connection也是绑定再session的,这用前面的观点就能证明是在一个事物中,
假如,在执行过程中又开启了一个事物,怎么办呢,继续往下看
protected Object doSuspend(Object transaction)
{
//挂起就是把当前事物的sessionHolder 设为空。
//并将他们与资源对象解绑,也就是说把sessionHolder不与当前对象绑定,
//把这么解绑的资源放在与线程绑定的SuspendedResourcesHolder这个对象上,此时,当前线程的session就为空了
HibernateTransactionObject txObject = (HibernateTransactionObject)transaction;
txObject.setSessionHolder(null);
SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.unbindResource(getSessionFactory());
txObject.setConnectionHolder(null);
ConnectionHolder connectionHolder = null;
if(getDataSource() != null)
connectionHolder = (ConnectionHolder)TransactionSynchronizationManager.unbindResource(getDataSource());
return new SuspendedResourcesHolder(sessionHolder, connectionHolder, null);
}
这个事物挂起,新开启的事物会继续执行上面的doGetTransaction,doBegin,当新的事物执行完成了后,个人估计是要先判断下是否有挂起的线程(原码太多了不好找),有就执行
protected void doResume(Object transaction, Object suspendedResources)
{
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder)suspendedResources;
if(TransactionSynchronizationManager.hasResource(getSessionFactory()))
TransactionSynchronizationManager.unbindResource(getSessionFactory());
TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
if(getDataSource() != null)
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
}
然后执行dao,Facade里面的各个方法。