Top

JAVA JDBC DAY01

  1. JDBC实现对Emp数据的简单查询(Oracle)
  2. JDBC实现对Emp数据的简单查询(MySQL)
  3. 实现DBUtility,提供连接的获取,关闭功能
  4. 使用Apache DBCP连接池重构DBUtility

1 JDBC实现对Emp数据的简单查询(Oracle)

1.1 问题

Oracle数据库中职员emp表的表结构如表-1所示:

表-1职员表emp 信息

职员表 emp中的示例数据,如图-1所示:

图-1

本案例要求使用JDBC连接Oracle数据库,查询emp表的所有员工的ID、姓名、薪资以及入职时间。

1.2 方案

使用JDBC连接Oracle数据库的基本步骤如下:

1.装载驱动程序。

代码如下:

Class.forName("oracle.jdbc.OracleDriver");

2.建立连接。

通过调用DriverManager的getConnection方法,获取Connection类的对象,建立连接。代码如下:

con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");

其中,getConnection方法有三个参数,第一个参数表示连接数据库的字符串格式URL,它包含了要连接数据库信息,例如本案例中连接Oracle数据库的连接字符串格式为:

jdbc:oracle:thin:@<主机名>:<端口号(默认1521)>:<实例名>

第二个参数表示连接数据库的用户名,第三个参数表示连接数据库的密码,可以根据连接数据库的不同,用户名和密码的不同,相应的修改以上所述的三个参数。

3.发送和执行SQL语句。

首先,通过Connection的createStatement()方法获取数据库操作对象Statement。代码如下:

stmt = con.createStatement();

Statement主要用于执行SQL语句并返回它所生成结果。通过调用它的executeQuery方法来执行SQL语句。

rs = stmt.executeQuery("select empno, ename, sal, hiredate from emp");

其中,executeQuery方法有一个参数,表示要执行的SQL语句,该方法的返回值为ResultSet对象。ResultSet表示数据库查询操作的结果集。它具有指向其当前数据行的光标。最初,光标被置于第一行之前,调用其next 方法将光标移动到下一行,该方法在 ResultSet 对象没有下一行时返回 false,因此可以在 while 循环中使用它来迭代结果集。代码如下所示:

while (rs.next()) {
		System.out.println(rs.getInt("empno") + ","
			+ rs.getString("ename") + "," 
			+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
}

从上述代码中看出ResultSet提供了getXXX(String column)方法,例如:getInt(String column)等,获取当前ResultSet对象的当前行中指定列名的值,其中参数column表示数据库表中的列名字。

另外,ResultSet提供了getXXX(int columnIndex)方法,例如:getInt(int columnIndex)等,获取当前ResultSet 对象的当前行中指定列索引的值,其中参数columnIndex表示要获取的列的数据在表中是第几列,注意:列数从1开始。建议使用根据列名去获取列的数据信息。

4.释放资源

数据库的连接资源是有限的,使用完毕后需要及时释放。否则会因资源耗尽使应用程序瘫痪,无法正常工作。调用JDBC对象的close方法即可释放与该对象关联的系统资源。代码如下:

if (rs != null) {
		rs.close();
	}
	if (stmt != null) {
		stmt.close();
	}
	if (con != null) {
		con.close();
	}

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:导入连接Oracle数据库所需的jar包

创建工程,在当前工程下导入连接Oracle数据库对应的驱动程序jar包。

步骤二:新建类EmpDAO及方法findAll

代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
	}
}

步骤三:构建连接数据所需的对象以及相应的异常处理

在findAll方法中,构建连接数据所需的对象以及相应的异常处理,代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
#cold_bold		Connection con = null;
#cold_bold		Statement stmt = null;
#cold_bold		ResultSet rs = null;
#cold_bold
#cold_bold		try {
#cold_bold			Class.forName("oracle.jdbc.OracleDriver");
#cold_bold			
#cold_bold		} catch (ClassNotFoundException e) {
#cold_bold			System.out.println("驱动类无法找到!");
#cold_bold			throw new RuntimeException(e);
#cold_bold		} catch (SQLException e) {
#cold_bold			System.out.println("数据库访问异常!");
#cold_bold			throw new RuntimeException(e);
#cold_bold		} 
	}
}

