前言
MyBatis是一个流行的ORM框架,它简单易用,支持自定义SQL
、存储过程以及高级映射。MyBatis
免除了几乎所有的JDBC
代码以及设置参数和获取结果集的工作。
作为一款优秀的持久层框架,连接池是必不可少的。本文将分析MyBatis
内置的数据源是如何实现连接池功能的(源码基于MyBatis3.4.5
)。
JDBC对数据库的操作
回忆一下,我们用原生的JDBC
来操作数据库的一般流程:
public class JDBCExample {
// JDBC driver name and database URL / credentials
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
static final String USER = "username";
static final String PASS = "password";
public List<User> queryUsers() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<User> users = new ArrayList<>();
try {
// STEP 1: 注册driver
Class.forName(JDBC_DRIVER);
// STEP 2: 打开连接
conn = DriverManager.getConnection(DB_URL,USER,PASS);
// STEP 3: 执行query
stmt = conn.createStatement();
String sql = "SELECT ID, NAME, AGE FROM USER";
rs = stmt.executeQuery(sql);
// STEP 4: 获取变量
while(rs.next()) {
User user = new User();
user.setId(rs.getInt("ID"));
user.setAge(rs.getInt("AGE"));
user.setName(rs.getString("NAME"));
users.add(user);
}
return users;
} catch(SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// STEP 5: 关闭资源
try {
if (rs != null)
rs.close();
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}
}
return users;
}
}
主要的步骤包括:
- 加载数据库驱动
- 获取数据库连接
- 执行
SQL
语句 - 从
ResultSet
获取结果,给Entity
赋值 - 关闭数据库连接等资源
不难发现,当中包含了大段的重复模版代码。而我们所关心的业务逻辑,只有寥寥几行。而且,每执行一次数据库操作都重新建立一个新的连接,用完之后关闭的做法,性能非常低。因为每次打开关闭连接,都会涉及到TCP
的三次握手与四次挥手,以及数据库的验证操作。为了优化第二个问题,出现了数据库连接池。
数据库连接池
不管是Connection Pool
,Thread Pool
还是Channel Pool
,所有池的思想都是一致的:
- 维护一个资源集合
- 消费者可以从集合中获取资源
- 消费者使用完资源后,将之放回池中
当然其中肯定会涉及到一些池的大小的维护,池满之后的处理等细节操作。接下来我们来分析一下MyBatis
中的连接池是如何实现的。
MyBatis中的连接池实现
如果我们使用MyBatis
内置的数据源的化,一般会在XML
配置文件里面这样配置:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>