package com.jing.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;

/**
 * 连接数据库的综合类。commons.dbcp-1.4,commons.dbutils-1.3,commons.pool-1.5.4包需要。
 * @author 朱志杰 QQ:695520848
 * Jul 16, 2013 10:15:00 AM
 */
public class DBUtil {
	
	private String dri = null;
	private String url = null;
	private String username = null;
	private String password = null;
	private String poolName = null;	//连接池名称
	private ObjectPool connectionPool = null; //连接池
	//取出时检查连接是否有效。
	private boolean testOnBorrow=true;
	
	/**
	 * 功能:获取一个DBUtil对象。
	 * @author 朱志杰 QQ:695520848
	 * Jul 16, 2013 10:24:00 AM
	 * @param dri 驱动全类名,例如:com.mysql.jdbc.Driver。
	 * @param url 数据库url连接,例如:"jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"
	 * @param userName 数据库用户名,例如:root
	 * @param password 数据库密码,例如:abc
	 * @param poolName 创建的数据库连接池的名称,例如mypool,注意一个web容器此名称不能重复。
	 */
	public static DBUtil getInstance(String dri,String url,String userName,String password,String poolName) {
		return new DBUtil( dri, url, userName, password, poolName);
	}
	
	
	/**
	 * 功能:构造函数
	 * @author 朱志杰 QQ:695520848
	 * Jul 16, 2013 10:24:00 AM
	 * @param dri 驱动全类名,例如:com.mysql.jdbc.Driver。
	 * @param url 数据库url连接,例如:"jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"
	 * @param userName 数据库用户名,例如:root
	 * @param password 数据库密码,例如:abc
	 * @param poolName 创建的数据库连接池的名称,例如mypool,注意一个web容器此名称不能重复。
	 */
	private  DBUtil(String dri,String url,String userName,String password,String poolName){
		this.dri=dri;
		this.url=url;
		this.username=userName;
		this.password=password;
		this.poolName=poolName;
	}
	
	/**
	 * 执行sql。
	 * @param conn 连接
	 * @param pstm PreparedStatement
	 * @return int 执行sql对应的影响行。
	 * @throws SQLException 
	 */
	public int execute(Connection conn,PreparedStatement pstm) throws SQLException {
		try {
			return pstm.executeUpdate();
		}  finally {
			Close(conn);
		}
	}
	
	/**
	 * 查询sql。
	 * @param conn 连接
	 * @param pstm PreparedStatement
	 * @return List<Map<String,Object>> 查询的结果集
	 * @throws SQLException 
	 */
	public List<Map<String,Object>> query(Connection conn,PreparedStatement pstm) throws SQLException {
		try {
			return resultSetToList(pstm.executeQuery());
		}  finally {
			Close(conn);
		}
	}
	
	/**
	 * 功能:ResultSet 转为List<Map<String,Object>>
	 * @author 朱志杰 QQ:862990787
	 * 2014-3-7 下午08:55:11
	 * @param rs ResultSet 原始数据集
	 * @return List<Map<String,Object>>
	 * @throws java.sql.SQLException
	 */
	private List<Map<String,Object>> resultSetToList(ResultSet rs) throws java.sql.SQLException {   
		if (rs == null)
			return Collections.EMPTY_LIST;

		ResultSetMetaData md = rs.getMetaData(); // 得到结果集(rs)的结构信息,比如字段数、字段名等
		int columnCount = md.getColumnCount(); // 返回此 ResultSet 对象中的列数
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		Map<String, Object> rowData = new HashMap<String, Object>();
		while (rs.next()) {
			rowData = new HashMap<String, Object>(columnCount);
			for (int i = 1; i <= columnCount; i++) {
				rowData.put(md.getColumnName(i), rs.getObject(i));
			}
			list.add(rowData);
		}
		return list;
	}

	/**
	 * 查询sql语句。
	 * @param sql 被执行的sql语句
	 * @return List<Map<String,Object>>
	 * @throws SQLException 
	 */
	public List<Map<String,Object>> query(String sql) throws SQLException {
		List<Map<String,Object>> results = null;
		Connection conn = null;
		try {
			conn = getConnection();
			QueryRunner qr = new QueryRunner();
			results =qr.query(conn, sql, new MapListHandler());
		}  finally {
			Close(conn);
		}
		return results;
	}
	