步骤四:装载驱动程序

代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
#cold_bold			Class.forName("oracle.jdbc.OracleDriver");
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} 
	}
}

步骤五:建立连接。

通过调用DriverManager的getConnection方法,获取Connection类的对象,建立连接。代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
#cold_bold			con = DriverManager.getConnection(
#cold_bold					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} 
	}
}

步骤六:发送和执行SQL语句

首先,通过Connection的createStatement()方法获取数据库操作对象Statement。通过调用Statement对象的executeQuery方法来执行SQL语句。代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
			con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
#cold_bold			stmt = con.createStatement();
#cold_bold			rs = stmt
#cold_bold					.executeQuery("select empno, ename, sal, hiredate from emp");
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} 
	}
}

步骤七:处理查询结果

Statement的executeQuery方法的返回值为ResultSet对象。ResultSet表示数据库查询操作的结果集。它具有指向其当前数据行的光标。最初,光标被置于第一行之前,调用其next 方法将光标移动到下一行,该方法在 ResultSet 对象没有下一行时返回 false,因此可以在 while 循环中使用它来迭代结果集。代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
			con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
#cold_bold			while (rs.next()) {
#cold_bold				System.out.println(rs.getInt("empno") + ","
#cold_bold						+ rs.getString("ename") + "," 
#cold_bold						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
#cold_bold			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} 
	}
}

从上述代码中看出ResultSet提供了getXXX(String column)方法,例如:getInt(String column)等,获取当前ResultSet对象的当前行中指定列名的值,其中参数column表示数据库表中的列名字。

步骤八:释放资源

在finally块中,依次关闭ResultSet对象、Statement对象以及Connection对象。代码如下:

public class EmpDAO {
	public static void main(String[] args) {
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
			con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
#cold_bold		} finally {
#cold_bold			try {
#cold_bold				if (rs != null) {
#cold_bold					rs.close();
#cold_bold				}
#cold_bold				if (stmt != null) {
#cold_bold					stmt.close();
#cold_bold				}
#cold_bold				if (con != null) {
#cold_bold					con.close();
#cold_bold				}
#cold_bold			} catch (SQLException e) {
#cold_bold				System.out.println("关闭连接时发生异常");
#cold_bold			}
#cold_bold		}
	}
}

步骤九:测试

在main方法中,调用findAll方法,代码如下所示:

public class EmpDAO {
	public static void main(String[] args) {
#cold_bold		EmpDAO dao = new EmpDAO();
#cold_bold		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
			con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}
}

运行EmpDAO类,控制台输出结果如下所示:

7369,SMITH,,800.0,1980-12-17
7499,ALLEN,,1600.0,1981-02-20
7521,WARD,,1250.0,1981-02-22
7566,JONES,,2975.0,1981-04-02
7654,MARTIN,,1250.0,1981-09-28
7698,BLAKE,,2850.0,1981-05-01
7782,CLARK,,2450.0,1981-06-09
7788,SCOTT,,3000.0,1987-04-19
7839,KING,,5000.0,1981-11-17
7844,TURNER,,1500.0,1981-09-08
7876,ADAMS,,1100.0,1987-05-23
7900,JAMES,,950.0,1981-12-03
7902,FORD,,3000.0,1981-12-03
7934,MILLER,,1300.0,1982-01-23

从输出结果可以看出,已经查询到emp表的所有员工的ID、姓名、薪资以及入职时间。

1.4 完整代码

本案例的完整代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmpDAO {
	public static void main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("oracle.jdbc.OracleDriver");
			con = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:ORCL", "scott",
 "tiger");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");

			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}
}

2 JDBC实现对Emp数据的简单查询(MySQL)

2.1 问题

MySQL数据库中职员emp表的表结构如表-2所示:

表-2职员表emp 信息

职员表 emp中的示例数据,如图-2所示:

图-2

本案例要求使用JDBC连接MySql数据库,查询emp表的所有员工的ID、姓名、薪资以及入职时间。

2.2 方案

使用JDBC连接MySql数据库的基本步骤和Oracle类似,只有前两个步骤不同。

