当前访客身份:游客 [ 登录 | 加入开源中国 ]

代码分享

当前位置:
代码分享 » Java  » 数据库编程
红薯

再发 OSChina 的 DBManager 类

红薯 发布于 2010年09月25日 23时, 45评/12337阅
分享到: 
收藏 +0
5
应会员们的要求,贴出 DBManager 类,这个类是用来管理数据库连接的,底层使用的是 c3p0 连接池。
标签: OSCHINA C3P0

代码片段(2) [全屏查看所有代码]

1. [代码]DBManager.java     跳至 [1] [2] [全屏预览]

package my.db;

import java.sql.*;
import java.util.*;
import java.lang.reflect.*;

import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 数据库管理
 * @author Winter Lau
 * @date 2010-2-2 下午10:18:50
 */
public class DBManager {

	private final static Log log = LogFactory.getLog(DBManager.class);
	private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
	private static DataSource dataSource;
	private static boolean show_sql = false;
	
	static {
		initDataSource(null);
	}

	/**
	 * 初始化连接池
	 * @param props
	 * @param show_sql
	 */
	private final static void initDataSource(Properties dbProperties) {
		try {
			if(dbProperties == null){
				dbProperties = new Properties();
				dbProperties.load(DBManager.class.getResourceAsStream("db.properties"));
			}
			Properties cp_props = new Properties();
			for(Object key : dbProperties.keySet()) {
				String skey = (String)key;
				if(skey.startsWith("jdbc.")){
					String name = skey.substring(5);
					cp_props.put(name, dbProperties.getProperty(skey));
					if("show_sql".equalsIgnoreCase(name)){
						show_sql = "true".equalsIgnoreCase(dbProperties.getProperty(skey));
					}
				}
			}
			dataSource = (DataSource)Class.forName(cp_props.getProperty("datasource")).newInstance();
			if(dataSource.getClass().getName().indexOf("c3p0")>0){
				//Disable JMX in C3P0
				System.setProperty("com.mchange.v2.c3p0.management.ManagementCoordinator", 
						"com.mchange.v2.c3p0.management.NullManagementCoordinator");
			}
			log.info("Using DataSource : " + dataSource.getClass().getName());
			BeanUtils.populate(dataSource, cp_props);

			Connection conn = getConnection();
			DatabaseMetaData mdm = conn.getMetaData();
			log.info("Connected to " + mdm.getDatabaseProductName() + 
                              " " + mdm.getDatabaseProductVersion());
			closeConnection();
		} catch (Exception e) {
			throw new DBException(e);
		}
	}
	
	/**
	 * 断开连接池
	 */
	public final static void closeDataSource(){
		try {
			dataSource.getClass().getMethod("close").invoke(dataSource);
		} catch (NoSuchMethodException e){ 
		} catch (Exception e) {
			log.error("Unabled to destroy DataSource!!! ", e);
		}
	}

	public final static Connection getConnection() throws SQLException {
		Connection conn = conns.get();
		if(conn ==null || conn.isClosed()){
			conn = dataSource.getConnection();
			conns.set(conn);
		}
		return (show_sql && !Proxy.isProxyClass(conn.getClass()))?
                      new _DebugConnection(conn).getConnection():conn;
	}
	
	/**
	 * 关闭连接
	 */
	public final static void closeConnection() {
		Connection conn = conns.get();
		try {
			if(conn != null && !conn.isClosed()){
				conn.setAutoCommit(true);
				conn.close();
			}
		} catch (SQLException e) {
			log.error("Unabled to close connection!!! ", e);
		}
		conns.set(null);
	}

	/**
	 * 用于跟踪执行的SQL语句
	 * @author Winter Lau
	 */
	static class _DebugConnection implements InvocationHandler {
		
		private final static Log log = LogFactory.getLog(_DebugConnection.class);
		
		private Connection conn = null;

		public _DebugConnection(Connection conn) {
			this.conn = conn;
		}

