在hibernate中,session.colse()
1、这只是把session,即连接再放回连接池里,没有关闭连接。
如果不显示的调用session.close(),最终这个连接会因为在一定时间没有使用后给还回连接池,也不会出错,但是在不使用session的那一段时间中,这个session就浪费了,所以在使用完session后要显示的调用session.close()把其还给连接池。
连接池的Connection.close()方法都是使用动态代理模式修改过。
2、调用连接池的close的接口,只是把连接的控制权还给了池子,至于是否关闭要看池子空闲的conn是否达到上限形成浪费,所以conn池的close只是软关闭。真正释放连接资源是pool自行判断并操作的。
session 只是通过conn来操作数据库,它自身是一个会话形式。它和conn只是资源配备的关系。
一、为什么使用数据库连接池
用户每次请求都需要向数据库获得连接,而数据库创建连接通常要消耗相对较大的资源,创建时间也长。假设网站一天10万的访问量,数据库服务器就需要创建10万次连接,极大浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。连接池工作原理图:
应用程序一开始就向数据库要了一批连接构成连接池,用户访问 Servlet,Servlet 向 Dao 要数据,此时 Dao 层不是直接向数据库要连接,而是向连接池中拿一个空闲的连接。当 Dao 用完连接以后,将连接还给连接池。
二、编写数据库连接池
1、编写连接池需要实现 java.sql.DataSource 接口。DataSource 接口中定义了两个重载的 getConnection 方法:
Connection.getConnection();
Connection.getConnection(String username, String password);
2、实现 DataSource 接口并实现连接池功能的步骤:
在 DataSource 构造函数中批量创建与数据库的连接,并把创建连接加入到 LinkedList 对象中。实现 getConnection 方法,让 getConnection 方法每次调用时,从 LinkedList 中取一个 Connection 返回给用户。当用户使用完 Connection 对象应保证将连接返回到 LinkedList 中,而不是还给数据库。
在自己编写数据库连接池时,我们要考虑到,从数据库获取连接的时候容易,但是当用户用完连接,调用 connection.close() 方法的时候,会直接将连接交还给数据库而不是再放回连接池中。所以我们要动态的改变 connection 方法。有三种方式可以实现
(1)写一个 connection 的子类,复写所有方法。但是这种方法有一个问题,因为 connection 对象中封装了太多数据,我们如何将 connection 的数据拷贝到我们当前的子类中呢?这是一个复杂的过程。(2)用装饰模式,装饰模式可以动态的为一个类增加新功能。装饰模式有几个步骤,首先装饰类和被装饰类要有一个共同的父接口,其次装饰类中维护着一个被装饰类的对象,同时在装饰类的构造函数中要将被装饰的对象传进来,目的是赋值。然后我们可以为根据需求增加修改原来的功能。当我们要使用被装饰类的时候,我们给用户返回的是我们装饰后的对象, 从而达到目的。但是装饰模式比较适用于简单类,不适合用于方法多的对象,因为我们有时仅仅需要修改一部分功能,而其它功能还是希望调用被装饰类本身的方法,那么这样我们就必须一个一个方法进行修改,来调用被装饰类的方法,这个工程非常庞大。(3)使用动态代理,动态代理可以实现拦截对对象的直接访问。如果我们想修改某一个方法,只需要在调用对象之前进行判断即可,如果是我们想修改的,我们直接拦截,如果不是我们想要的,我们可以直接交给被代理对象处理。由于动态代理是基于代理对象和被代理对象要有相同接口,所以我们可以基于接口统一调用,运行时以多态的形式表现出来。
- public class DataSourcePool {
- private static LinkedList<Connection> list =new LinkedList<Connection>();
- static {
- for (int i = 0; i < 5; i++) {
- Connection con = DBHelper.getConnection();
- list.add(con);
- }
- }
- public Connection getConnection() throws SQLException {
- if (list.size() > 0) {
- System.out.println("当前连接池有:" + list.size() + "个连接");
- final Connection con = list.removeFirst();
- System.out.println("当前连接池有:" + list.size() + "个连接");
- return (Connection) Proxy.newProxyInstance(DataSourcePool.class.getClassLoader(), con.getClass().getInterfaces(),new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- String methodName = method.getName();
- if(methodName.equals("close")){
- list.add(con);
- System.out.println("当前连接池有:" + list.size() + "个连接");
- return null;
- }else{
- return method.invoke(con, args);
- }
- }
- });
- }
- return null;
- }
- }
public class DataSourcePool {
private static LinkedList<Connection> list = new LinkedList<Connection>();
static {
for (int i = 0; i < 5; i++) {
Connection con = DBHelper.getConnection();
list.add(con);
}
}
public Connection getConnection() throws SQLException {
if (list.size() > 0) {
System.out.println("当前连接池有:" + list.size() + "个连接");
final Connection con = list.removeFirst();
System.out.println("当前连接池有:" + list.size() + "个连接");
return (Connection) Proxy.newProxyInstance(DataSourcePool.class.getClassLoader(), con.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if(methodName.equals("close")){
list.add(con);
System.out.println("当前连接池有:" + list.size() + "个连接");
return null;
}else{
return method.invoke(con, args);
}
}
});
}
return null;
}
}
3、配置 Tomcat 内置 dbcp 连接池
Tomcat 采用的是 JNDI 容器存放连接的,在服务器启动的时候,会在 JNDI 容器中创建一批连接,应用程序需要连接的时候直接在 JNDI 容器中查找。我们在使用时首先应该初始化 JNDI ,然后获取 Tomcat 中 JNDI 容器,最后在 JNDI 容器中检索连接池。
- Context initCtx = new InitialContext();
- Context envCtx = (Context) initCtx.lookup("java:comp/env");
- DataSource source = (DataSource) envCtx.lookup("jdbc/EmployeeDB");