一:先看下面一个简单的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.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.