Mybatis源码解读

一:先看下面一个简单的mybatis的程序

package com.hanwei;

import org.junit.Before;

import com.hanwei.dao.Istudent;
import com.hanwei.dao.impl.IstudentImpl;
import com.hanwei.pojo.Student;

public class Test {
	
	private Istudent student;
	private Student stu;

	@Before
	public void before() {
		student = new IstudentImpl();
		stu = new Student();
		//test();
	}
	
	@org.junit.Test
	public void test() {
		
		stu.setGrade("一年级");
		stu.setName("2111");
		stu.setSex("男");
		stu.setAge("7");
		
		//插入一个对象
		student.insert(stu);
		student.insertgetUserIDcatche(stu);
		
		//插入一个变量
		student.insertoneparame("7");
		
	}
	
	
	/*public  static void main(String args[]) {
		new Test().before();
	}
	*/
	
}




package com.hanwei.dao;

import com.hanwei.pojo.Student;

public interface Istudent {
  
	 public void insert(Student student);//插入
}


package com.hanwei.dao.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.hanwei.dao.Istudent;
import com.hanwei.pojo.Student;

public class IstudentImpl implements Istudent{

	@Override
	public void insert(Student stu) {
		// TODO Auto-generated method stub
		SqlSession sqlSession=null;
		InputStream inputStream=null;
		try {
			//创建主配置文件
			inputStream = Resources.getResourceAsStream("mybatis.xml");
			//创建sqlSessionFactory对象  这里的build方法已经把inputStream关闭了,所以后面不需要再关闭一次
			SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
			//创建sqlsession对象
			sqlSession=sqlSessionFactory.openSession();
			
			System.out.println(sqlSession);
			//sqlSession.insert("student", stu);
			sqlSession.insert("student", stu);
			//最终事务提交  
			sqlSession.commit();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			/*
			 * 
			*/  //事务关闭    事务提交后就不用回滚  否则回滚数据不会写到数据库
			if(sqlSession!=null) {
				sqlSession.close();
			}
		}
		
		
		
	}
	
	
	
	@Override
	public void insertgetUserIDcatche(Student student) {//插入并且获取刚插入的id
		
		
		// TODO Auto-generated method stub
				SqlSession sqlSession=null;
				InputStream inputStream=null;
				try {
					//创建主配置文件
					inputStream = Resources.getResourceAsStream("mybatis.xml");
					//创建sqlSessionFactory对象  这里的build方法已经把inputStream关闭了,所以后面不需要再关闭一次
					SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
					//创建sqlsession对象
					sqlSession=sqlSessionFactory.openSession();
					
					System.out.println(sqlSession);
					//sqlSession.insert("student", stu);
					System.out.println("插入前学生信息"+student.getId());
					
					sqlSession.insert("getUserinsertIDcatche", student);
					
					
					//最终事务提交  
					sqlSession.commit();
					
					System.out.println("插入后学生信息"+student.getId());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally {
					/*
					 * 
					*/  //事务关闭    事务提交后就不用回滚  否则回滚数据不会写到数据库
					if(sqlSession!=null) {
						sqlSession.close();
					}
				}
				
		
	}



	@Override
	public void insertoneparame(String studentage) {//只给谋列插入单个值
		// TODO Auto-generated method stub
		// TODO Auto-generated method stub
		SqlSession sqlSession=null;
		InputStream inputStream=null;
		try {
			//创建主配置文件
			inputStream = Resources.getResourceAsStream("mybatis.xml");
			//创建sqlSessionFactory对象  这里的build方法已经把inputStream关闭了,所以后面不需要再关闭一次
			SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
			//创建sqlsession对象
			sqlSession=sqlSessionFactory.openSession();
			
			System.out.println(sqlSession);
			//sqlSession.insert("student", stu);
			//System.out.println("插入前学生信息"+student.getId());
			
			sqlSession.insert("student", studentage);
			
			
			//最终事务提交  
			sqlSession.commit();
			
			//System.out.println("插入后学生信息"+student.getId());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			/*
			 * 
			*/  //事务关闭    事务提交后就不用回滚  否则回滚数据不会写到数据库
			if(sqlSession!=null) {
				sqlSession.close();
			}
		}
		
	}
	

}





mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration 
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<!-- ?代表只能小于等于1个 -->
<configuration>


        <!-- 引进数据库变量配置文件 -->
        <properties resource="jdbc.properties"/> 
         
         <!-- 简化mapper文件中很长的实体类名位置parameterType   alias别名-->
         <typeAliases>
            <typeAlias type="com.hanwei.pojo.Student" alias="xxx"/>
         </typeAliases>
         
         
<!-- 可以来选择用什么数据库环境,本地还是服务器或者MySQL还是Oracle -->
     <environments default="mysqlEM">
       
           <!-- 大于等于0个 -->
         <environment  id="mysqlEM">
         <!--jdbc默认的事务管理  -->
              <transactionManager type="JDBC"/>
         <!--       -->
              <dataSource type="POOLED">
                 <property name="driver" value="${jdbc.driver}"/>
                 <property name="url" value="${jdbc.url}"/>
                 <property name="username" value="${jdbc.username}"/>
                 <property name="password" value="${jdbc.password}"/>
              </dataSource>
              