1.装载驱动程序。

在此,需要使用MySql数据库的驱动类,代码如下:

Class.forName("com.mysql.jdbc.Driver");

2.建立连接。

建立连接时,连接的字符串格式发生变化,代码如下:

con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/tts7", "root", "root");

其中,getConnection方法有三个参数,第一个参数表示连接数据库的字符串格式URL,它包含了要连接数据库信息,例如本案例中连接MySql数据库的连接字符串格式为:

jdbc:mysql://<主机名>:<端口号(默认3306)>/<数据库名>

第二个参数表示连接数据库的用户名,第三个参数表示连接数据库的密码,可以根据连接数据库的不同,用户名和密码的不同,相应的修改以上所述的三个参数。

其余,步骤与连接Oracle数据类似,这里不再赘述。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在MySql数据库中,创建emp表并插入测试数据

SQL语句如下所示:

CREATE TABLE emp(
    empno int(4),
	ename VARCHAR(10),
	job VARCHAR(9),
	mgr int(4),
	hiredate DATE,
	sal double(7,2),
	comm double(7,2),
    deptno double(2,0)
);

INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,'1981-2-20',1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,'1981-2-22',1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,'1981-4-2',2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,'1981-9-21',1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,'1981-5-1',2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,'1981-6-9',2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,'1987-4-19',3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,'1981-9-8',1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,'1987-5-27',1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,'1981-12-1',950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,'1981-12-3',3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,'1982-1-23',1300,NULL,10);

步骤二:导入连接MySql数据库所需的jar包

在当前工程下导入连接MySql数据库对应的驱动程序jar包。

步骤三:重构EmpDAO类

重构EmpDAO类,在该类中使用MySql数据库的驱动程序和连接MySql数据库的连接字符串格式,代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


publicclass EmpDAO {
	publicstaticvoid main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	publicvoid findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
#cold_bold			Class.forName("com.mysql.jdbc.Driver");
#cold_bold			con = DriverManager.getConnection(
#cold_bold					"jdbc:mysql://localhost:3306/tts7", "root", "root");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + rs.getDouble("sal")
						+ "," + rs.getDate("hiredate"));
			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			thrownew RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			thrownew RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}
}

运行EmpDAO类,控制台输出结果如下所示:

7369,SMITH,,800.0,1980-12-17
7499,ALLEN,,1600.0,1981-02-20
7521,WARD,,1250.0,1981-02-22
7566,JONES,,2975.0,1981-04-02
7654,MARTIN,,1250.0,1981-09-28
7698,BLAKE,,2850.0,1981-05-01
7782,CLARK,,2450.0,1981-06-09
7788,SCOTT,,3000.0,1987-04-19
7839,KING,,5000.0,1981-11-17
7844,TURNER,,1500.0,1981-09-08
7876,ADAMS,,1100.0,1987-05-23
7900,JAMES,,950.0,1981-12-03
7902,FORD,,3000.0,1981-12-03
7934,MILLER,,1300.0,1982-01-23

从输出结果可以看出,已经查询到emp表的所有员工的ID、姓名、薪资以及入职时间。

2.4 完整代码

本案例中,类EmpDAO的完整代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


publicclass EmpDAO {
	publicstaticvoid main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	publicvoid findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Class.forName("com.mysql.jdbc.Driver");
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/tts7", "root", "root");
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + rs.getDouble("sal")
						+ "," + rs.getDate("hiredate"));
			}
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类无法找到!");
			thrownew RuntimeException(e);
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			thrownew RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}
}

3 实现DBUtility,提供连接的获取,关闭功能

3.1 问题

将数据库连接的获取和关闭封装到DBUtility类中,并重构案例“JDBC实现对Emp数据的简单查询(Oracle)”,使用DBUtility类获取连接和关闭连接。

3.2 方案

封装数据库连接的获取和关闭的过程如下:

1.创建属性db.properties,在该文件中以键值对的形式来存储连接数据库的相关信息,该文件的内容如下:

jdbc.driver=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:ORCL
jdbc.user=scott
jdbc.password=tiger

这样做的目的是当需要修改连接数据的信息时,只需修改该文件集合,降低了连接数据库的信息与使用类之间的耦合。

