一、使用详解

  • 在mysql中创建一个库mall_system,并创建user表和插入表的数据。
  • 新建一个Java工程jdbc,并导入数据驱动。

二、详细步骤

1、加载数据库驱动

1
2
3
4
5
//1.加载驱动(开发推荐的方式) mysql版本5.x方法
Class.forName("com.mysql.jdbc.Driver");

//mysql8.x方法
Class.forName("com.mysql.cj.jdbc.Driver");

2、建立连接

1、数据库URL

URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:

jdbc:mysql:[]//localhost:3306/mall_system?参数名:参数值

常用数据库URL地址的写法:

  • Oracle:jdbc:oracle:thin:@localhost:1521:mall_system
  • SqlServer:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=mall_system
  • MySql:jdbc:mysql://localhost:3306/mall_system?useUnicode=true&characterEncoding=UTF-8&userSSL=true&serverTimezone=Hongkong

注意:如果是localhost:3306,mysql可以简写为jdbc:mysql:///sid(尽量不这样)

2、Connection

Jdbc程序中的Connection,它用于代表数据库的链接,Collection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,创建方法为:

1
Connection conn = DriverManager.getConnection(url,user,pass); 

这个对象的常用方法:

方法 描述
createStatement() 创建向数据库发送sql的statement对象。
prepareStatement(sql) 创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql) 创建执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit) 设置事务是否自动提交。
commit() 在链接上提交事务。
rollback() 在此链接上回滚事务。
1
2
3
4
5
6
7
String url = "jdbc:mysql://localhost:3306/mall_system";
String username = "egret";
String password = "123456";
Connection conn = null;

//2.获取与数据库的链接
conn = DriverManager.getConnection(url, username, password);

3、执行SQL语句

1、Statement

Jdbc程序中的Statement对象用于向数据库发送SQL语句,创建方法为:

1
Statement st = conn.createStatement();

Statement对象常用方法

方法 含义
executeQuery(String sql) 用于向数据发送查询语句。
executeUpdate(String sql) 用于向数据库发送insert、update或delete语句
execute(String sql) 用于向数据库发送任意sql语句
addBatch(String sql) 把多条sql语句放到一个批处理中。
executeBatch() 向数据库发送一批sql语句执行。
1
2
3
4
5
6
7
Statement st = null;
//3.获取用于向数据库发送sql语句的statement
st = conn.createStatement();

//4.向数据库发sql
String sql = "select id,name,password,email,birthday from users";
st.executeQuery(sql);

2、PreperedStatement

PreperedStatement是Statement的子类,它的实例对象的获取可以通过调用:

1
PreperedStatement st =  conn.preparedStatement()

示例:

1
2
3
4
5
6
7
8
9
10
PreperedStatement st = null;
String sql = "select * from users where name=? and password=?";

//3.获取用于向数据库发送sql语句的Preperedstatement
st = conn.preparedStatement(sql);//在此次传入,进行预编译
st.setString(1, username);
st.setString(2, password);

//4.向数据库发sql
st.executeQuery();//在这里不需要传入sql

比较:相对于Statement对象而言

  • PreperedStatement可以避免SQL注入的问题。

    1
    2
    3
    4
    5
    6
    7
    如:String sql="select * from admin where loginname='"+loginName+"' and loginpwd='"+loginPwd+"'";

    // 在应用中:
    // ->请输入账号:333
    //
    // ->请输入密码:wer'or'1'='1
    // 实际上发送:select * from admin where loginname='333' and //loginpwd='wer'or'1'='1',登录成功!
  • Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。

  • 并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。

4、获取结果

​ jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式,ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。

1、获取行

ResultSet提供了对结果集进行滚动的方法:

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast() :移动到resultSet的最后面。

2、获取值

ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:

  • 获取任意类型的数据

    getObject(int index)

    getObject(string columnName)

  • 获取指定类型的数据,例如:

    getString(int index)

    getString(String columnName)

