mybatis中的链接池
关于连接池
对于链接池,大家应该都不陌生,工程中不免存在许多需要访问数据的请求(访问服务、数据库、缓存等),对于这些下游服务,官方会提供不同语言的Driver、Document、DemoCode来指导使用方建立连接与接口调用。基本的通讯步骤为:建立连接、发送请求、关闭连接释放资源。对于并发量很低的请求,连接可以临时建立,但是当服务吞吐量非常大,建立连接connect和销毁连接close就会成为瓶颈,为了对其进行优化,连接池出现了。
通过链接池,请求的通讯步骤变为:从池中取出链接、发送请求、放回链接。
连接池核心原理与实现
数据库操作伪代码
DBClientConnection* c = ConnectionPool::GetConnection();
c->insert(“db.s”, BSON(”shenjian”));
ConnectionPool::FreeConnection(c);
可以看到连接池ConnectionPool主要有三个核心接口:
(1)Init:初始化Array[DBClientConnection],这个接口只在服务启动时调用一次;
(2)GetConnection:请求每次需要访问数据库时,不connect一个新连接,而是通过连接池的这个接口来拿连接;
(3)FreeConnection:请求每次访问完数据库时,不是close一个连接,而是把这个连接放回连接池;
通俗的讲,连接池就是用于存储链接的一个容器,容器其实就是一个集合对象,并且该集合必须是线程安全的,不能两个线程拿到同一个链接。该集合还必须实现队列的特性,先进先出。
mybatis中的链接池
几种连接池介绍
在mybatis中,含有以下几类连接池:
pooled:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。
unpooled:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用池的思想。
jndi:采用服务器提供的jndi技术实现,来获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的。
在这三种数据源中,我们一般采用的是 POOLED 数据源。
mybatis中连接池配置
在 Mybatis 的 SqlMapConfig.xml 配置文件中,通过来实现 Mybatis 中连接池的配置。type属性就是表示采用何种链接池方式。
<!-- 配置数据源(连接池)信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
当然如果配置了properties标签,可以进行连接信息的抽离:
<properties resource="dbconfig.properties"></properties>
<environments default="development">
<environment id="development">
<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>
MyBatis 在初始化时,根据的 type 属性来创建相应类型的的数据源 DataSource,即:type=”POOLED”:MyBatis 会创建 PooledDataSource 实例,然后返回使用。
MyBatis 是通过工厂模式来创建数据源 DataSource 对象的, MyBatis 定义了抽象的工厂接口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源DataSource。
//DataSourceFactory源码
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 对象中, 供以后使用。
当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。
@Test
public void testSql() throws Exception {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.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 对象。
数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中。
mybatis中的事务
对于增删改操作,并不是读到语句后就立马对数据库数据进行更新,而是通过建立连接,每建立一次连接,并且这些操作在同一个连接内,那么在语句执行完毕后,这些增删改操作将会被一起提交到数据库中执行。
而对于select语句,在语句关联结果集关闭时,也就是本条查询语句已经明确将查询结果付给什么变量时,就会向数据库中提交一次执行。
以上这些是JDBC的默认提交时间。
在 JDBC 中我们可以自行将事务的提交改为手动方式,通过setAutoCommit()方法就可以调整。也就是当我们在新建立连接时,可以根据传入参数的不同,说明本次连接时自动提交还是手动提交。
那么我们的 Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC的setAutoCommit()方法来设置事务提交方式的。
Mybatis 手动提交事务
@Test
public void testSaveUser() throws Exception {
User user = new User();
user.setUsername("mybatis user09");
//6.执行操作
int res = userDao.saveUser(user);
System.out.println(res);
System.out.println(user.getId());
}
@Before//在测试方法执行之前执行
public void init()throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象
session = factory.openSession();
//5.创建 Dao 的代理对象
userDao = session.getMapper(IUserDao.class);
}
@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
//7.提交事务
session.commit();
//8.释放资源
session.close();
in.close();
}
以上在创建连接时:session=factory.openSession();没有传入参数,这样一来连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。
Mybatis 自动动提交事务
@Before//在测试方法执行之前执行
public void init()throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象,传入参数为true,代表开启自动提交
session = factory.openSession(true);
//5.创建 Dao 的代理对象
userDao = session.getMapper(IUserDao.class);
}
@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
//7.释放资源
session.close();
in.close();
}
此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为 false再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。