2.创建DBUtility类,使用static块,初始化连接数据的信息,代码如下:

	static {
		try {
			// 加载配置文件
			properties.load(DBUtility.class.getClassLoader().getResourceAsStream(
					));
			driver = properties.getProperty("jdbc.driver");
			url = properties.getProperty("jdbc.url");
			user = properties.getProperty("jdbc.user");
			pwd = properties.getProperty("jdbc.password");
			Class.forName(driver);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

其中,"day01/v3/db.properties"表示文件db.properties所在的包路径,可以按照你自己工程的包路径来写代码。

3.创建打开和关闭连接数据库的方法,代码如下:

	public static Connection openConnection() throws SQLException {
		return DriverManager.getConnection(url, user, pwd);
	}
	public static void closeConnection(Connection con) {
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建属性db.properties

创建属性db.properties,在该文件中以键值对的形式来存储连接数据库的相关信息,该文件的内容如下:

jdbc.driver=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:ORCL
jdbc.user=scott
jdbc.password=tiger

步骤二:在类中初始化数据库连接信息

首先,创建DBUtility类;在该类中,使用static块,加载db.properties中的文件内容,来初始化数据库连接数据的信息,代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBUtility {
	private static Properties properties =new Properties() ;
	private static String driver = null;
	private static String url = null;
	private static String user = null;
	private static String pwd = null;

	static {
		try {
			// 加载配置文件
			properties.load(DBUtility.class.getClassLoader().getResourceAsStream(
					"day01/v3/db.properties"));
			driver = properties.getProperty("jdbc.driver");
			url = properties.getProperty("jdbc.url");
			user = properties.getProperty("jdbc.user");
			pwd = properties.getProperty("jdbc.password");
			Class.forName(driver);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
}

步骤三:创建打开和关闭连接数据库的方法

在DBUtility类中,添加openConnection方法和closeConnection,来实现打开数据库连接和关闭数据库连接的功能,代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBUtility {
	private static Properties properties =new Properties() ;
	private static String driver = null;
	private static String url = null;
	private static String user = null;
	private static String pwd = null;

	static {
		try {
			// 加载配置文件
			properties.load(DBUtility.class.getClassLoader().getResourceAsStream(
					"day01/v3/db.properties"));
			driver = properties.getProperty("jdbc.driver");
			url = properties.getProperty("jdbc.url");
			user = properties.getProperty("jdbc.user");
			pwd = properties.getProperty("jdbc.password");
			Class.forName(driver);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
#cold_bold	public static Connection openConnection() throws SQLException {
#cold_bold		return DriverManager.getConnection(url, user, pwd);
#cold_bold	}
#cold_bold	public static void closeConnection(Connection con) {
#cold_bold		if (con != null) {
#cold_bold			try {
#cold_bold				con.close();
#cold_bold			} catch (SQLException e) {
#cold_bold				System.out.println("关闭连接时发生异常");
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤四:重构EmpDAO类,使用DBUtility类获取连接和关闭连接

重构EmpDAO类,在该类中使用DBUtility类的openConnection获取连接、使用closeConnection方法关闭连接,代码如下所示:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmpDAO {
	public static void main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
#cold_bold			con = DBUtility.openConnection();
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				System.out.println("释放资源时发生异常");
			}
#cold_bold			DBUtility.closeConnection(con);
		}
	}
}

运行类EmpDAO,输出结果与案例“JDBC实现对Emp数据的简单查询(Oracle)”是相同的。

3.4 完整代码

本案例中,db.properties文件的内容如下所示:

jdbc.driver=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:ORCL
jdbc.user=scott
jdbc.password=tiger

类DBUtility的完整代码如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBUtility {
	private static Properties properties =new Properties() ;
	private static String driver = null;
	private static String url = null;
	private static String user = null;
	private static String pwd = null;

	static {
		try {
			// 加载配置文件
			properties.load(DBUtility.class.getClassLoader().getResourceAsStream(
					"day01/v3/db.properties"));
			driver = properties.getProperty("jdbc.driver");
			url = properties.getProperty("jdbc.url");
			user = properties.getProperty("jdbc.user");
			pwd = properties.getProperty("jdbc.password");
			Class.forName(driver);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	public static Connection openConnection() throws SQLException {
		return DriverManager.getConnection(url, user, pwd);
	}
	public static void closeConnection(Connection con) {
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				System.out.println("关闭连接时发生异常");
			}
		}
	}
}

EmpDAO类的完整代码如下所示:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmpDAO {
	public static void main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			con = DBUtility.openConnection();
			stmt = con.createStatement();
			rs = stmt
				.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				System.out.println("释放资源时发生异常");
			}
			DBUtility.closeConnection(con);
		}
	}
}

4 使用Apache DBCP连接池重构DBUtility

4.1 问题

本案例要求使用Apache DBCP连接池重构类DBUtility为ConnectionSource类,并重构案例“实现DBUtility,提供连接的获取,关闭功能”中的EmpDAO类,在该类中使用ConnectionSource来获取连接。

4.2 方案

直接使用JDBC访问数据库时,需要避免以下隐患:

1.每一次数据操作请求都需要建立数据库连接、打开连接、存取数据和关闭连接等步骤。而建立和打开数据库连接是一件既耗资源又费时的过程,如果频繁发生这种数据库操作,势必会使系统性能下降。

2. 连接对象代表着数据库系统的连接进程,是有限的资源。如果系统的使用用户非常多,有可能超出数据库服务器的承受极限,造成系统的崩溃。

数据库连接池是解决上述问题最常用的方法。所谓连接池,即可以创建并持有数据库连接的组件。连接池可以预先创建并封装一些连接对象并将其缓存起来,当需要使用连接对象时可以向连接池“借”一个连接,用完之后将其“归还”到连接池中。数据库连接池的主要功能如下:

1. 连接池对象的创建和释放。

2. 服务器启动时,创建指定数量的数据库连接。

3. 为用户请求提供可用连接。如果没有空闲连接,且连接数没有超出最大值,创建一个新的数据库连接。

4. 将用户不再使用的连接标识为可用连接,等待其他用户请求。

5. 当空闲的连接数过多时,释放连接对象。

连接池组件一般都需要实现JDBC规范中的javax.sql.DataSource接口。DataSource接口定义了获取连接的方法getConnection方法。常用的连接池组件有DBCP、c3p0和proxool等,本案例以Apache的DBCP组件为例来实现数据库连接池。简单的应用代码如下所示:

	 BasicDataSource ds = new BasicDataSource();
	ds.setUrl("jdbc:oracle:thin:@192.168.0.23:1521:tarena");
	ds.setDriverClassName("oracle.jdbc.OracleDriver");
	ds.setUsername("openlab");
	ds.setPassword("open123");
	Connection con = ds.getConnection();
	
	Statement stmt = con.createStatement();
	ResultSet rs = stmt.executeQuery("select count(*) from emp");
	if (rs.next()) {
		System.out.println(rs.getInt(1));
	}
	stmt.close(); 
	con.close();

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:导入使用DBCP组件所需的jar包

在当前工程下,导入使用DBCP组件所需的jar包,包括commons-dbcp.jar以及commons-pool.jar两个jar包,这两个jar包的名字可能会因为版本的不同,名字的最后为版本信息,例如:commons-dbcp-1.2.1.jar。

步骤二:重构db.properties文件

重构db.properties文件,在该文件中添加创建数据库连接池所需的信息,包括初始化连接数、最大空闲连接数、大小空闲连接数、最大连接数量以及超时回收时间。该文件内容如下所示:

jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
jdbc.username=scott
jdbc.password=tiger
#cold_bold#<!-- 初始化连接 -->
#cold_bolddataSource.initialSize=10
#cold_bold#<!-- 最大空闲连接 -->
#cold_bolddataSource.maxIdle=20
#cold_bold#<!-- 最小空闲连接 -->
#cold_bolddataSource.minIdle=5
#cold_bold#最大连接数量
#cold_bolddataSource.maxActive=50
#cold_bold#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
#cold_bolddataSource.maxWait=1000

步骤三:创建ConnectionSource类,在该类中初始化数据源信息

首先创建ConnectionSource类;然后在该类中添加init方法,在该方法中数据源信息进行初始,代码如下所示:

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;

public class ConnectionSource {
	private static BasicDataSource dataSource = null;

	public ConnectionSource() {
	}
	public static void init() {

		Properties dbProps = new Properties();
		// 取配置文件可以根据实际的不同修改
		try {
			dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream(
					"day01/v4/db.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			String driveClassName = dbProps.getProperty("jdbc.driverClassName");
			String url = dbProps.getProperty("jdbc.url");
			String username = dbProps.getProperty("jdbc.username");
			String password = dbProps.getProperty("jdbc.password");

			String initialSize = dbProps.getProperty("dataSource.initialSize");
			String minIdle = dbProps.getProperty("dataSource.minIdle");
			String maxIdle = dbProps.getProperty("dataSource.maxIdle");
			String maxWait = dbProps.getProperty("dataSource.maxWait");
			String maxActive = dbProps.getProperty("dataSource.maxActive");

			dataSource = new BasicDataSource();
			dataSource.setDriverClassName(driveClassName);
			dataSource.setUrl(url);
			dataSource.setUsername(username);
			dataSource.setPassword(password);

			// 初始化连接数
			if (initialSize != null)
				dataSource.setInitialSize(Integer.parseInt(initialSize));

			// 最小空闲连接
			if (minIdle != null)
				dataSource.setMinIdle(Integer.parseInt(minIdle));

			// 最大空闲连接
			if (maxIdle != null)
				dataSource.setMaxIdle(Integer.parseInt(maxIdle));

			// 超时回收时间(以毫秒为单位)
			if (maxWait != null)
				dataSource.setMaxWait(Long.parseLong(maxWait));

			// 最大连接数
			if (maxActive != null) {
				if (!maxActive.trim().equals("0"))
					dataSource.setMaxActive(Integer.parseInt(maxActive));
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("创建连接池失败!请检查设置!!!");
		}
	}
}

步骤四:添加获取连接的方法

在ConnectionSource类中添加获取连接的方法getConnection,代码如下所示:

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;

public class ConnectionSource {
	private static BasicDataSource dataSource = null;

	public ConnectionSource() {
	}
	public static void init() {

		Properties dbProps = new Properties();
		// 取配置文件可以根据实际的不同修改
		try {
			dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream(
					"day01/v4/db.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			String driveClassName = dbProps.getProperty("jdbc.driverClassName");
			String url = dbProps.getProperty("jdbc.url");
			String username = dbProps.getProperty("jdbc.username");
			String password = dbProps.getProperty("jdbc.password");

			String initialSize = dbProps.getProperty("dataSource.initialSize");
			String minIdle = dbProps.getProperty("dataSource.minIdle");
			String maxIdle = dbProps.getProperty("dataSource.maxIdle");
			String maxWait = dbProps.getProperty("dataSource.maxWait");
			String maxActive = dbProps.getProperty("dataSource.maxActive");

			dataSource = new BasicDataSource();
			dataSource.setDriverClassName(driveClassName);
			dataSource.setUrl(url);
			dataSource.setUsername(username);
			dataSource.setPassword(password);

			// 初始化连接数
			if (initialSize != null)
				dataSource.setInitialSize(Integer.parseInt(initialSize));

			// 最小空闲连接
			if (minIdle != null)
				dataSource.setMinIdle(Integer.parseInt(minIdle));

			// 最大空闲连接
			if (maxIdle != null)
				dataSource.setMaxIdle(Integer.parseInt(maxIdle));

			// 超时回收时间(以毫秒为单位)
			if (maxWait != null)
				dataSource.setMaxWait(Long.parseLong(maxWait));

			// 最大连接数
			if (maxActive != null) {
				if (!maxActive.trim().equals("0"))
					dataSource.setMaxActive(Integer.parseInt(maxActive));
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("创建连接池失败!请检查设置!!!");
		}
	}

#cold_bold	public static synchronized Connection getConnection() throws SQLException {
#cold_bold		if (dataSource == null) {
#cold_bold			init();
#cold_bold		}
#cold_bold		Connection conn = null;
#cold_bold		if (dataSource != null) {
#cold_bold			conn = dataSource.getConnection();
#cold_bold		}
#cold_bold		return conn;
#cold_bold	}
}

getConnection方法使用synchronized修饰,来保证线程安全。

步骤四:重构EmpDAO类

重构EmpDAO类,在该类中使用ConnectionSource类的getConnection方法获取连接,代码如下所示:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmpDAO {
	public static void main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
#cold_bold			con = ConnectionSource.getConnection();
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
#cold_bold				if (con != null) {
#cold_bold					con.close();
#cold_bold				}
			} catch (SQLException e) {
				System.out.println("释放资源时发生异常");
			}
		}
	}
}

在此,调用Connection类的close方法关闭连接,会将该连接归还到连接池中。

运行类EmpDAO,输出结果与案例“JDBC实现对Emp数据的简单查询(Oracle)”是相同的。

4.4 完整代码

本案例中,db.properties文件的的完整内容如下所示:

jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
jdbc.username=scott
jdbc.password=tiger

#<!-- 初始化连接 -->
dataSource.initialSize=10
#<!-- 最大空闲连接 -->
dataSource.maxIdle=20
#<!-- 最小空闲连接 -->
dataSource.minIdle=5
#最大连接数量 
dataSource.maxActive=50
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
dataSource.maxWait=1000

ConnectionSource类的完整代码如下所示:

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;

public class ConnectionSource {
	private static BasicDataSource dataSource = null;

	public ConnectionSource() {
	}
	public static void init() {

		Properties dbProps = new Properties();
		// 取配置文件可以根据实际的不同修改
		try {
			dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream(
					"day01/v4/db.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			String driveClassName = dbProps.getProperty("jdbc.driverClassName");
			String url = dbProps.getProperty("jdbc.url");
			String username = dbProps.getProperty("jdbc.username");
			String password = dbProps.getProperty("jdbc.password");

			String initialSize = dbProps.getProperty("dataSource.initialSize");
			String minIdle = dbProps.getProperty("dataSource.minIdle");
			String maxIdle = dbProps.getProperty("dataSource.maxIdle");
			String maxWait = dbProps.getProperty("dataSource.maxWait");
			String maxActive = dbProps.getProperty("dataSource.maxActive");

			dataSource = new BasicDataSource();
			dataSource.setDriverClassName(driveClassName);
			dataSource.setUrl(url);
			dataSource.setUsername(username);
			dataSource.setPassword(password);

			// 初始化连接数
			if (initialSize != null)
				dataSource.setInitialSize(Integer.parseInt(initialSize));

			// 最小空闲连接
			if (minIdle != null)
				dataSource.setMinIdle(Integer.parseInt(minIdle));

			// 最大空闲连接
			if (maxIdle != null)
				dataSource.setMaxIdle(Integer.parseInt(maxIdle));

			// 超时回收时间(以毫秒为单位)
			if (maxWait != null)
				dataSource.setMaxWait(Long.parseLong(maxWait));

			// 最大连接数
			if (maxActive != null) {
				if (!maxActive.trim().equals("0"))
					dataSource.setMaxActive(Integer.parseInt(maxActive));
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("创建连接池失败!请检查设置!!!");
		}
	}

	public static synchronized Connection getConnection() throws SQLException {
		if (dataSource == null) {
			init();
		}
		Connection conn = null;
		if (dataSource != null) {
			conn = dataSource.getConnection();
		}
		return conn;
	}
}

类EmpDAO的完整代码如下所示:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmpDAO {
	public static void main(String[] args) {
		EmpDAO dao = new EmpDAO();
		dao.findAll();
	}

	public void findAll() {
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			con = ConnectionSource.getConnection();
			stmt = con.createStatement();
			rs = stmt
					.executeQuery("select empno, ename, sal, hiredate from emp");
			while (rs.next()) {
				System.out.println(rs.getInt("empno") + ","
						+ rs.getString("ename") + "," + ","
						+ rs.getDouble("sal") + "," + rs.getDate("hiredate"));
			}
		} catch (SQLException e) {
			System.out.println("数据库访问异常!");
			throw new RuntimeException(e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				System.out.println("释放资源时发生异常");
			}
		}
	}
}