附加:

常用数据类型转换:

SQL类型 Jdbc对应方法 返回类型
bit(1),bit(n) getBoolean,getBytes() Boolean,byte[]
tinyint getByte() Byte
smallint getShort() Short
int getInt Int
bigint getLong() Long
char,varchar,longvarchar getString String
text(clob) blob getClob(),getblob() Clob,blob
date getDate() java.sql.Date
time getTime() java.sql.Time
timestamp getTimestamp java.sql.Timestamp
1
2
3
4
5
6
7
8
9
10
11
12
13
ResultSet rs = null;
//4.向数据库发sql,并获取代表结果集的resultset
String sql = "select id,name,password,email,birthday from users";
rs = st.executeQuery(sql);

//5.取出结果集的数据
rs.afterLast();
rs.previous();
System.out.println("id=" + rs.getObject("id"));
System.out.println("name=" + rs.getObject("name"));
System.out.println("password=" + rs.getObject("password"));
System.out.println("email=" + rs.getObject("email"));
System.out.println("birthday=" + rs.getObject("birthday"));

或者

1
2
3
4
5
6
//循环取出(id)
while(rs.next())
{
String id=rs.getString(1);//1代表数据库中表的列数,id在第一列也可以("id")!!!
System.out.println(id+" ");
}

5、释放资源

​ Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。

注意:为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//6.关闭链接,释放资源
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;

}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}

}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}

三、基本操作

1、DDL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 在java中使用ddl语句(credate,drop,backup...)
*/
package com.shen.study2;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 test=new Test1();
}

public Test1()
{
this.lianjie();
}
public void lianjie()
{
//定义需要的对象
PreparedStatement ps=null;
Connection ct=null;
ResultSet rs=null;
try {
//初始化对象
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
ct = DriverManager.getConnection("jdbc:mysql://localhost:3306/mall_system");

//3.创建Preparestatement,创建数据
ps=ct.prepareStatement("create database vvv");
// ps=ct.prepareStatement("create table xxx");//创建表
// ps=ct.prepareStatement("backup database shen to disk='F:/123.bak'");//备份数据库

//如果执行的是ddl语句
boolean b=ps.execute();
if(b)
{
System.out.println("创建成功!");
}else {
System.out.println("失败");
}
} catch (Exception e) {
// TODO: handle exception
}finally {
//关闭资源
try {
//为了程序健壮
if(ps!=null)
ps.close();
if(ct!=null)
ct.close();
} catch (Exception e2) {
// TODO: handle exception
}
}

}
}

2、CRUD

实体类:user

1
2
3
4
5
6
7
8
9
public class User {
private int id;
private String name;
private String password;
private String email;
private Date birthday;

//相关的get和set
}

连接工具:JdbcUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUtils {

private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;

static{
try{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);

driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");

Class.forName(driver);

}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}


public static Connection getConnection() throws SQLException{

return DriverManager.getConnection(url, username,password);
}

public static void release(Connection conn,Statement st,ResultSet rs){

if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;

}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}

}

if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}

}

}
}

资源文件:db.properties

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mall_system
username=egret
password=123456

功能实现:Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

import cn.itcast.utils.JdbcUtils;.

//使用jdbc对数据库增删改查
public class Demo {

@Test
public void insert(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "insert into users(id,name,password,email,birthday) values(4,'xxx','123','xx@sina.com',to_date('1980-09-09','YYYY-MM-DD'))";
int num = st.executeUpdate(sql); //update
if(num>0){
System.out.println("插入成功!!");
}

}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}

@Test
public void delete(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "delete from users where id=4";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!!");
}
}catch (Exception e) {


}finally{
JdbcUtils.release(conn, st, rs);
}
}

@Test
public void update(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set name='wuwang',email='wuwang@sina.com' where id=3";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {


}finally{
JdbcUtils.release(conn, st, rs);
}
}

