数据库连接池
一、应用程序直接获取数据库连接的缺点
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费
数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:
1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
3. 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载
的getConnection方法:
? Connection getConnection() ———————————————————————————————————————————————
? Connection getConnection(String username, String password)
实现DataSource接口,并实现连接池功能的步骤:
1. 在DataSource构造函数中批量创建与数据库的连接,并把创
建的连接加入LinkedList对象中。
7 import java.lang.reflect.Proxy;
8 import java.sql.Connection;
9 import java.sql.DriverManager;
10 import java.sql.SQLException;
11 import java.util.LinkedList;
12 import java.util.Properties;
13 import javax.sql.DataSource;
14
15 /**
16 * @ClassName: JdbcPool
18 * @author: admin
19 * @date: 2014-9-30 下午11:07:23
20 *
21 */
22 public class JdbcPool implements DataSource{
23
24 /**
25 * @Field: listConnections ———————————————————————————————————————————————
26 * 使用LinkedList集合来存放数据库链接,
27 * 由于要频繁读写List集合,所以这里使用LinkedList存储数
据库连接比较合适28 */
29 private static LinkedList listConnections = new LinkedList();
30
31 static{
32 //在静态代码块中加载db.properties数据库配置文件
33 InputStream in =
JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
34 Properties prop = new Properties();
35 try {
36 prop.load(in);
37 String driver = prop.getProperty("driver");
38 String url = prop.getProperty("url");
39 String username = prop.getProperty("username");
40 String password = prop.getProperty("password");
42 int jdbcPoolInitSize
=Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));43 //加载数据库
驱动
44 Class.forName(driver);
45 for (int i = 0; i < jdbcPoolInitSize; i++) { ———————————————————————————————————————————————
46 Connection conn = DriverManager.getConnection(url, username,
password);47 System.out.println("获取到了链接" + conn);
48 //将获取到的数据库连接加入到listConnections集合中,
listConnections集合此时就是一个存放了数据库连接的连接池
49 listConnections.add(conn);
50 }
51
52 } catch (Exception e) {
53 throw new ExceptionInInitializerError(e);
54 }
55 }
56
57 @Override
58 public PrintWriter getLogWriter() throws SQLException {
59 // TODO Auto-generated method stub
60 return null;
61 }
62
63 @Override
64 public void setLogWriter(PrintWriter out) throws SQLException
{65 // TODO Auto-generated method stub
66
———————————————————————————————————————————————
67 }
68
69 @Override
70 public void setLoginTimeout(int seconds) throws SQLException
{71 // TODO Auto-generated method stub
72
73 }
74
75 @Override
76 public int getLoginTimeout() throws SQLException {
77 // TODO Auto-generated method stub
78 return 0;
79 }
80
81 @Override
82 public T unwrap(Class iface) throws SQLException {
83 // TODO Auto-generated method stub
84 return null;
85 }
86
87 @Override
88 public boolean isWrapperFor(Class iface) throws SQLException
———————————————————————————————————————————————
{89 // TODO Auto-generated method stub
90 return false;
91 }
92
93 /* 获取数据库连接
94 * @see javax.sql.DataSource#getConnection()
95 */
96 @Override
97 public Connection getConnection() throws SQLException {
99 if (listConnections.size()>0) {
100 //从listConnections集合中获取一个数据库连接
101 final Connection conn = listConnections.removeFirst();
104 return (Connection)
Proxy.newProxyInstance(JdbcPool.class.getClassLoader(),conn.getClass().
getInterfaces(), new InvocationHandler(){
105 @Override
106 public Object invoke(Object proxy, Method method, Object[] args)
107 throws Throwable {
108 if(!method.getName().equals("close")){
109 return method.invoke(conn, args);
110 }else{
———————————————————————————————————————————————
116 }
117 }
8
9 public class JdbcUtil {
10
11 /**
12 * @Field: pool
14 */
15 private static JdbcPool pool = new JdbcPool();
16
17 /**
18 * @Method: getConnection
20 * @Anthor:admin
21 * @return Connection数据库连接对象
22 * @throws SQLException
23 */
24 public static Connection getConnection() throws SQLException{
25 return pool.getConnection();
26 }
27
28 /**
29 * @Method: release ———————————————————————————————————————————————
30 * @Description: 释放资源,
31 * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
32 * @Anthor:admin
33 *
34 * @param conn
35 * @param st
36 * @param rs
37 */
38 public static void release(Connection conn,Statement
st,ResultSet rs){39 if(rs!=null){
40 try{
41 //关闭存储查询结果的ResultSet对象
42 rs.close();
43 }catch (Exception e) {
44 e.printStackTrace();
45 }
46 rs = null;
47 }
48 if(st!=null){
49 try{
50 //关闭负责执行SQL命令的Statement对象
———————————————————————————————————————————————
51 st.close();
52 }catch (Exception e) {
53 e.printStackTrace();
54 }
55 }
56
57 if(conn!=null){
58 try{
59 //关闭Connection数据库连接对象
60 conn.close();
61 }catch (Exception e) {
62 e.printStackTrace();
6
7 #
8 initialSize=10
9
10 #最大连接数量
11 maxActive=50
12
13 #
14 maxIdle=20
15
———————————————————————————————————————————————
16 #
17 minIdle=5
18
19 #
20 maxWait=60000
21
22
23 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]24 #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。25
connectionProperties=useUnicode=true;characterEncoding=UTF8
26
27 #指定由连接池所创建的连接的自动提交(auto-commit)状态。
28 defaultAutoCommit=true
29
30 #driver default 指定由连接池所创建的连接的只读(read-only)状态。
31 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
32 defaultReadOnly=
33
12 /**
———————————————————————————————————————————————
13 * @ClassName: JdbcUtils_DBCP
14 * @Description: 数据库连接工具类
15 * @author: admin
16 * @date: 2014-10-4 下午6:04:36
17 *
18 */
19 public class JdbcUtils_DBCP {
20 /**
22 * DBCP连接池就是java.sql.DataSource接口的一个具体实现
23 */
24 private static DataSource ds = null;
26 static{
27 try{
28 //加载dbcpconfig.properties配置文件
29 InputStream in =
JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfi
g.properties");
30 Properties prop = new Properties();
31 prop.load(in);
32 //创建数据源
33 ds = BasicDataSourceFactory.createDataSource(prop);
———————————————————————————————————————————————
34 }catch (Exception e) {
35 throw new ExceptionInInitializerError(e);
36 }
37 }
38
39 /**
40 * @Method: getConnection
41 * @Description: 从数据源中获取数据库连接
42 * @Anthor:admin
43 * @return Connection
44 * @throws SQLException
45 */
46 public static Connection getConnection() throws SQLException{
47 //从数据源中获取数据库连接
48 return ds.getConnection();
49 }
50
51 /**
52 * @Method: release
53 * @Description: 释放资源,
54 * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
———————————————————————————————————————————————
55 * @Anthor:admin
56 *
57 * @param conn
58 * @param st
59 * @param rs
60 */
61 public static void release(Connection conn,Statement
st,ResultSet rs){
62 if(rs!=null){
63 try{
64 //关闭存储查询结果的ResultSet对象
65 rs.close();
66 }catch (Exception e) {
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import org.junit.Test;
7 import me.gacl.util.JdbcUtils_DBCP;
8
9 public class DataSourceTest {
10
11 @Test
12 public void dbcpDataSourceTest() { ———————————————————————————————————————————————
13 Connection conn = null;
14 PreparedStatement st = null;
15 ResultSet rs = null;
16 try{
17 //获取数据库连接
18 conn = JdbcUtils_DBCP.getConnection();
19 String sql = "insert into test1(name) values(?)";20 st =
conn.prepareStatement(sql);
21 st.setString(1, "gacl");
22 st.executeUpdate();
23 //获取数据库自动生成的主键
24 rs = st.getGeneratedKeys();
25 if(rs.next()){
26 System.out.println(rs.getInt(1));
27 }
28 }catch (Exception e) {
29 e.printStackTrace();
30 }finally{
31 //释放资源
17
18 private static ComboPooledDataSource ds = null;
20 static{
———————————————————————————————————————————————
21 try{
23 /*ds = new ComboPooledDataSource();
24 ds.setDriverClass("com.mysql.jdbc.Driver");
25 ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
26 ds.setUser("root");
27 ds.setPassword("XDP");
28 ds.setInitialPoolSize(10);
29 ds.setMinPoolSize(5);
30 ds.setMaxPoolSize(20);*/
31
32 //通过读取C3P0的xml配置文件创建数据源,C3P0的xml配
置文件c3p0-config.xml必须放在src目录下
33 //ds = new ComboPooledDataSource();//使用C3P0的默认配置
来创建数据源
34 ds = new ComboPooledDataSource("MySQL");//使用C3P0的命
名配置来创建数据源35
36 }catch (Exception e) {
37 throw new ExceptionInInitializerError(e);
38 }
39 }
40
41 /**
———————————————————————————————————————————————
42 * @Method: getConnection
43 * @Description: 从数据源中获取数据库连接
45 * @return Connection
46 * @throws SQLException
47 */
48 public static Connection getConnection() throws SQLException{
49 //从数据源中获取数据库连接
50 return ds.getConnection();
51 }
52
53 /**
54 * @Method: release
55 * @Description: 释放资源,
56 * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
57 * @Anthor:admin
58 *
59 * @param conn
60 * @param st
61 * @param rs
62 */
63 public static void release(Connection conn,Statement
———————————————————————————————————————————————
st,ResultSet rs){
64 if(rs!=null){
65 try{
66 //关闭存储查询结果的ResultSet对象
67 rs.close();
68 }catch (Exception e) {
69 e.printStackTrace();
70 }
71 rs = null;
7 import me.gacl.util.JdbcUtils_C3P0;
8 import me.gacl.util.JdbcUtils_DBCP;
9
10 public class DataSourceTest {
11
12 @Test
13 public void c3p0DataSourceTest() {
14 Connection conn = null;
15 PreparedStatement st = null;
16 ResultSet rs = null;
17 try{
18 //获取数据库连接
19 conn = JdbcUtils_C3P0.getConnection(); ———————————————————————————————————————————————
20 String sql = "insert into test1(name) values(?)";21 st =
conn.prepareStatement(sql);
22 st.setString(1, "gacl");
23 st.executeUpdate();
24 //获取数据库自动生成的主键
25 rs = st.getGeneratedKeys();
26 if(rs.next()){
27 System.out.println(rs.getInt(1));
28 }
29 }catch (Exception e) {
30 e.printStackTrace();
31 }finally{
32 //释放资源
33 JdbcUtils_C3P0.release(conn, st, rs);34 }
11 /**
12 * @ClassName: JdbcUtils_DBCP
13 * @Description: 数据库连接工具类
14 * @author: admin
15 * @date: 2014-10-4 下午6:04:36
16 *
17 */
18 public class JdbcUtils_JNDI { ———————————————————————————————————————————————
19
20 private static DataSource ds = null;
22 static{
23 try{
24 //初始化JNDI
25 Context initCtx = new InitialContext();
26 //得到JNDI容器
27 Context envCtx = (Context) initCtx.lookup("java:comp/env");28 //
从JNDI容器中检索name为jdbc/datasource的数据源29 ds =
(DataSource)envCtx.lookup("jdbc/datasource");30 }catch (Exception e) {
31 throw new ExceptionInInitializerError(e);
32 }
33 }
34
35 /**
36 * @Method: getConnection
37 * @Description: 从数据源中获取数据库连接
38 * @Anthor:admin
39 * @return Connection
40 * @throws SQLException
41 */
42 public static Connection getConnection() throws SQLException{
———————————————————————————————————————————————
43 //从数据源中获取数据库连接
44 return ds.getConnection();
45 }
46
47 /**
48 * @Method: release
49 * @Description: 释放资源,
50 * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
51 * @Anthor:admin
52 *
53 * @param conn
54 * @param st
55 * @param rs
56 */
57 public static void release(Connection conn,Statement
st,ResultSet rs){
58 if(rs!=null){
59 try{
60 //关闭存储查询结果的ResultSet对象
61 rs.close();
62 }catch (Exception e) { ———————————————————————————————————————————————
63 e.printStackTrace();
64 }
65 rs = null;
66 }
8 import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import me.gacl.util.JdbcUtils_JNDI;
12
13 public class JNDITest extends HttpServlet {
14
15 public void doGet(HttpServletRequest request, HttpServletResponse response)16 throws ServletException, IOException
{
17 Connection conn = null;
18 PreparedStatement st = null;
19 ResultSet rs = null;
20 try{
21 //获取数据库连接
22 conn = JdbcUtils_JNDI.getConnection();
23 String sql = "insert into test1(name) values(?)";
24 st = conn.prepareStatement(sql);
———————————————————————————————————————————————
25 st.setString(1, "gacl");
26 st.executeUpdate();
27 //获取数据库自动生成的主键
28 rs = st.getGeneratedKeys();
29 if(rs.next()){
30 System.out.println(rs.getInt(1));
31 }
32 }catch (Exception e) {
33 e.printStackTrace();
34 }finally{
35 //释放资源
———————————————————————————————————————————————