	/**
	 * 根据参数查询sql语句
	 * @param sql sql语句
	 * @param param 参数
	 * @return List<Map<String,Object>>
	 * @throws SQLException 
	 */
	public List<Map<String,Object>> query(String sql, Object param) throws SQLException {
		List<Map<String,Object>> results = null;
		Connection conn = null;
		try {
			conn = getConnection();
			QueryRunner qr = new QueryRunner();
			results = (List<Map<String,Object>>) qr.query(conn, sql, param, new MapListHandler());
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			Close(conn);
		}
		return results;
	}
	
	/**
	 * 根据参数查询sql语句
	 * @param sql sql语句
	 * @param param 参数数组
	 * @return List<Map<String,Object>>
	 * @throws SQLException 
	 */
	public List<Map<String,Object>> query(String sql, Object... param) throws SQLException {
		List<Map<String,Object>> results = null;
		Connection conn = null;
		try {
			conn = getConnection();
			QueryRunner qr = new QueryRunner();
			results = (List<Map<String,Object>>) qr.query(conn, sql, param, new MapListHandler());
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			Close(conn);
		}
		return results;
	}

	/**
	 * 执行sql语句
	 * @param sql 被执行的sql语句
	 * @return 受影响的行
	 * @throws Exception
	 */
	public int execute(String sql) throws Exception {
		Connection conn = getConnection();
		int rows = 0;
		try {
			QueryRunner qr = new QueryRunner();
			rows = qr.update(conn, sql);
		} finally {
			Close(conn);
		}
		return rows;
	}

	/**
	 * 执行含参数的sql语句
	 * @param sql 被执行的sql语句
	 * @param params 参数
	 * @return 返回受影响的行
	 * @throws Exception
	 */
	public int execute(String sql, Object[] params) throws Exception {
		Connection conn = getConnection();
		int rows = 0;
		try {
			QueryRunner qr = new QueryRunner();
			rows = qr.update(conn, sql, params);
		} finally {
			Close(conn);
		}
		return rows;
	}

	/**
	 * 关闭连接
	 * @param conn
	 * @throws SQLException 
	 */
	public void Close(Connection conn) throws SQLException {
		if(conn!=null){
			conn.close();
		}
		DbUtils.closeQuietly(conn);
	}
	
	/**
	 * 启动连接池
	 */
	private void StartPool() {
		try {
			Class.forName(dri);
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		}
		if (connectionPool != null) {
			ShutdownPool();
		}
		try {
			GenericObjectPool.Config config = new GenericObjectPool.Config();  
			//取出时检查连接是否有效。
			config.testOnBorrow=testOnBorrow;
	        
			connectionPool = new GenericObjectPool(null,config);
			ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
					url, username, password);
			PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
					connectionFactory, connectionPool, null, "SELECT 1",
					false, true);
			Class.forName("org.apache.commons.dbcp.PoolingDriver");
			PoolingDriver driver = (PoolingDriver) DriverManager
					.getDriver("jdbc:apache:commons:dbcp:");
			driver.registerPool(poolName, poolableConnectionFactory.getPool());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 关闭连接池
	 */
	private void ShutdownPool() {
		try {
			PoolingDriver driver = (PoolingDriver) DriverManager
					.getDriver("jdbc:apache:commons:dbcp:");
			driver.closePool(poolName);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 得到一个连接
	 * @return
	 */
	public synchronized Connection getConnection() {
		Connection conn = null;
		try {
			if (connectionPool == null)
				StartPool();
			conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:"+poolName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 取出时检查连接是否有效。
	 * @return
	 */
	public boolean isTestOnBorrow() {
		return testOnBorrow;
	}


	/**
	 * 设置取出时检查连接是否有效。
	 * @param testOnBorrow true 检测,false 不检测。
	 */
	public void setTestOnBorrow(boolean testOnBorrow) {
		this.testOnBorrow = testOnBorrow;
	}
}