@Test
public void find(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=1";
st = conn.createStatement();
rs = st.executeQuery(sql);
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {

}finally{
JdbcUtils.release(conn, st, rs);
}
}

}

3、JdbcUtil

完整的JdbcUtil工具包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package cn.egret.util;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class JdbcUtil {
private static String url;
private static String username;
private static String password;
private static String driver;

static {
// 读取资源文件,获取值
// 创建properties集合类
Properties properties = new Properties();
try {
// 获取src路径下的文件的⽅式--->ClassLoader 类加载器
ClassLoader classLoader = JdbcUtil.class.getClassLoader();

// URL表示统⼀资源标识符
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
System.out.println(path);

// 2. 加载文件
properties.load(new FileReader(path));
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
Class.forName(driver);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 获取连接
*
* @return 连接对象
*/

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}

/***
* DML操作(增删改) 1.获取连接数据库对象 2.预处理 3.执行更新操作
*
* @param sql
* @param obj
*/
// 调用者只需传入一个sql语句,和一个Object数组。该数组存储的是SQL语句中的占位符
public static boolean executeUpdate(String sql, Object... obj) {

Connection con = null;
try {
con = getConnection(); // 调用getConnection()方法连接数据库
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);// 预处理

for (int i = 0; i < obj.length; i++) {// 预处理声明占位符
ps.setObject(i + 1, obj[i]);
}
int count = ps.executeUpdate();// 执行更新操作

if(count > 0) {
return true;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {

close(null, ps, con);// 调用close()方法关闭资源
}
return false;
}

/***
* DQL查询 Result获取数据集
*
* @param sql
* @param obj
* @return
*/
public static List<Map<String, Object>> executeQuery(String sql, Object... obj) {
Connection con = null;
try {
con = getConnection();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ResultSet rs = null;
PreparedStatement ps = null;

try {
ps = con.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();

// new 一个空的list集合用来存放查询结果
List<Map<String, Object>> list = new ArrayList<>();

// 获取结果集的列数
int count = rs.getMetaData().getColumnCount();

// 对结果集遍历每一条数据是一个Map集合,列是key,值是value
while (rs.next()) {
// 一个空的map集合,用来存放每一行数据
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < count; i++) {
Object ob = rs.getObject(i + 1);// 获取值
String key = rs.getMetaData().getColumnName(i + 1);// 获取k即列名
map.put(key, ob);
}
list.add(map);
}
return list;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {

close(rs, ps, con);
}

return null;

}

/**
* 释放资源
*
* @param rs
* @param ps
* @param conn
*/
public static void close(ResultSet rs, PreparedStatement ps, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();

}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

}

四、C3P0连接池

1、连接池定义

C3P0是一个开源的JDBC连接池,它实现了数据源与JNDI绑定,支持JDBC3规范和实现了JDBC2的标准扩展说明的Connection和Statement池的DataSources对象。

C3P0将用于连接数据库的连接整合在一起形成一个随取随用的数据库连接池(Connection pool)。

2、使用C3P0的必要性

当我们在进行基于数据库的web程序开发时,我们可以先在主程序(如Servlet、Bean)中通过JDBC中的DriverManager建立数据库连接,然后将要对数据库进行操作的sql语句封装到Statement中,最后在返回结果集后断开数据库连接。以上是较为传统的开发模式,然而用这种模式开发会埋下严重的安全隐患。

  • 时间和内存资源消耗巨大
    • 普通的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再根据JDBC代码(或配置文件)中的用户名和密码进行验证其正确性。这一过程一般会花费0.05~1s,一旦需要数据库连接的时候就必须向数据库请求一个,执行完后再断开连接。显然,如果同一个数据库在同一时间有数十人甚至上百人请求连接势必会占用大量的系统资源,严重的会导致服务器崩溃
  • 有内存泄漏的风险
    • 因为每一次数据库连接使用完后都需要断开连接,但如果程序出现异常致使连接未能及时关闭,这样就可能导致内存泄漏,最终只能以重启数据库的方法来解决;另外使用传统JDBC模式开发不能控制需要创建的连接数,系统一般会将资源大量分出给连接以防止资源不够用,如果连接数超出一定数量也会有极大的可能导致内存泄漏

为了解决由使用传统开发模式创建连接导致的一系列问题,我们可以采用数据库连接池技术

数据库连接池的基本原理就是为数据库建立一个缓冲池。在缓冲池中先创建指定数量的数据库连接,当有连接请求时就从缓冲池中取出处于“空闲”状态的连接,并将此连接标记为“忙碌”,直到该请求进程结束后,它所使用的连接才会重新回到“空闲”状态,并等待下一次请求调用

数据库连接池的主要作用就是负责分配、管理和释放数据库连接,它允许程序重复使用同一个现有的数据库连接,大大缩短了运行时间,提高了执行效率。这里需要强调一点的是,数据库连接池中的连接数是在其初始化时根据c3p0-config.xml中的最小连接数来确定的。当然,无论连接池的连接数是否有被使用,它都至少会保持最小连接数,如果请求连接数超过最小连接数也会根据c3p0-config.xml中指定的自增长数增加连接数直到达到最大连接数,这时如果请求连接数量还是大于连接池中的连接数的话,剩下的请求将会被放入等待队列直到有空闲连接出现

这样一来,数据库连接池相较于传统JDBC模式等到请求发出才创建连接的做法有着显而易见的优势

  • 资源的高效利用
    • 由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销,减小了系统资源消耗的同时也提高了系统运行环境的平稳性。
  • 更快的系统反应速度
    • 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接可以避免数据库在连接初始化和释放过程所需的时间开销,从而减少了系统的响应时间,提高了系统的反应速度
  • 减少了资源独占的风险
    • 新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现对某一应用最大可用数据库连接数的限制,避免了应用独占所有数据库资源的风险
  • 统一的连接管理,避免数据库连接泄露
    • 在实现较为完善的数据库连接池时,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

3、使用步骤

3.1、导入jar包

c3p0的jar包

3.2、配置c3p0-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

<!--连接池名字-->
<named-config name="mysql">
<!-- 最基础参数配置 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mall_system?useUnicode=true&amp;characterEncoding=UTF-8&amp;userSSL=true&amp;serverTimezone=Hongkong</property>
<property name="user">root</property>
<property name="password">123456</property>

<!--连接池初始化时创建的连接数(介于maxPoolSize和minPoolSize之间),默认值3-->
<property name="initialPoolSize">10</property>

<!--连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,默认值15-->
<property name="maxPoolSize">20</property>

<!--连接池保持的最小连接数,后面的maxIdleTimeExcessConnections跟这个配合使用来减轻连接池的负载,默认值3-->
<property name="minPoolSize">5</property>

<!--连接池在无空闲连接可用时一次性创建的新数据库连接数,默认值3-->
<property name="acquireIncrement">5</property>

<!--连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接,如果为0,则永远不会断开连接,默认值为0-->
<property name="maxIdleTime">30</property>

<!--连接的最大绝对年龄,单位是秒,0表示绝对年龄无限大,默认值0-->
<property name="maxConnectorAge">30</property>

<!--单位秒,为了减轻连接池的负载,当连接池经过数据访问高峰创建了很多连接,但是后面连接池不需要维护这么多连接,必须小于maxIdleTime,配置不为0,则将连接池的数量保持到minPoolSize,默认值0-->
<property name="maxIdleTimeExcessConnection">5</property>

<!--如果不为null,c3p0将生成指定名称的空表,使用该表来测试连接,默认值null-->
<property name="automaticTestTable">null</property>

<!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。默认值com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName">com.mchange.v2.c3p0.impl.DefaultConnectionTester</property>

<!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意: 测试的表必须在初始数据源的时候就存在,默认值null-->
<property name="preferredTestQuery">null</property>

<!--每个几秒检查所有连接池中的空闲连接,默认值0-->
<property name="idleConnectionTestPeriod">0</property>

<!--如果设为true那么在取得连接的同时将校验连接的有效性,默认值false-->
<property name="testConnectionOnCheckin">false</property>

<!--如果为true,在连接释放的同事将校验连接的有效性,默认值false-->
<property name="testConnectionOnCheckout">false</property>

<!--在这几个参数中,idleConnectionTestPeriod、testConnectionOnCheckout和testConnectuonOnCheckin控制什么时候连接将被校验检测。automaticTestTable、connectionTesterClassName和perferedTestQuery控制连接将怎么样被检测。-->

<!--JDBC的标准参数,用以控制数据源内加载d的PreparedStatements数量,默认值0-->
<property name="maxStatements">0</property>

<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数,默认值0-->
<property name="maxStatementsPerConnection">0</property>

<!--如果大于零,则语句池将延迟物理close()缓存语句直到其父连接未被任何客户端使用,或者在其内部(例如在测试中)由池本身使用,默认值0-->
<property name="statementCacheNumDeferredCloseThreads">0</property>

<!--定义在从数据库获取新连接失败后重复尝试的次数,默认值30-->
<property name="acquireRetryAttempts">30</property>

<!--两次连接间隔时间,单位毫秒,,默认值1000-->
<property name="acquireRetryDelay">1000</property>

<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试 获取连接失败后该数据源将申明已断开并永久关闭,默认值false-->
<property name="breakAfterAcquireFailure">false</property>

<!--连接关闭时默认将所有未提交的操作回滚。如果为true,则未提交设置为待提交而不是回滚,默认值false-->
<property name="autoCommitOnClose">false</property>

<!--官方文档建议这个不要设置为true,默认值false-->
<property name="forceIgnoreUnresolvedTransactions">false</property>

<!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待,单位毫秒,默认值0-->
<property name="checkoutTimeout">0</property>

<!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可-->
<property name="factoryClassLocation">null</property>

<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能通过多线程实现多个操作同时被执行,默认值3-->
<property name="numHelperThreads">3</property>

</named-config>

<!--连接池名字-->
<!-- <named-config name="oracle"> -->
<!-- <property name="driverClass">com.mysql.jdbc.Driver</property>-->
<!-- <property name="jdbcUrl">jdbc:mysql://localhost:3306/mall_system</property>-->
<!-- <property name="user">root</property>-->
<!-- <property name="password">root</property>-->
<!-- <property name="initialPoolSize">50</property>-->
<!-- <property name="maxPoolSize">100</property>-->
<!-- </named-config>-->

</c3p0-config>

3.3、C3P0连接池的JdbcUtil

获取连接池的主要代码:

1
private static ComboPooledDataSource pool = new ComboPooledDataSource("mysql");

JdbcUtil代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
package cn.egret.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtil {

private static ComboPooledDataSource pool;

/**
* 构造方法私有化
*/
private JdbcUtil() {
}


// 初始化池子,这里就已经获得了连接池了
static {
pool = new ComboPooledDataSource("mysql");
}

/**
* 获取数据连接
*
* @return
*/
public static Connection getConnection() {
try {
return pool.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}

/**
* 关闭相关资源
*
* @param con 数据库链接
* @param st 查询接口
* @param rs 结果集
*/
public static void close(Connection con, Statement st, ResultSet rs) {
try {
try {
if (rs != null) {
rs.close();
rs = null;
}
} finally {
try {
if (st != null) {
st.close();
st = null;
}
} finally {
if (con != null) {
con.close();
con = null;
}
}
}

} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

/**
* 查询指定sql的结果集记录数,返回值为int类型
*
* @param sql 查询sql
* @param args sql中带条件字段值
* @return 返回记录数
*/
public static int queryForCount(String sql, Object... args) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
try {
con = JdbcUtil.getConnection();//从池子里面拿连接对象
// con = pool.getConnection();
ps = con.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
count++;
}
return count;
} catch (Exception e) {
e.printStackTrace();
} finally {
close(con, ps, rs);
}
return count;
}

/**
* 查询指定sql,将查询结果集的每条记录封装成指定类型对象,返回类型为List
* <br/>
* 表的字段数据类型必须和类的属性数据类型一致 表的字段名字必须和类的属性名字一致
*
* @param <T>
* @param sql 查询sql
* @param c 指定封装类类型
* @param args sql中带条件字段值
* @return 返回查询结果记录集合List
*/
public static <T> List<T> queryAll(String sql, Class<T> c, Object... args) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<T> list = null;
try {
// con = getConnection();
con = pool.getConnection();
ps = con.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 通过反射获取类的所有属性
Field fields[] = c.getDeclaredFields();
list = new ArrayList<T>();
ResultSetMetaData rsmd = rs.getMetaData();
while (rs.next()) {
// 根据类实例化对象
T o = c.newInstance();
/**
* 对象的属性赋值,需要调用set方法,需要获取set方法对象,需要知道set方法名字
*/
for (int i = 0; i < fields.length; i++) {
// 获取每一个字段
Field field = fields[i];
// 获取字段名字
String fieldName = field.getName();// id--setI d
// 获取字段类型
Class<?> fieldType = field.getType();
// 获取set方法名
String methodName = "set" + (fieldName.charAt(0) + "").toUpperCase() + fieldName.substring(1);
// 获取set方法对象
Method method = c.getMethod(methodName, fieldType);

/**
* 注意点:
* 1.如果查询的结果的字段名和属性名不一样会报错User(id,name,password) select id,name from t_user
* 2.查询结果某一行数据为空的时候会报错
* 3.在oralce:value获取的数据类型和实体列中的数据类型可能匹配不上
* 如果oralce中是数字类型,value获取的数据类型不是int类型
*/
// 判断字段是否在结果集中存在
if (isExists(rsmd, fieldName)) {// fieldName=category
// 根据类的属性名字获取字段的值
Object value = rs.getObject(fieldName);
if (value != null) {
// 调用set方法,对象中的属性赋值
method.invoke(o, value);
}

}

}
// 需要把对象放到List中,然后返回list

list.add(o);
}

} catch (Exception e) {
e.printStackTrace();
} finally {
close(con, ps, rs);
}

return list;
}

/**
* 判断实体类中的属性是否在结果集中存在
*
* @param rsmd 结果集的元信息
* @param fieldName 实体类的属性
* @return
*/
private static boolean isExists(ResultSetMetaData rsmd, String fieldName) {

try {
int count = rsmd.getColumnCount();
for (int i = 0; i < count; i++) {
// 获取每列的字段名字,字段的下标从1开始
String columnName = rsmd.getColumnLabel(i + 1);
if (columnName.equals(fieldName)) {
return true;
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;

}

/**
* 查询指定sql单条记录,返回指定对象类型数据
*
* @param sql 指定sql
* @param c 对象的类型
* @param args
* @return
*/
public static <T> T queryForObject(String sql, Class<T> c, Object... args) {
List<T> list = queryAll(sql, c, args);
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;

}

/**
* <p>
* 对表的增删改功能的统一调用此方法
* </p>
*
* @param sql 需要执行的SQL语句
* @param args 动态参数
* @return 受影响的行数;如果没有修改、删除、新增数据,返回0
*/
public static int update(String sql, Object... args) {
Connection con = null;

PreparedStatement ps = null;
try {
// 获取连接
con = JdbcUtil.getConnection();
// con = pool.getConnection();
// 创建statement对象
ps = con.prepareStatement(sql);
// 设置参数
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}

// 执行SQL语句
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
close(con, ps, null);
}
return 0;
}
}