Mybatis
1.MyBatis的连接池技术和事务控制
1.1 MyBatis 连接池技术
1.1.1 MyBatis 连接池的分类
在 Mybatis
中我们将它的数据源 dataSource
分为以下几类:
可以看出 Mybatis 将它自己的数据源分为三类:
数据源 | 作用 |
---|---|
UNPOOLED | 不使用连接池的数据源 |
POOLED | 使用连接池的数据源 |
JNDI | 使用 JNDI 实现的数据源 |
具体结构如下:
相应地, MyBatis
内部分别定义了实现了 java.sql.DataSource
接口的 UnpooledDataSource
,PooledDataSource
类来表示 UNPOOLED
、 POOLED
类型的数据源。
1.1.2 MyBatis 数据源配置
数据源配置就是在 mybatis-config.xml 文件中, 具体配置如下:
<!-- 配置数据源(连接池)信息 -->
<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>
MyBatis
在初始化时, 根据<dataSource>
的 type
属性来创建相应类型的的数据源 DataSource
,即:
type="POOLED"
: MyBatis
会创建 PooledDataSource
实例
type="UNPOOLED"
: MyBatis
会创建 UnpooledDataSource
实例
type="JNDI"
: MyBatis
会从 JNDI
服务上查找 DataSource
实例,然后返回使用
1.1.3 MyBatis 中DataSource 的存取
MyBatis
是 通 过 工 厂 模 式 来 创 建 数 据 源 DataSource
对 象 的 , MyBatis 定 义 了 抽 象 的 工 厂接口:org.apache.ibatis.datasource.DataSourceFactory
,通过其 getDataSource()
方法返回数据源DataSource
。
DataSourceFactory源码:
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
/**
* @author Clinton Begin
*/
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
MyBatis
创建了 DataSource
实例后,会将其放到 Configuration
对象内的 Environment
对象中, 供以后使用。
具体分析过程如下:
1.先进入 XMLConfigBuilder
类中,可以找到如下代码:
2.分析 configuration
对象的 environment
属性,结果如下:
1.1.4 MyBatis连接的获取过程分析
当我们需要创建 SqlSession
对象并需要执行 SQL
语句时,这时候 MyBatis
才会去调用 dataSource
对象来创建java.sql.Connection
对象。也就是说, java.sql.Connection
对象的创建一直延迟到执行SQL
语句的时候。
@Test
public void testSql() throws Exception {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("findUserById",41);
System.out.println(list.size());
}
只有当第 4 句 sqlSession.selectList("findUserById")
,才会触发 MyBatis
在底层执行下面这个方法来创建 java.sql.Connection
对象。
如何证明它的加载过程呢?
我们可以通过断点调试,在 PooledDataSource
中找到如下 popConnection()
方法,如下所示:
分析源代码,得出 PooledDataSource
工作原理如下:
下面是连接获取的源代码:
最后我们可以发现,真正连接打开的时间点,只是在我们执行SQL语句时,才会进行。其实这样做我们也可以进一步发现,数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再
立即将数据库连接归还到连接池中。
1.2 MyBatis 事务控制
1.2.1 JDBC 中事务控制
在 JDBC
中我们可以通过手动方式将事务的提交改为手动方式,通过setAutoCommit()
方法就可以调整。
通过 JDK 文档,我们找到该方法如下:
那么我们的 Mybatis
框架因为是对 JDBC
的封装,所以 Mybatis
框架的事务控制方式,本身也是用 JDBC
的setAutoCommit()
方法来设置事务提交方式的。
1.2.2 MyBatis 事务提交方式
Mybatis
中事务的提交方式,本质上就是调用 JDBC
的 setAutoCommit()
来实现事务控制。
运行之前所写的代码:
package com.spg.test;
import com.spg.dao.UserDao;
import com.spg.entity.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 在测试方法执行之前执行
public void init() throws IOException {
// 1.读取配置文件
in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 3.创建SqlSession工厂对象
factory = builder.build(in);
// 4.创建SqlSession对象
sqlSession = factory.openSession();
// 5.创建Dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 在测试方法执行之后执行
public void destroy() throws IOException {
sqlSession.commit();
// 7. 释放资源
sqlSession.close();
in.close();
}
@Test
public void save(){
// 6.执行操作
User user = new User();
user.setUserName("tom");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("天津市");
int count = userDao.save(user);
System.out.println(user);
if(count > 0){
System.out.println("保存成功!");
} else {
System.out.println("保存失败!");
}
}
}
观察在它在控制台输出的结果:
这是我们的 Connection
的整个变化过程, 通过分析我们能够发现之前的CUD 操作过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()
方法,在执行时它的值被设置为 false
了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()
方法来执行提交操作。
1.2.3 MyBatis3 自动提交事务的设置
通过上面的研究和分析,现在我们一起思考,为什么 CUD 过程中必须使用 sqlSession.commit()
提交事务?主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)
方法,这样我们就必须使用sqlSession.commit()
方法,相当于使用了 JDBC
中的 connection.commit()
方法实现事务提交。
package com.spg.test;
import com.spg.dao.UserDao;
import com.spg.entity.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 在测试方法执行之前执行
public void init() throws IOException {
// 1.读取配置文件
in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 3.创建SqlSession工厂对象
factory = builder.build(in);
// 4.创建SqlSession对象
sqlSession = factory.openSession(true);
// 5.创建Dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 在测试方法执行之后执行
public void destroy() throws IOException {
// 7. 释放资源
sqlSession.close();
in.close();
}
}
所对应的 DefaultSqlSessionFactory 类的源代码:
运行的结果如下:
我们发现,此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为 false
再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。