Mybatis中的SqlSession与mapper的动态代理的事务提交方式

我们通过SqlSessionFactory.openSession()方法创建出SqlSession对象时,自动提交事务默认是关闭的,即每次用SqlSession对象执行sql操作时必须显式commit()否则操作无效.源码如下:

public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

从源码可以看出boolean autoCommit=false.因此在使用SqlSession对象调用相应的方法去执行sql语句时必须要手动调用commit()方法提交事务修改操作才会生效.
但是如果通过mapper动态代理的方式获取DAO对象后去执行sql语句就不需要执行commit()方法来提交事务,这是为什么呢?我们来看一下源码:
1.获取mapper动态代理对象

public  T getMapper(Class type) {
        return this.configuration.getMapper(type, this);
    }

此时,在真正执行sql的时候会通过MapperMethod对象调用execute(SqlSession sqlSession, Object[] args)方法,源码如下:

   public Object execute(SqlSession sqlSession, Object[] args) {
            Object param;
            Object result;
            switch(this.command.getType()) {
            case INSERT:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
                break;
            case UPDATE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
                break;
            case DELETE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
                break;
            case SELECT:
                if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                    this.executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (this.method.returnsMany()) {
                    result = this.executeForMany(sqlSession, args);
                } else if (this.method.returnsMap()) {
                    result = this.executeForMap(sqlSession, args);
                } else if (this.method.returnsCursor()) {
                    result = this.executeForCursor(sqlSession, args);
                } else {
                    param = this.method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(this.command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }
    
            if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
                throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
            } else {
                return result;
            }
        }

接着跟踪源码就可以发现,最终的执行时由Statement或PreparedStatement对象去真正的执行sql语句.

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;

        int var6;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var6 = handler.update(stmt);
        } finally {
            this.closeStatement(stmt);
        }

        return var6;
    }

追踪源码到此就应该知道为什么采用mapper动态代理的方式不需要手动提交事务了.因为Mysql默认都是自动提交事务的,此时使用Statement或PreparedStatement对象去真正的执行sql语句时并没有显式关闭自动提交事务,所以不需要手动提交.这就是为什么使用SqlSession去执行sql时要手动提交事务而使用mapper动态代理的方式不需要.

补充知识(JDBC连接的创建代码):

public class JDBCTest12 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1.注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//2.获取数据库连接
			String url = "jdbc:mysql://127.0.0.1:3366/bjpowernode";
			String user = "root";
			String password = "0987";
			conn = DriverManager.getConnection(url, user, password);
			//关闭事务的自动提交,开始事务
			conn.setAutoCommit(false);
			//3.定义SQL语句构架
			String sql = "select job,ename,sal from emp_bak where job = ? for update";
			//4.进行SQL语句的预编译
			ps = conn.prepareStatement(sql);
			//5.进行赋值
			ps.setString(1, "MANAGER");
			//6.执行SQL语句
			rs = ps.executeQuery();
			//7.处理查询结果集
			while(rs.next()){
				String job = rs.getString("job");
				String ename = rs.getString("ename");
				double sal = rs.getDouble("sal");
				System.out.println(job + " " + ename + " " + sal);
			}
			//提交事务
			conn.commit();
		} catch (Exception e) {
			try {
				//事务的回滚
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}finally{
			//关闭资源
			if(rs != null){
				try {
					rs.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(ps != null){
				try {
					ps.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(conn != null){
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}

public class JDBCTest06
{
	public static void main(String[] args){
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try{
			//1.注册驱动
			Driver driver = new com.mysql.jdbc.Driver();
			DriverManager.registerDriver(driver);
			//2.获取数据库连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3366/bjpowernode","root","111");
			//3.获取数据库操作对象
			stmt = conn.createStatement();
			//4.执行SQL语句:DQL语句->处理查询结果集
			String sql = "select e.ename,e.sal,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal";
			rs = stmt.executeQuery(sql);
			/*
			+--------+---------+-------+
			| ename  | sal     | grade |
			+--------+---------+-------+
			| SMITH  |  800.00 |     1 |
			| ALLEN  | 1600.00 |     3 |
			| WARD   | 1250.00 |     2 |
			| JONES  | 2975.00 |     4 |
			| MARTIN | 1250.00 |     2 |
			| BLAKE  | 2850.00 |     4 |
			| CLARK  | 2450.00 |     4 |
			| SCOTT  | 3000.00 |     4 |
			| KING   | 5000.00 |     5 |
			| TURNER | 1500.00 |     3 |
			| ADAMS  | 1100.00 |     1 |
			| JAMES  |  950.00 |     1 |
			| FORD   | 3000.00 |     4 |
			| MILLER | 1300.00 |     2 |
			+--------+---------+-------+
			
			*/
			//5.处理查询结果集
			while(rs.next()){
				/*
				String ename = rs.getString("ename");
				double sal = rs.getDouble("sal");
				int grade = rs.getInt("grade");
				*/
				//不建议以下写法:程序可读性不强
				String ename = rs.getString(1);
				double sal = rs.getDouble(2);
				int grade = rs.getInt(3);
				System.out.println(ename + " " + sal + " " + grade);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			//6.关闭资源
			if(rs != null){
				try{
					rs.close();
				}catch(Exception e){
					e.printStackTrace();
				}	
			}
			if(stmt != null){
				try{
					stmt.close();
				}catch(Exception e){
					e.printStackTrace();
				}	
			}

			if(conn != null){
				try{
					conn.close();
				}catch(Exception e){
					e.printStackTrace();
				}	
			}
		}
	}
}

你可能感兴趣的:(Mybatis框架)