JDBC 和连接池
1 JDBC概述
Java DataBase Connectivity,Java数据库连接,一种执行SQL的Java API,为多种关系数据库提供统一访问规范,由一组Java语言编写的类和接口组成。
数据库驱动:各个数据库生产商提供的JDBC实现类。使用统一的JDBC规范,不用专门去了解各个数据库的驱动API。
JDBC 可做三件事:与数据库建立连接、发送SQL、处理结果。
2 JDBC常用方法及增删改查
2.1 JDBC的开发步骤
先导入对应数据库的jar包 mysql-connector-java-5.0.8-bin.jar
创建lib目录,用于存放当前项目需要的所有jar包
选择jar包,右键执行build path / Add to Build Path

- 加载驱动
- 获得连接
- 获得语句执行平台statement
- 执行sql
- 处理结果
- 释放资源
import org.junit.Test;
public class JDBCDemo1 {
@Test
public void demo1() throws Exception{
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获得连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "abc"); //url如果连接的是本机的路径,可以简化为jdbc:mysql:///web_test3 (3个///)
// 3.基本操作:执行SQL
// 3.1获得执行SQL语句的对象
Statement statement = conn.createStatement();
// 3.2编写SQL语句:
String sql = "select * from user";
// 3.3执行SQL:
ResultSet rs = statement.executeQuery(sql);
// 3.4遍历结果集:
while(rs.next()){
System.out.print(rs.getInt("id")+" ");
System.out.print(rs.getString("username")+" ");
System.out.print(rs.getString("password")+" ");
System.out.print(rs.getString("nickname")+" ");
System.out.print(rs.getInt("age"));
System.out.println();
}
// 4.释放资源
rs.close();
statement.close();
conn.close();
}
}
2 JDBC常用类/接口:
DriverManager类:注册JDBC驱动,获取Connection对象(数据库链接对象,每个Connection代表一个物理连接会话)。
Class.forName("com.mysql.jdbc.Driver");
Connection接口:获取执行SQL的Statement对象,并包含多个用于控制事务的方法。
Statement createStatement() throws SQLException; //返回一个Statement对象,用来将SQL语句发送到数据库; PreparedStatement prepareStatement(String sql)throws SQLException; //返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译; CallableStatement prepareCall(String sql) throws SQLException; //返回CallableStatement对象,用于调用存储过程。 //以上都返回用于执行sql语句的Statement对象,PreparedStatement和CallableStatement是Statement的子类,只有获得了Statement之后才可以执行sql语句; void setAutoCommit(boolean autoCommit) throws SQLException; //关闭自动提交,打开事务; void rollback() throws SQLException; //回滚事务,并释放Connection对象当前持有的数据库锁; void commit() throws SQLException; //提交事务,并释放Connection对象当前持有的数据库锁; void setTransactionIsolation(int level) throws SQLException; //设置事务的隔离级别; Savepoint setSavepoint() throws SQLException; //创建一个保存点; Savepoint setSavepoint(String name) throws SQLException; //以指定名字来创建一个保存点; void rollback(Savepoint savepoint) throws SQLException; //将事务回滚到指定的保存点;
Statement接口:用于执行静态SQL语句并返回它所生成结果的对象。
ResultSet executeQuery(String sql) throws SQLException;//执行查询语句,返回查询结果对应ResultSet对象。该方法只能用于执行查询语句。 int executeUpdate(String sql) throws SQLException;//执行inset/update/delete语句,返回受影响的行数;也可以执行create/alter/drop/truncate语句,并返回0; boolean execute(String sql) throws SQLException;//执行任意sql语句。若执行后第一个结果为ResultSet对象,则返回true; //若执行后第一个结果为受影响的行数或无结果,则返回false; void addBatch(String sql);//将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中。 void clearBatch();//清空此Statement对象当前的SQL命令列表。 int[] executeBatch();//将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
注意:如果 addBatch() -> executeBatch() 很慢,需要启动批处理操作rewriteBatchedStatements=true,即在数据库连接URL后面加上这个参数:String dbUrl = "jdbc:mysql://localhost:3306/User? rewriteBatchedStatements=true";该参数将多条sql分成若干个报文发送到mysql服务器,并在最后加上commit操作。
ResultSet接口:结果集对象,包含查询结果,可以通过列索引或列名获得列数据。
boolean next();//将光标从当前位置向前移动一行
int getInt(int columnIndex);//获取结果集当前行指定列
int getInt(String columnLabel);//获取结果集当前行指定列
long getLong(int columnIndex);//获取结果集当前行指定列
long getLong(String columnLabel);//获取结果集当前行指定列
String getString(int columnIndex);//获取结果集当前行指定列
String getString(String columnLabel);//获取结果集当前行指定列
//示例代码:
while(resultSet.next()){
System.out.println(resultSet.getInt("id"))
System.out.println(resultSet.getString("ename"))
System.out.println(resultSet.getString("nickname"))
}
3 JDBC的资源释放
JDBC程序执行结束后,需要将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection。尤其是Connection对象,一定要做到晚创建,早释放。
如果把所有关闭语句写在同一个try块里面,一旦前面的关闭语句抛异常,后面的关闭语句就无法执行,所以要给每个关闭语句一个try块。
最后为了保证资源能够释放,还有在每个关闭语句的后面加一个finally,在finally里给要关闭的资源赋值为空。这样即使关闭过程中抛异常不能及时关闭,但是由于赋值为空,没有引用该资源,在垃圾回收的时候也能够回收。
if(rs !=null){ try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}
if(statement !=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
statement = null;
}
}
if(conn !=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
2.2 JDBC的增删改查
public void demo1(){
Connection conn = null;
Statement stmt = null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
stmt = conn.createStatement();
//增加一条数据
String sql = "insert into user values (null,'eee','123','阿黄',21)";
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("保存用户成功!!!");
}
/*
//删除、修改类似
//查询一条或多条多条
String sql = "select * from user";
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
*/
}catch(Exception e){
e.printStackTrace();
}finally{
// 资源释放:略
}
}
3 JDBC的工具类的抽取
public class JDBCUtils {
private static final String driverClassName;
private static final String url;
private static final String username;
private static final String password;
// 获取属性文件中的内容:
static{
driverClassName="com.mysql.jdbc.Driver";
url="jdbc:mysql:///web_test3";
username="root";
password="abc";
}
/*
static{
// 获取属性文件中的内容:
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src/db.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
driverClassName=properties.getProperty("driverClassName");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
}
*/
/**
* 注册驱动的方法
*/
public static void loadDriver(){
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获得连接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try{
// 将驱动一并注册:
loadDriver();
// 获得连接
conn = DriverManager.getConnection(url,username, password);
}catch(Exception e){
e.printStackTrace();
}
return conn;
}
/**
* 释放资源的方法
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
// 资源释放:
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
测试工具类:
public void demo1(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
conn = JDBCUtils.getConnection();
stmt = conn.createStatement();
String sql = "select * from user";
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
}
4 用 properties 文件配置JDBC信息
1 开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
使用properties文件要求:
1.文件位置:任意,建议src下
2.文件内容:一行一组数据,格式是“key=value”
a)key命名自定义,如果是多个单词,习惯使用点分隔。例如:jdbc.driver
b)value值不支持中文,如果需要使用非英文字符,将进行unicode转换
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb user=root password=root
2 加载properties配置文件
public class JDBCUtils {
private static String driver;
private static String url;
private static String user;
private static String password;
// 静态代码块
static {
try {
// 1 使用Properties处理流
// 使用load()方法加载指定的流
Properties props = new Properties();
Reader is = new FileReader("db.properties");
props.load(is);
// 2 使用getProperty(key),通过key获得需要的值,
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获得连接
*/
public static Connection getConnection() {
try {
// 1 注册驱动
Class.forName(driver);
// 2 获得连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
5 SQL 注入
输入一个存在的用户名 userName,再输入任意密码:XXX' OR 'a' = 'a'时,
SELECT * FROM Table WHERE Name = 'userName' AND PassWord = 'XXX' OR 'a' = 'a';
此时绕过了密码校验成功登录,这便是SQL注入问题。
为此,可以使用PreparedStatement将SQL预先进行编译,使用?作为占位符。当传入变量(包含SQL的关键字)时,不会识别 or 这些关键字。
public class UserDao {
public boolean login(String username,String password){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// 定义一个变量:
boolean flag = false;
try{
conn = JDBCUtils.getConnection();
String sql = "select * from user where username = ? and password = ?";
// 预编译SQL
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, username);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
if(rs.next()){
flag = true;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
return flag;
}
6 JDBC批处理
Statement接口:void addBatch(String sql)
PreparedStatement接口重写了addBatch()方法:void addBatch()
public void demo1(){
Connection conn = null;
Statement stmt = null;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 创建执行批处理对象:
stmt = conn.createStatement();
// 编写一批SQL语句:
String sql1 = "create database test1";
String sql2 = "use test1";
String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql4 = "insert into user values (null,'aaa')";
String sql5 = "insert into user values (null,'bbb')";
String sql6 = "insert into user values (null,'ccc')";
String sql7 = "update user set name = 'mmm' where id = 2";
String sql8 = "delete from user where id = 1";
// 添加到批处理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
stmt.addBatch(sql7);
stmt.addBatch(sql8);
// 执行批处理:
stmt.executeBatch();
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(stmt, conn);
}
}
//批量插入(使用PreparedStatement)
@Test
/**
* 批量插入记录:
* * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可。
*/
public void demo2(){
// 记录开始时间:
long begin = System.currentTimeMillis();
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
String sql = "insert into user values (null,?)";
pstmt = conn.prepareStatement(sql);
for(int i=1;i<=10000;i++){
pstmt.setString(1, "name"+i);
pstmt.addBatch();
if(i % 1000 == 0){
pstmt.executeBatch();
pstmt.clearBatch();
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
long end = System.currentTimeMillis();
System.out.println((end-begin));
}
7 JDBC事务管理
例如在转账操作中,如果没有添加事务管理,可能出现 a b 账户互相转账后,两账户总额跟操作前不相等的情况,此时需要给转账功能添加事务管理。
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
// 1 开启事务
conn.setAutoCommit(false);
String sql = "update account set money = money + ? where name = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setDouble(1, -1000);
pstmt.setString(2, "aaa");
pstmt.executeUpdate();
int i = 1 / 0;
pstmt.setDouble(1, 1000);
pstmt.setString(2, "bbb");
pstmt.executeUpdate();
// 2 提交事务:
conn.commit();
}catch(Exception e){
// 3 回滚事务:
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
}
8 DBUtils简介
为简化JDBC开发,可以使用DBUtils,DBUtils封装了对JDBC的操作,是JDBC的简化开发工具包。使用需要导入commons-dbutils-1.6.jar。
Dbutils三个核心功能
QueryRunner 提供对sql语句操作的API,用来执行SQL语句的对象
ResultSetHandler接口 定义select后怎样封装结果集.
DbUtils类 工具类,定义了关闭资源与事务处理的方法
1 QueryRunner
无事务管理:
int update(String sql,Object… args);:增、删、改
T query(String sql,ResultSetHandler rsh,Object… args);:查询
有事务管理:
int update(Connection conn, String sql, Object... params) :增、删、改
T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) :查询
query(Connection con,String sql,ResultSetHandler r, Object..params)
ResultSetHandler r 结果集的处理方式,传递ResultSetHandler接口实现类
Object..params SQL语句中的?占位符
注意: query方法返回值,返回的是T 泛型, 具体返回值类型,跟随结果集处理方式变化
//增加
public void demo1() throws SQLException{
// 创建核心类:QueryRunner:
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
}
//删除
public void demo3() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("delete from account where id = ?", 3);
}
//修改
public void demo2() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("update account set name=?,money=? where id =?", "eee",20000,4);
}
//增加
public void insert(){
try {
//获取一个QueryRunner对象,用来执行SQL语句
QueryRunner queryRunner = new QueryRunner();
String sql = "INSERT INTO zhangwu(name,money,parent) VALUES(?,?,?)";
Object[] params = {"股票收入", 5500, "收入"};
Connection conn = JDBCUtils.getConnection();
int line = queryRunner.update(conn,sql,params);// 用来完成表数据的增加、删除、更新操作
//结果集处理
System.out.println("line = " + line);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//删除
public void delete(){
try {
QueryRunner queryRunner = new QueryRunner();
String sql = "DELETE FROM zhangwu WHERE name = ?";
Object[] params = {"股票收入"};
Connection conn = JDBCUtils.getConnection();
int line = queryRunner.update(conn, sql, params);
System.out.println("line="+line);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//修改
public void update(){
try {
QueryRunner queryRunner = new QueryRunner();
String sql = "UPDATE zhangwu SET money = money+1000 WHERE name=?";
Object[] params = {"股票收入"};
Connection conn = JDBCUtils.getConnection();
int line = queryRunner.update(conn, sql, params);
System.out.println("line="+line);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
QueryRunner的两个构造方法
第一种:不带connection参数
QueryRunner queryRunner = new QueryRunner();
这种情况下,调用update或query方法时,需要传入对应的connection参数
queryRunner.update(conn, sql,params);
conn.close();
DBUtils调用这种带connection参数的方法时,
只会关闭preparedstatement和resultset对象,不会关闭conneciton对象,
一些情况下,没有手动关闭,可能会导致连接池满了,访问数据库是处于一直等待的状态。
就是为了其他方法来调用这个conneciton,所以这种连接数据库的方法适合操作事务。
第二种:带connection参数
QueryRunner queryRunner = new QueryRunner(dataSource);
将dataSource传递进去,这样update或query方法内部就调用this.getconnection方法来从这个数据源获得连接,
queryRunner.update( sql,params);
操作完后,就关闭conneciton,preparedstatement和resultset对象.
事务是自动控制的,一条SQL语句一个事务,不需要人为的控制。
2 ResultSetHandler
ArrayHandler 将结果集的第一条记录封装到Object[]数组中,数组中的每一个元素就是这条记录中每一个字段的值
ArrayListHandler 将结果集的每一条记录都封装到Object[]数组中,数组封装到List集合中。
BeanHandler 将结果集第一条记录封装到javaBean中。
BeanListHandler 将结果集每一条记录封装到javaBean中,javaBean封装到List集合中
ColumnListHandler 将结果集指定的列的字段值,封装到一个List集合中
ScalarHandler 用于单数据。例如select count(*) from 表操作。
MapHandler 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据
MapListHandler 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合
1)JavaBean
JavaBean就是一个类,在开发中常用封装数据。具有如下特性
1.需要实现接口:java.io.Serializable ,通常实现接口这步骤省略了,不会影响程序。
2.提供私有字段:private 类型 字段名;
3.提供getter/setter方法:
4.提供无参构造
示例:
public class ZhangWu {
private int id;
private String name;
private double money;
private String parent;
public ZhangWu() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
@Override
public String toString() { //该方法可以省略
return "ZhangWu [id=" + id + ", name=" + name + ", money=" + money + ", parent=" + parent + "]";
}
}
3 ResultSetHandler使用案例
public class ArrayHandlerDemo {
public void method(){
try {
//获取QueryRunner对象
QueryRunner qr = new QueryRunner();
//执行SQL语句
String sql = "SELECT * FROM zhangwu";
Object[] params = {};
Connection conn = JDBCUtils.getConnection();
Object[] objArray = qr.query(conn, sql, new ArrayHandler(), params);
//结果集的处理
System.out.println( Arrays.toString(objArray) );
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//ArrayListHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<Object[]> list = qr.query(conn, sql, new ArrayListHandler(), params);
for (Object[] objArray : list) {
System.out.println( Arrays.toString(objArray) );
}
//BeanHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE id=?";
Object[] params = {1};
Connection conn = JDBCUtils.getConnection();
ZhangWu zw = qr.query(conn, sql, new BeanHandler<ZhangWu>(ZhangWu.class), params);
System.out.println(zw);
//BeanListHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<ZhangWu> list = qr.query(conn, sql, new BeanListHandler<ZhangWu>(ZhangWu.class), params);
for (ZhangWu zw : list) {
System.out.println(zw);
}
//ColumnListHandlerDemo
String sql = "SELECT name FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<String> list = qr.query(conn, sql, new ColumnListHandler<String>(), params);
for (String str : list) {
System.out.println(str);
}
//ScalarHandlerDemo
String sql = "SELECT MAX(money) FROM zhangwu";
Object[] params = {};
Connection conn = JDBCUtils.getConnection();
Double max = qr.query(conn, sql, new ScalarHandler<Double>(), params);
System.out.println("max=" + max);
/*
* 结果集第一种处理方法, ArrayHandler
* 将结果集的第一行存储到对象数组中 Object[]
*/
public static void arrayHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query执行查询,传递连接对象,SQL语句,结果集处理方式的实现类
//返回对象数组
Object[] result = qr.query(con, sql, new ArrayHandler());
for(Object obj : result){
System.out.print(obj);
}
}
/*
* 结果集第二种处理方法,ArrayListHandler
* 将结果集的每一行,封装到对象数组中, 出现很多对象数组
* 对象数组存储到List集合
*/
public static void arrayListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用query方法,结果集处理的参数上,传递实现类ArrayListHandler
//方法返回值 每行是一个对象数组,存储到List
List<Object[]> result= qr.query(con, sql, new ArrayListHandler());
//集合的遍历
for( Object[] objs : result){
//遍历对象数组
for(Object obj : objs){
System.out.print(obj+" ");
}
System.out.println();
}
}
/*
* 结果集第三种处理方法,BeanHandler
* 将结果集的第一行数据,封装成JavaBean对象
* 注意: 被封装成数据到JavaBean对象, Sort类必须有空参数构造
*/
public static void beanHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法,传递结果集实现类BeanHandler
//BeanHandler(Class<T> type)
Sort s = qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
System.out.println(s);
}
/*
* 结果集第四种处理方法, BeanListHandler
* 结果集每一行数据,封装JavaBean对象
* 多个JavaBean对象,存储到List集合
*/
public static void beanListHander()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法query,传递结果集处理实现类BeanListHandler
List<Sort> list = qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
for(Sort s : list){
System.out.println(s);
}
}
/*
* 结果集第五种处理方法,ColumnListHandler
* 结果集,指定列的数据,存储到List集合
* List<Object> 每个列数据类型不同
*/
public static void columnListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法 query,传递结果集实现类ColumnListHandler
//实现类构造方法中,使用字符串的列名
List<Object> list = qr.query(con, sql, new ColumnListHandler<Object>("sname"));
for(Object obj : list){
System.out.println(obj);
}
}
/*
* 结果集第六种处理方法,ScalarHandler
* 对于查询后,只有1个结果
*/
public static void scalarHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT COUNT(*) FROM sort";
//调用方法query,传递结果集处理实现类ScalarHandler
long count = qr.query(con, sql, new ScalarHandler<Long>());
System.out.println(count);
}
/*
* 结果集第七种处理方法,MapHandler
* 将结果集第一行数据,封装到Map集合中
* Map<键,值> 键:列名 值:这列的数据
*/
public static void mapHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query,传递结果集实现类MapHandler
//返回值: Map集合,Map接口实现类, 泛型
Map<String,Object> map = qr.query(con, sql, new MapHandler());
//遍历Map集合
for(String key : map.keySet()){
System.out.println(key+".."+map.get(key));
}
}
/*
* 结果集第八种处理方法,MapListHandler
* 将结果集每一行存储到Map集合,键:列名,值:数据
* Map集合过多,存储到List集合
*/
public static void mapListHandler()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort";
//调用方法query,传递结果集实现类MapListHandler
//返回值List集合, 存储的是Map集合
List<Map<String,Object>> list = qr.query(con, sql, new MapListHandler());
//遍历集合list
for( Map<String,Object> map : list ){
for(String key : map.keySet()){
System.out.print(key+"..."+map.get(key));
}
System.out.println();
}
}
9 连接池
9.1 DBCP连接池
Java提供了数据库连接池公共接口:javax.sql.DataSource,各厂商需要让自己的连接池实现这个接口,这样程序可以方便地切换不同厂商的连接池。
DBCP是tomcat内置开源连接池,提供了DataSource接口的实现类:BasicDataSource类
需要的jar包:
mysql-connector-java-5.1.37-bin.jar:数据库驱动
commons-dbutils-1.6.jar:提供QueryRunner类方便进行增删改查操作
commons-dbcp-1.4.jar:
commons-pool-1.5.6.jar:提供高效的数据库连接池技术
BasicDataSource类的使用:
//使用DBCP实现数据库的连接池
public class JDBCUtils{
//创建出BasicDataSource类对象
private static BasicDataSource datasource = new BasicDataSource();
static{
datasource.setDriverClassName("com.mysql.jdbc.Driver");
datasource.setUrl("jdbc:mysql://localhost:3306/day33_user");
datasource.setUsername("root");
datasource.setPassword("123");
datasource.setInitialSize(10);//初始化的连接数
datasource.setMaxActive(8);//最大连接数量
datasource.setMaxIdle(5);//最大空闲数
datasource.setMinIdle(1);//最小空闲
}
//定义静态方法,返回BasicDataSource类的对象
public static DataSource getDataSource(){
return datasource;
}
}
常见配置
必须项
driverClassName 数据库驱动名称
url 数据库的地址
username 用户名
password 密码
基本项(扩展)
maxActive 最大连接数量
minIdle 最小空闲连接
maxIdle 最大空闲连接
initialSize 初始化连接
测试连接池:
/*
* 测试写好的工具类,
* 提供的是一个DataSource接口的数据源
* QueryRunner类构造方法,接收DataSource接口的实现类
* 后面,调用方法update,query,无需传递他们Connection连接对象
*/
public class QueryRunnerDemo{
public static void main(String[] args) {
select();
}
//定义2个方法,实现数据表的添加,数据表查询
//QueryRunner类对象,写在类成员位置
private static QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
//数据表查询
public static void select(){
String sql = "SELECT * FROM sort";
try{
List<Object[]> list = qr.query(sql, new ArrayListHandler());
for(Object[] objs : list){
for(Object obj : objs){
System.out.print(obj+"\t");
}
System.out.println();
}
}catch(SQLException ex){
throw new RuntimeException("数据查询失败");
}
}
}
9.2 C3P0连接池
C3P0是一个开源JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
C3P0与DBCP区别:DBCP没有自动回收空闲连接的功能,C3P0有
C3P0连接池的使用:
/**
* 1 手动设置参数的方式:
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 获得连接:从连接池中获取:
// 创建连接池:
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置连接参数:
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///web_test4");
dataSource.setUser("root");
dataSource.setPassword("abc");
conn = dataSource.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
/**
* 2 采用配置文件的方式:
*/
public void demo2(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 获得连接:从连接池中获取:
// 创建连接池://创建连接池默认去类路径下查找c3p0-config.xml
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 从连接池中获得连接:
conn = dataSource.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- c3p0-config.xml -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web_test4</property>
<property name="user">root</property>
<property name="password">abc</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
C3P0详细配置:
<?xml version="1.0" encoding="gbk"?>
<c3p0-config>
<!--默认配置,如果获取连接池时没有指定名称,则使用默认配置信息-->
<default-config>
<!--连接url-->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
<!--数据库驱动类-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!--用户名。Default: null-->
<property name="user">root</property>
<!--密码。Default: null-->
<property name="password"></property>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement">3</property>
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts">30</property>
<!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
<property name="acquireRetryDelay">1000</property>
<!--连接关闭时默认将所有未提交的操作回滚。Default: false -->
<property name="autoCommitOnClose">false</property>
<!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么
属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试
使用。Default: null-->
<property name="automaticTestTable">Test</property>
<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出
SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>
<!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。
Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>
<!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可
Default: null-->
<property name="factoryClassLocation">null</property>
<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文档原文)作者强烈建议不使用的一个属性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>
<!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>
<!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize">3</property>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">60</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">15</property>
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection"></property>
<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads">3</property>
<!--当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0
的数据源时。Default: null-->
<property name="overrideDefaultUser">root</property>
<!--与overrideDefaultUser参数对应使用的一个参数。Default: null-->
<property name="overrideDefaultPassword">password</property>
<!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:
测试的表必须在初始数据源的时候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>
<!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>
<!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
等方法来提升连接测试的性能。Default: false -->
<property name="testConnectionOnCheckout">false</property>
<!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
<!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数
允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始
广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到
支持,但今后可能的版本可能不支持动态反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>
</default-config>
<!--指定配置名称的配置信息-->
<named-config name="dumbTestConfig">
</named-config>
</c3p0-config>
9.3 Druid连接池
阿里旗下开源连接池产品,使用简单,可以与Spring框架进行快速整合
1 Druid的使用:
/**
* 1 手动设置参数的方式
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 使用连接池:
DruidDataSource dataSource = new DruidDataSource();
// 手动设置数据库连接的参数:
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///web_test4");
dataSource.setUsername("root");
dataSource.setPassword("abc");
// 获得连接:旧方式
//conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
/**
* 2 配置方式设置参数
* Druid配置方式可以使用属性文件配置的。
* 文件名称没有规定但是属性文件中的key要一定的。
*/
public void demo2(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 使用连接池:
// 从属性文件中获取:
Properties properties = new Properties();
properties.load(new FileInputStream("src/druid.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获得连接:旧方式
//conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
2 Druid监控及慢sql记录:
https://www.cnblogs.com/telwanggs/p/7484854.html
9.4 自定义连接池
步骤:
1 编写一个类实现DataSource接口
2 重写getConnection方法
3 初始化多个连接在内存中
4 编写归还连接的方法
//自定义连接池
public class MyDataSource implements DataSource {
// 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
private List<Connection> connList = new ArrayList<Connection>();
// 在初始化的时候提供一些连接
public MyDataSource() {
// 初始化连接:
for(int i = 1;i<=3;i++){
// 向集合中存入连接:
connList.add(JDBCUtils.getConnection());
}
}
// 从连接池中获得连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0);
// 增强连接:
MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connList);
return connWrapper;
}
// 编写一个归还连接的方法:
/*public void addBack(Connection conn){
connList.add(conn);
}*/
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> T unwrap(Class<T> arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Connection getConnection(String arg0, String arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
测试自定义连接池:
public class DataSourceDemo1 {
@Test
/**
* 测试自定义连接池
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
DataSource dataSource = null;
try{
// 获得连接:
// conn = JDBCUtils.getConnection();
// 从连接池中获得连接:
dataSource = new MyDataSource();
conn = dataSource.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
// 归还连接:
// dataSource.addBack(conn);
}
}
}
使用装饰者增强Connection中的close方法:将原有的close方法的逻辑改为归还(增强一个类中的方法)
装饰者模式使用条件:
1、增强类和被增强类实现相同的接口
2、在增强的类中获得被增强的类的引用

public class MyConnectionWrapper extends ConnectionWrapper{
private Connection conn;
private List<Connection> connList;
public MyConnectionWrapper(Connection conn,List<Connection> connList) {
super(conn);
this.conn = conn;
this.connList= connList;
}
// 增强某个方法:
@Override
public void close() throws SQLException {
// super.close();
// 归还连接:
connList.add(conn);
}
}
JDBC 和连接池的更多相关文章
- c3p0、dbcp、tomcat jdbc pool 连接池配置简介及常用数据库的driverClass和驱动包
[-] DBCP连接池配置 dbcp jar包 c3p0连接池配置 c3p0 jar包 jdbc-pool连接池配置 jdbc-pool jar包 常用数据库的driverClass和jdbcUrl ...
- jdbc数据连接池dbcp要导入的jar包
jdbc数据连接池dbcp要导入的jar包 只用导入commons-dbcp-x.y.z.jarcommons-pool-a.b.jar
- 关于JDBC和连接池我学到的(转载保存)
1.JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模 ...
- JDBC之 连接池
JDBC之 连接池 有这样的一种现象: 用java代码操作数据库,需要数据库连接对象,一个用户至少要用到一个连接.现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力 ...
- JDBC数据源连接池(4)---自定义数据源连接池
[续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...
- JDBC数据源连接池(3)---Tomcat集成DBCP
此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...
- JDBC数据源连接池(2)---C3P0
我们接着<JDBC数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3P0的j ...
- DBCP,C3P0与Tomcat jdbc pool 连接池的比较
hibernate开发组推荐使用c3p0; spring开发组推荐使用dbcp(dbcp连接池有weblogic连接池同样的问题,就是强行关闭连接或数据库重启后,无法reconnect,告诉连接被重置 ...
- JDBC数据源连接池的配置和使用实例
个人学习参考所用,勿喷! 使用JDBC建立数据库连接的两种方式: 1.在代码中使用DriverManager获得数据库连接.这种方式效率低,并且其性能.可靠性和稳定性随着用户访问量得增加逐渐下降. 2 ...
- mysql,jdbc、连接池
show processlist; select * from information_schema.processlist; Command: The type of command the thr ...
随机推荐
- Jenkins自动部署增加http状态码校验
公司推进Jenkins自动化部署,因为web站点都是集群部署,部署需要测试指定服务器web服务是否成功启动,页面是否正常访问,经过不断baidu发现,python的request模块可以很好的解决这一 ...
- Mac上安装mysqlclient的报错
[背景] 今天我把算把自己的python基础平台从python-3.6.2升级到python-3.7.2,在我安装完python-3.7.2之后,打算在此基础之上安装 mysqlclient的时候报错 ...
- 详解Docker的网络模式
我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: host模式:使用--net=host指定container模式:使用- ...
- Android jks 签名文件 生成
Android Win7 上使用cmd生成Jks cmd 命令 C:\Program Files\Java\jre1.8.0_111\bin>keytool -genkeypair -alias ...
- vs code 快捷键中英文对照
常用 General 按 Press 功能 Function Ctrl + Shift + P,F1 显示命令面板 Show Command Palette Ctrl + P 快速打开 Quick O ...
- 【OCR技术系列之七】端到端不定长文字识别CRNN算法详解
在以前的OCR任务中,识别过程分为两步:单字切割和分类任务.我们一般都会讲一连串文字的文本文件先利用投影法切割出单个字体,在送入CNN里进行文字分类.但是此法已经有点过时了,现在更流行的是基于深度学习 ...
- surface shader获取像素深度差值
void vert (inout appdata_full v, out Input i) { UNITY_INITIALIZE_OUTPUT(Input, i); i.proj = ComputeS ...
- 系统编码,文件编码,python编码
系统编码,可以通过locale命令查看(LINUX)https://wiki.archlinux.org/index.php/Locale_(简体中文), centos7 配置文件在/etc/prof ...
- HTTP 07 追加协议与 Ajax
Ajax 解决方法 是一种有效的利用 JavaScript 和 DOM 的操作, 以达到局部Web 页面替换加载异步的通信手段.以达到局部web页面替换加载异步通信手段.和以前的同步通信相比, 由于它 ...
- python3命令行ImportError: No module named 'xxxx'的问题
主要原因:启动脚本不在当前目录下,无法找到上一层 在pycharm写好的脚本程序,在命令行无法运行,报错 Traceback (most recent call last): File "t ...