         </environment>
     </environments>     
         <!-- 注册映射文件    也可以url="指向硬盘物理路径"-->
         
            <mappers>
               <mapper  resource="com/hanwei/dao/mapper/studentmapper.xml"/>
            </mappers>
     
    
</configuration>



mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper 
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="xxx">

  <insert id="student" parameterType="xxx">
  insert into T_student (name,age,grade,sex) values(#{name}, #{age},#{grade},#{sex}) 
 </insert>
  
 <!--这里的#{}只是一个占位符  里面可以包含任何值-->
  <insert id="insertoneparame" parameterType="xxx">
  insert into T_student (age) values(#{zzzzz}) 
 </insert>

 <insert id="getUserinsertIDcatche" parameterType="xxx">
  insert into T_student (name,age,grade,sex) values(#{name}, #{age},#{grade},#{sex}) 
    <selectKey resultType="int" keyProperty="id" order="AFTER">
       select @@identity
    </selectKey>
       
 </insert>
 
</mapper>




log4j.properties

#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.Target=System.out 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH:mm:ss}] %c %L %m%n  
  

log4j.rootLogger=debug,stdout

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.username=root
jdbc.password=123456




1.问题一发现IstudentImpl没有对inputstream输入流进行关闭

最终源码发现

//创建sqlSessionFactory对象  这里的build方法已经把inputStream关闭了,所以后面不需要再关闭一次
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);


2.当我们去掉sqlSession.commit();会出现

 [DEBUG][2017-10-24 14:23:41] com.hanwei.pojo.Student.student 49 <==    Updates: 1
  [DEBUG][2017-10-24 14:23:41] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:23:41] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:23:41] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:23:41] org.apache.ibatis.datasource.pooled.PooledDataSource 49 Returned connection 120694604 to pool.
  
  不难发现jdbc.JdbcTransaction 49 Rolling back JDBC Connection 事务回滚,再回到数据库查看发现没有数据插入????/

这是为什么呢?


追踪方法源码

sqlSession=sqlSessionFactory.openSession();


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

//autoCommit=false

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
      return new DefaultSqlSession(configuration, executor);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

//dirty=false

 public DefaultSqlSession(Configuration configuration, Executor executor) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
  }



sqlSession.insert("student", stu);


public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }


      

 public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }



 dirty = true;

(1)当我们关闭sqlSession.commit();


追踪方法close()

isCommitOrRollbackRequired(false)//返回true


 public void close() {
    try {
      executor.close(isCommitOrRollbackRequired(false));
      dirty = false;
    } finally {
      ErrorContext.instance().reset();
    }
  }

 private boolean isCommitOrRollbackRequired(boolean force) {
    return dirty || force;
  }

 forceRollback=true

 public void close(boolean forceRollback) {
    try {
      try {
        rollback(forceRollback);
      } finally {
        if (transaction != null) transaction.close();
      }
    } catch (SQLException e) {
      // Ignore.  There's nothing that can be done at this point.
      log.debug("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      deferredLoads = null;
      localCache = null;
      localOutputParameterCache = null;
      closed = true;
    }
  }

//required=true

 public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }


 public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }


 public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }


 

(2)当我们打开sqlSession.commit();

//  dirty = true;

 public void commit() {
    commit(false);
  }


 public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }


 private boolean isCommitOrRollbackRequired(boolean force) {
    return dirty || force;
  }


 public void commit(boolean required) throws SQLException {
    if (closed) throw new ExecutorException("Cannot commit, transaction is already closed");
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();//事务提交
    }
  }


//dirty = false;

public void close() {
    try {
      executor.close(isCommitOrRollbackRequired(false));
      dirty = false;
    } finally {
      ErrorContext.instance().reset();
    }
  }

 private boolean isCommitOrRollbackRequired(boolean force) {
    return dirty || force;  //false
  }

 forceRollback=true

 public void close(boolean forceRollback) {//false
    try {
      try {
        rollback(forceRollback);//false
      } finally {
        if (transaction != null) transaction.close();
      }
    } catch (SQLException e) {
      // Ignore.  There's nothing that can be done at this point.
      log.debug("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      deferredLoads = null;
      localCache = null;
      localOutputParameterCache = null;
      closed = true;
    }
  }


 public void rollback(boolean required) throws SQLException {//false
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {//false  不会事务回滚
          transaction.rollback();
        }
      }
    }
  }


 


这就解释了为什么打开屏蔽的提交代码会出现事务提交并且数据库有数据,但是自增长id不是加1变成了加2,并且也会提交事务。

 [DEBUG][2017-10-24 14:41:33] com.hanwei.pojo.Student.student 49 <==    Updates: 1
  [DEBUG][2017-10-24 14:41:33] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:41:33] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:41:33] org.apache.ibatis.transaction.jdbc.JdbcTransaction 49 Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@731a74c]
  [DEBUG][2017-10-24 14:41:33] org.apache.ibatis.datasource.pooled.PooledDataSource 49 Returned connection 120694604 to pool.
  




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值