Mybatis学习笔记-数据源与连接池
2015-08-06 15:32
260 查看
参考:/article/1373172.html
对于应用程序来说,与数据库的交互是必不可少的。但对于大多数应用来说,数据访问对象(Dao)的性能是整个应用的一个瓶颈点,目前比较成熟的解决方案是利用数据库连接池对数据库连接(Connection)进行本地缓存,避免频繁的创建数据库连接。
Mybatis作为当前最流行的数据访问层ORM框架之一,对连接池技术做了很好的集成,下面就来探究一下Mybatis的数据源与连接池的实现。
Mybatis数据源的分类
通过观察Mybatis核心包或查看帮助文档可知,有三种数据源:
1)UNPOOLED:每次请求时简单的打开数据库连接。
2)POOLED:每次请求时从连接池中取得连接。
3)JNDI:从容器上下文的数据源中(JNDI)获取连接。
接口关系如下所示:
数据源的创建过程
Mybatis中数据源的创建是由对应的数据源工程来实现的,数据源工厂的顶级接口为DataSourceFactory。下面是数据源工厂体系的类图:
1)读取mybatis核心配置文件
根据
type=”POOLED”,创建PooledDataSource实例
type=”UNPOOLED”,创建UnpooledDataSource实例
type=”JNDI”,从容器上下文中查找并获取数据源实例
注:mybatis的在执行具体的sql语句的时候才调用DataSource的getConnection()的。
为什么要使用连接池技术
我们来看一下下面的例子
结果为:
由此可见,创建数据库连接是及其耗时的,如果一个应用需要频繁的操作数据库,那绝大多数时间会浪费在创建数据库连接而不是处理业务数据上。
解决以上问题的办法是事先创建好一部分连接放到内存中,当程序需要数据库连接时,直接从内存中获取,而不需要与数据库重新建立socket连接。数据库连接池是上述方案的非常好的实现。
Mybatis数据库连接池的实现
基本原理
首先mybatis将创建好的Connection对象装饰城PooledConnection,
然后将其放入到PoolState(存储连接的容器)中,在PooledState中有两个用于存放PooledConnection的容器,其中一个是空闲连接容器(idleConnections),用于存放空闲的PooledConnection,另一个是活动连接容器(activeConnections),用于存放正在使用的PooledConnection。
重点来了…
从mybatis连接池中获取连接时:
1)首先判断idleConnections 中是否有空闲的连接,若有,则从idleConnections 中直接返回一个空闲的PooledConnection,若没有,则进行 2)
2)查看活动池activeConnections 是否已满,如果未满,则创建一个新的PooledConnection对象,并放入到activeConnections 中,并返回该对象,若activeConnections 已满,则进行 3)
3)查看最先加入到activeConnections 中的PooledConnection对象是否已经过期,若已过期,则将次对象从activeConnections 中移除,创建一个新的PooledConnection对象并将其加入到activeConnections 中。
若没有过期的PooledConnection对象,则进行 4)
4)进入等待状态,重复2)
下一个重点…
在传统JDBC中的Connection对象上调用close()方法时执行的操作的断开本次连接,但现在应用的连接池技术,就不能将该连接直接断开,而是将连接返回到连接池PoolState中。
那么,mybatis是怎么实现的呢?前面已经说过,mybatis将Connection对象封装成PooledConnection,同时PooledConnection是一个InvocationHandler,这是重点
这样一来,PooledConnection对象就是Connection的代理对象,当调用Connection接口的方法时,代理对象PooledConnection对象就要利用
从上面的代码中可以看到,close()对应的动作是
综上所述,mybatis利用动态代理改写了Connection接口中的close()方法。
对于应用程序来说,与数据库的交互是必不可少的。但对于大多数应用来说,数据访问对象(Dao)的性能是整个应用的一个瓶颈点,目前比较成熟的解决方案是利用数据库连接池对数据库连接(Connection)进行本地缓存,避免频繁的创建数据库连接。
Mybatis作为当前最流行的数据访问层ORM框架之一,对连接池技术做了很好的集成,下面就来探究一下Mybatis的数据源与连接池的实现。
Mybatis数据源的分类
通过观察Mybatis核心包或查看帮助文档可知,有三种数据源:
1)UNPOOLED:每次请求时简单的打开数据库连接。
2)POOLED:每次请求时从连接池中取得连接。
3)JNDI:从容器上下文的数据源中(JNDI)获取连接。
接口关系如下所示:
数据源的创建过程
Mybatis中数据源的创建是由对应的数据源工程来实现的,数据源工厂的顶级接口为DataSourceFactory。下面是数据源工厂体系的类图:
1)读取mybatis核心配置文件
<environments/>标签
<environments default="mysql_environment"> <environment id="mysql_environment"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="zhangdd" /> <property name="password" value="zd1991.." /> </dataSource> </environment> </environments>
根据
<dataSource/>的type类型来选择用不同的工厂类创建对应的数据源。
type=”POOLED”,创建PooledDataSource实例
type=”UNPOOLED”,创建UnpooledDataSource实例
type=”JNDI”,从容器上下文中查找并获取数据源实例
注:mybatis的在执行具体的sql语句的时候才调用DataSource的getConnection()的。
为什么要使用连接池技术
我们来看一下下面的例子
public class CreateConnectionTest { public static void main(String[] args) throws Exception { String url = "jdbc:mysql://localhost:3306/test"; String user = "zhangdd"; String password = "zd1991.."; long beforeTimeOffset = -1L; //创建Connection对象前时间 long afterTimeOffset = -1L; //创建Connection对象后时间 long executeTimeOffset = -1L; //执行sql的时间 PreparedStatement pstmt; ResultSet rs; String sql = "select * from employee where id = ?"; Class.forName("com.mysql.jdbc.Driver"); beforeTimeOffset = new Date().getTime(); System.out.println("before create:" + beforeTimeOffset); Connection connection = DriverManager.getConnection(url, user, password); afterTimeOffset = new Date().getTime(); System.out.println("after create:" + afterTimeOffset); System.out.println("create cost:" + (afterTimeOffset - beforeTimeOffset) + "ms"); pstmt = connection.prepareStatement(sql); pstmt.setInt(1, 1); rs = pstmt.executeQuery(); executeTimeOffset = new Date().getTime(); System.out.println("execute time:" + executeTimeOffset); System.out.println("execute cost:" + (executeTimeOffset - afterTimeOffset) + "ms"); connection.close(); } }
结果为:
before create:1438840632462 after create:1438840632776 create cost:314ms execute time:1438840632789 execute cost:13ms
由此可见,创建数据库连接是及其耗时的,如果一个应用需要频繁的操作数据库,那绝大多数时间会浪费在创建数据库连接而不是处理业务数据上。
解决以上问题的办法是事先创建好一部分连接放到内存中,当程序需要数据库连接时,直接从内存中获取,而不需要与数据库重新建立socket连接。数据库连接池是上述方案的非常好的实现。
Mybatis数据库连接池的实现
基本原理
首先mybatis将创建好的Connection对象装饰城PooledConnection,
public PooledConnection(Connection connection, PooledDataSource dataSource) { this.hashCode = connection.hashCode(); this.realConnection = connection; this.dataSource = dataSource; this.createdTimestamp = System.currentTimeMillis(); this.lastUsedTimestamp = System.currentTimeMillis(); this.valid = true; proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this); }
然后将其放入到PoolState(存储连接的容器)中,在PooledState中有两个用于存放PooledConnection的容器,其中一个是空闲连接容器(idleConnections),用于存放空闲的PooledConnection,另一个是活动连接容器(activeConnections),用于存放正在使用的PooledConnection。
protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>(); protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
重点来了…
从mybatis连接池中获取连接时:
1)首先判断idleConnections 中是否有空闲的连接,若有,则从idleConnections 中直接返回一个空闲的PooledConnection,若没有,则进行 2)
2)查看活动池activeConnections 是否已满,如果未满,则创建一个新的PooledConnection对象,并放入到activeConnections 中,并返回该对象,若activeConnections 已满,则进行 3)
3)查看最先加入到activeConnections 中的PooledConnection对象是否已经过期,若已过期,则将次对象从activeConnections 中移除,创建一个新的PooledConnection对象并将其加入到activeConnections 中。
若没有过期的PooledConnection对象,则进行 4)
4)进入等待状态,重复2)
下一个重点…
在传统JDBC中的Connection对象上调用close()方法时执行的操作的断开本次连接,但现在应用的连接池技术,就不能将该连接直接断开,而是将连接返回到连接池PoolState中。
那么,mybatis是怎么实现的呢?前面已经说过,mybatis将Connection对象封装成PooledConnection,同时PooledConnection是一个InvocationHandler,这是重点
class PooledConnection implements InvocationHandler
这样一来,PooledConnection对象就是Connection的代理对象,当调用Connection接口的方法时,代理对象PooledConnection对象就要利用
invoke()方法进行过滤,当过滤到
close()方法时,执行了一些特别的动作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } else { try { if (method.getDeclaringClass() != Object.class) { checkConnection(); } return method.invoke(realConnection, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } }
从上面的代码中可以看到,close()对应的动作是
dataSource.pushConnection(this);
综上所述,mybatis利用动态代理改写了Connection接口中的close()方法。
相关文章推荐
- Java:String、StringBuffer和StringBuilder的区别
- Unity 2D动画控制器详解
- 成员变量、局部变量、实例变量、类变量
- 透视函数glFrustum(), gluPerspective()函数用法和glOrtho()函数用法.
- MFC调用WPF函数
- mysql-proxy 读写分离
- Android SDK代理服务器解决国内不能更新下载问题
- HDU 2680 Choose the best route
- presto 文档
- VisualSVN Server搭建svn服务器 TotoiseSVN做客户端 或安装subclipse插件
- 机器学习大牛主页,这个分类比较杂
- ZOJ - 3279 Ants
- print的用法
- 数据结构基础(18) --哈希表的设计与实现
- Android 反编译工具简介
- 文件夹和文件的创建
- [转载]技术普及帖:你刚才在淘宝上买了一件东西
- xml文件有误
- 【Hades】ades是一个开源库,基于JPA和Spring构建,通过减少开发工作量显著的改进了数据访问层的实现
- 对于多线程两种方式的理解