		/**
		 * Returns the conn.
		 * @return Connection
		 */
		public Connection getConnection() {
			return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), 
                             conn.getClass().getInterfaces(), this);
		}
		
		public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
			try {
				String method = m.getName();
				if("prepareStatement".equals(method) || "createStatement".equals(method))
					log.info("[SQL] >>> " + args[0]);				
				return m.invoke(conn, args);
			} catch (InvocationTargetException e) {
				throw e.getTargetException();
			}
		}

	}
	
}

2. [代码]db.properties     跳至 [1] [2] [全屏预览]

# DataSource
jdbc.datasource=com.mchange.v2.c3p0.ComboPooledDataSource
jdbc.show_sql=true

# Database Configurations
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/oscdb
jdbc.user=root
jdbc.password=xxxx
jdbc.maxPoolSize=100
jdbc.minPoolSize=2
jdbc.initialPoolSize=2
jdbc.acquireIncrement=2
jdbc.maxStatements=1000
jdbc.maxIdleTime=300
jdbc.checkoutTimeout=5000


开源中国-程序员在线工具:Git代码托管 API文档大全(120+) JS在线编辑演示 二维码 更多»

发表评论 回到顶部 网友评论(45)

  • 1楼:张宋付 发表于 2010-11-02 02:55 回复此评论
    其实这个最主要还是这个东西。
    要是没有这个了。这个并发时就是有问题了。
    呵呵。。。
    private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
    大家可以细细查看spring中源码中。
    spring数据源封装中肯定有这个身影的。呵呵。

  • 2楼:山哥 发表于 2010-11-11 14:01 回复此评论
    红薯大哥,你的这个DBManager使用 ThreadLocal 保存单一Connection 对于多数据源系统是不是不适应啊?
    比如:在一个 action(Struts2的Action) 中的一个方法中调用了分属2个数据源的DAO:
    public void saveXX()
    {
        // 调用DAO1,DAO1的数据源是 ds1
        DAO1.save(...);
        
        // 调用DAO2,DAO2的数据源是 ds2
        DAO2.save(...);
    }
    这时,是不是要修改ThreadLocal的变量为 HashMap : key = 唯一标识数据源名称, value = 对应的Connection,使用 DBManager.getConnection( String key ) 来获取对应数据源的Connection 啊??






  • 3楼:JavaGG 发表于 2010-11-12 09:53 回复此评论
    哈哈,终于不用什么连接池了,早就应把这个做到线程 上 了~~
  • 4楼:张麟 发表于 2010-11-12 11:58 回复此评论
    checkoutTimeout
  • 5楼:不会飞的羊 发表于 2010-12-29 08:57 回复此评论

    弱弱的问一句这里关闭连接为什么是conn.close();
    而不是将连接返回给连接池,

  • 6楼:冰封情 发表于 2011-01-15 17:02 回复此评论

    引用来自“JSON”的评论

    红薯大哥,你的这个DBManager使用 ThreadLocal 保存单一Connection 对于多数据源系统是不是不适应啊?
    比如:在一个 action(Struts2的Action) 中的一个方法中调用了分属2个数据源的DAO:
    public void saveXX()
    {
        // 调用DAO1,DAO1的数据源是 ds1
        DAO1.save(...);
        
        // 调用DAO2,DAO2的数据源是 ds2
        DAO2.save(...);
    }
    这时,是不是要修改ThreadLocal的变量为 HashMap : key = 唯一标识数据源名称, value = 对应的Connection,使用 DBManager.getConnection( String key ) 来获取对应数据源的Connection 啊??






    是的哦,红薯这个只能对应一个数据源,如果多个就不行了
  • 7楼:小杨阿哥哥 发表于 2011-03-09 10:35 回复此评论
    这个事务是在Action上,还是在service上
  • 8楼:与梦同行 发表于 2011-05-24 11:19 回复此评论
    为什么加入log4j后就会报错?
  • 9楼:tsl0922 发表于 2011-07-30 20:25 回复此评论
    那个用于获取sql语句的代理类好像有点小问题。
    Connection的CreateStatement方法应该是不能传sql语句进去的吧?所以通过CreateStatement的args是获取不到sql语句的,甚至还可能会报错吧?
  • 10楼:游客 发表于 2011-09-01 18:50 回复此评论
    连接池在什么时候初始化的?
  • 11楼:大龙虾 发表于 2012-02-03 21:13 回复此评论
    这个
      return (show_sql && !Proxy.isProxyClass(conn.getClass()))?

                         new _DebugConnection(conn).getConnection():conn;
    是什么意思啊
  • 12楼:flygogo 发表于 2012-02-06 21:47 回复此评论

    这个地方貌似不太对吧?   createStatement 的时候是没有参数的。
  • 13楼:小薇 发表于 2012-03-08 13:24 回复此评论
    不好意思,我才开始接触数据库连接池,我想问问这段代码好像没有用到   c3p0  什么东西啊?
  • 14楼:小薇 发表于 2012-03-08 13:28 回复此评论
    还有一个, ThreadLocal在代码中是实现获取connection的并发吗??


    我在一个小项目里面利用C3P0是这个进行管理的,不知道行不行?请教:

    /**
     *从数据库连接池获得数据库连接
     */
    package com.bluesea.db;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    /**
     * 
     * @author Xewee.Zhiwei.Wang
     * @version 2012-2-28 下午05:46:43
     */
    public class DBPool {
    
    	private static final ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
    	/**
    	 * 
    	 */
    	private DBPool() {
    		
    	}
    	/**
    	 * 从连接池获得数据库连接
    	 * @author Xewee.Zhiwei.Wang
    	 * @version 2012-3-1 下午03:18:03
    	 * @return
    	 * @throws SQLException
    	 */
    	public static Connection getConnection() throws SQLException {
    //		System.out.println(cpds.getPassword());
    		return cpds.getConnection();
    	}
    	/**
    	 * 关闭某一个数据库连接
    	 * @author Xewee.Zhiwei.Wang
    	 * @version 2012-3-7 下午06:18:43
    	 * @param connection
    	 * @param statement
    	 * @param resultSet
    	 */
    	public static void close(Connection connection, Statement statement, ResultSet resultSet) {
    		try {
    			if (statement != null) {
    				statement.close();
    			}
    			if (connection != null) {
    				connection.close();
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    }



  • 15楼:香草 发表于 2012-04-04 12:46 回复此评论

    引用来自“不会飞的羊”的评论

    弱弱的问一句这里关闭连接为什么是conn.close();
    而不是将连接返回给连接池,

    还有为什么closeDataSource 不直接调用,要用反射
  • 16楼:beidao-ai 发表于 2012-04-19 23:58 回复此评论

    引用来自“tianshupei88”的评论

    这个
      return (show_sql && !Proxy.isProxyClass(conn.getClass()))?

                         new _DebugConnection(conn).getConnection():conn;
    是什么意思啊
    我觉得,这个就是判断是否显示sql语句,通过动态代理
  • 17楼:铂金大雕 发表于 2012-05-06 12:58 回复此评论
    dataSource.getConnection();是线程安全的吗? @红薯
  • 18楼:无名人士 发表于 2012-05-08 23:45 回复此评论

    引用来自“wingyiu”的评论

    dataSource.getConnection();是线程安全的吗? @红薯
    dataSource当然不是线程安全的,它只是一个对象,getConnection()是一个方法,没有线程安全之说,基本上很多方法都要被多线程调用的吧~
  • 19楼:水牛叔叔 发表于 2012-05-27 12:51 回复此评论

    引用来自“不会飞的羊”的评论

    弱弱的问一句这里关闭连接为什么是conn.close();
    而不是将连接返回给连接池,

    在一个叫做miniConnectionPoolMagager数据库连接池里,如果conn是从连接池里拿出来的,那么conn.close( )的效果就是把conn返回到原来的连接池。应该其他连接池也是这样处理的。
  • 20楼:水牛叔叔 发表于 2012-05-27 13:11 回复此评论
    请问代码里那个closeDatasource()方法是被谁调用的?如何调用?
    我一直很纠结当我的服务器关闭时,池里的那些connection怎么关闭。请教
开源从代码分享开始 分享代码