MyBatis 连接池
1 MyBatis内置数据源类型
MyBatis支持三种内置的数据源类型:
- Pooled:实现dataSource接口,并且使用了池的思想。
- UNPooled:同样也是实现了dataSource接口,但是该类型并没有使用池的思想。
- JNDI:采用服务器提供的JNDI技术实现,并且在不同服务器之间获取的连接池不一样。
2 MyBatis数据源配置
下面是MyBatis的SqlMapConfig.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>
3 UNPooled
这种方式是不具备连接池的思想,如果频繁的创建和销毁连接对象,会影响程序的运行效率。
3.1 UNPooled配置
<dataSource type="unpooled">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEcndoing=utf8"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
3.2 UNPooled源码分析
1)获取连接的源码
@Override
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
2)接着再进入到doGetConnection()方法中:
private Connection doGetConnection(String username, String password) throws SQLException {
// 实例化一个集合
Properties props = new Properties();
if (driverProperties != null) {
props.putAll(driverProperties);
}
// 判断用户名是否为空
if (username != null) {
props.setProperty("user", username);
}
// 判断密码是否为空
if (password != null) {
props.setProperty("password", password);
}
return doGetConnection(props);
}
3)此时又返回一个doGetConnection(),这是重载的另一个方法。
private Connection doGetConnection(Properties properties) throws SQLException {
// 初始化
initializeDriver();
// 获取一个连接对象
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
4)第一行代码中,调用了initializeDriver()方法。
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
// 使用反射获取到连接驱动
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
// 实例化连接驱动
Driver driverInstance = (Driver)driverType.newInstance();
// 注册驱动
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
大致流程:
- 在获取连接对象时,调用initializeDriver()方法判断是否已经注册连接驱动。
- 完成驱动注册,使用DriverManager.getConnection获取一个连接对象。
- 将连接对象交给configureConnection()方法,并设置自动提交事务,及事务的隔离级别。
private void configureConnection(Connection conn) throws SQLException {
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
4 Pooled
连接池就是用于存储连接对象的一个容器。而容器就是一个集合,且必须是线程安全的,即两个线程不能拿到同一个连接对象。同时还要具备队列的特性:先进先出原则。
使用连接池的好处:避免频繁创建和关闭数据库连接造成的开销,节省系统资源。
4.1 Pooled配置
<dataSource type="pooled">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEcndoing=utf8"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
4.2 Pooled源码分析
1)获取连接源码
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
2)调用popConnection()方法
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) {
// Pool has available connection
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// Pool does not have available connection
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
log.debug("Bad connection. Could not roll back");
}
}
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
// ping to server and check the connection is valid or not
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;
}
大致流程:
- 判断连接池中是否有空闲的连接对象,有则直接返回。
- 如果连接池没有空闲的连接,先判断活动连接池是否小于连接池承载的最大数量,小于则再创建新的连接对象。
- 但是如果连接池已经达到最大承载数量,那么在连接池中就把最先进来的连接(oldest)返回出去。
5 JNDI
JNDI(Java Naming and Directory Interface),是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。
如果将类型设置成JNDI,MyBatis从应用服务器(如:Tomcat)配置好的JNDI数据源获取数据源连接。
注意:MyBatis需要使用JNDI作为数据源,必须为Web项目。
5.1 创建Web工程,导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yiidian</groupId>
<artifactId>ch02_07_mybatis_datasource</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</project>
5.2 创建和配置context.xml配置
在webapp目录建立META-INF目录,在里面建立context.xml文件
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
name="jdbc/mybatis"
type="javax.sql.DataSource"
auth="Container"
maxActive="20"
maxWait="10000"
maxIdle="5"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mybatis"
/>
</Context>
以上配置也可以配置在Tomcat的server.xml文件里面:
Tomcat的server.xml
<Context>
<Resource
name="jdbc/mybatis"
type="javax.sql.DataSource"
auth="Container"
maxActive="20"
maxWait="10000"
maxIdle="5"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mybatis"
/>
</Context>
5.3 SqlMapConfig.xml配置
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/mybatis"/>
</dataSource>
</environment>
</environments>
5.4 编写Servlet调用Dao方法
package com.yiidian.web;
import com.yiidian.dao.CustomerDao;
import com.yiidian.domain.Customer;
import com.yiidian.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 控制器
* 一点教程网 - www.yiidian.com
*/
public class CustomerServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取SqlSession对象
SqlSession session = MyBatisUtils.getSession();
//2.生成Dao代理对象
CustomerDao customerDao = session.getMapper(CustomerDao.class);
//3.调用方法
List<Customer> list = customerDao.findAll();
System.out.println(list);
//4.关闭连接
session.close();
}
}
5.5 MyBatisUtils工具类内容
package com.yiidian.utils;
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 java.io.IOException;
import java.io.InputStream;
/**
* 工具类
*一点教程网 - www.yiidian.com
*/
public class MyBatisUtils {
private static SqlSessionFactoryBuilder builder;
private static SqlSessionFactory factory;
/**
* 初始化SqlSessionFactory
*/
static {
try {
builder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取sqlSession
*/
public static SqlSession getSession(){
return factory.openSession();
}
}
5.6 部署到Tomcat,访问测试
http://localhost:8080/customer
热门文章
优秀文章