JDBC及开源连接池Druid和C3P0的使用

1.JDBC

前面我们讲解了如何创建我们的第一个JDBC程序,接下来就详细的介绍我们的第一个JDBC程序

JDBC概述:JDBC是一种用于执行sql语句的java API,可以为多种关系数据库提供统一访问,它由一组java语言编写的类和接口组成,JDBC提供了一种基准,据此1就可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

什么是数据库驱动

两个设备(应用)之间通信的桥梁

为什么要学习JDBC

没有JDBC的时候,如果现在要开发一套系统,使用java连接MySQL数据库,那么这时候java程序员需要了解MySQL驱动API,如果使用java连接Oracle数据库,那么这个时候java程序源需要了解Oracle数据库驱动API,后来,SUN公司就提供了一套统一的规范(接口),然后各个数据库生产商提供这套接口的实现,这套接口规范就是JDBC的规范了

JDBC的环境部署(以下JDBC操作的数据库都是这个数据库)

创建数据库:

create database my;
创建表:

-- 用户表

CREATE TABLE user(
nid int not null auto_increment PRIMARY KEY,
username varchar(20),
password varchar(20),
role varchar(20)
);

-- 银行账户表

create table account(
nid int not null auto_increment PRIMARY key,
name varchar(20),
money int
);

添加数据:

//往用户表添加数据

insert into student(username, password, role) VALUES("luyi", "123", "student"), ("luer","321", "teacher")

//往银行账户表添加数据
insert into account(name, money) values("luyi", 1000), ("luer", 1000)

JDBC的使用步骤

1.加载驱动

2.获得连接

3.基本操作(增删改查)

4.释放资源

代码示例:

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

public class JDBCDemo {
public static void main(String[] args) throws Exception {
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获得连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
//获得执行sql的对象
Statement statement = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql语句
ResultSet rs = statement.executeQuery(sql);
//遍及结果集
while(rs.next()){
System.out.println(rs.getInt("id") + " ");
System.out.println(rs.getString("username") + " ");
System.out.println(rs.getString("password") + " ");
System.out.println(rs.getString("role") + " ");
}

//释放资源
rs.close();
statement.close();
conn.close();
}
}

2.JDBC的API详解

DriverManager:驱动管理类

  • registerDriver(Driver driver):该方法可以完成驱动的注册,但是实际开发中一般不会使用这个方法完成驱动的注册,因为通过查看源代码我们可以知道,在代码中有一段静态代码块,静态代码块已经调用了注册驱动的方法,所以如果我们手动调用该方法注册驱动,就会导致驱动被注册两次,实际开发中我们使用Class.forName("com.mysql.cj.jdbc.Driver"),通过加载这个类,然后执行静态代码块的代码,注册了驱动
  • getConnection(url, user, password):这个方法就是用来获得与数据库连接的,这个方法的三个参数,返回值是一个Connection实现类对象:
    • url:与数据库连接的路径:格式为jdbc:mysql://localhost:3306/mydb,如果连接的是本机,则可简写为jdbc:mysql:///mydb
      • jdbc:连接数据库的协议
      • mysql:是jdbc的子协议,我们用的是mysql数据
      • localhost:连接的MySQL数据库服务区的主机地址(连接本机的数据所以写出localhost,如果不是本机的数据库,就需要写上连接主机的ip地址)
      • 3306:MySQL数据库服务器的端口号
      • mydb:数据库名称
    • user:与数据库连接的用户名
    • password:与数据库连接的密码

Connection:与数据库连接的对象,是一个接口

Connection的实现类对象可以干嘛呢?

1.创建执行sql语句的对象:

  • createStatement():创建一个Statement对象,用于将sql语句发送到数据库
  • prepareCall(String sql):创建一个CallableStatement对象来调用数据库存储过程
  • prepareStatement(String sql):创建一个PreparedStatment对象来将参数化的sql语句发送到数据库,用于对sql进行预处理,解决sql注入漏洞

2.管理事务

  • setAutoCommit(boolean autoCommit):用于设置是否自动提交事务
  • commit():用于提交事务
  • rollback():事务回滚的方法

Statement:用于执行静态sql语句并返回它所生成结果的对象,一个接口

Statement的实现类对象可以干嘛呢?

1.执行sql:

  • boolean execute(String sql):执行查询,修改,添加,删除的sql语句,不经常使用
  • ResultSet executeQuery(String sql):执行查询语句,返回结果集ResultSet
  • int executeUpdate(String sql):执行修改,添加,删除的sql语句

2.执行批处理

  • void addBatch(String sql):将给定的sql命令添加到此Statement对象的当前命令列表中(添加批处理)
  • void clearBatch():清空此Statement对象的当前sql命令列表(删除批处理)
  • int[] executeBatch():将命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组(执行批处理)

ResultSet(结果集):表示的是数据库结果集的数据表,通常通过执行查询数据库的语句生成,是一个接口

1.结果集的遍历和获取:

  • next():将光标从当前位置向前移动一行
  • getXxx(int columnIndex):根据第几列第几列来获取数据
  • getXxx(String columnName):根据列名来获取数据

JDBC资源释放

概述:JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection,这几个对象中尤其是Connection对象是非常稀有的,这个对象一定要做到尽量晚创建,尽早释放掉

标准的释放代码应该写入finally代码中,而且需要手动将这几个对象置为空,以便及早被垃圾回收器发现,回收

标准的释放资源代码如下:

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

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

Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try{
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
//获得执行sql的对象
statement = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql语句
rs = statement.executeQuery(sql);
//遍及结果集
while(rs.next()){
System.out.println(rs.getInt("id") + " ");
System.out.println(rs.getString("username") + " ");
System.out.println(rs.getString("password") + " ");
System.out.println(rs.getString("role") + " ");
}
}catch(Exception e){
e.printStackTrace();
}finally{
//标准的释放资源
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
statement = null;
}

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

3.JDBC的CRUD操作

代码示例(对数据库进行增删改查):

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

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

Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try{
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
//获得执行sql的对象
statement = conn.createStatement();
//编写sql语句
String sql1 = "insert into user(username, password, role) value('卢一', '123', 'doctor')";//添加操作
//String sql2 = "delete from user where username = 'luyi'";//删除操作
//String sql3 = "update user set password = '666' where username = 'luer'";//修改操作
String sql4 = "select * from user";//查询操作

//增删改操作对应返回影响的行数
int column = statement.executeUpdate(sql1);
if(column > 0){
System.out.println("操作成功");
}

///查询操作对应返回结果集
* //执行sql语句
rs = statement.executeQuery(sql4);
//遍及结果集
while(rs.next()){
System.out.print(rs.getInt("id") + " ");
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
/
}catch(Exception e){
e.printStackTrace();
}finally{
//标准的释放资源
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
statement = null;
}

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

4.对JDBC的工具类的抽取以及提取配置信息到配置文件

从上面对JDBC的增删改查操作可以看出

1.工具类:

没有对象封装数据体现,只有方法,禁止了对象创建

2.配置文件:

  • 属性文件
    • 格式:扩展名是.properties
    • 内容:key=value:键值对形式出现
  • XML文件

示例代码:

//db.properties属性配置文件

driverClassName = com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC
username=root
password=123

//JDBCUtils.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/*
* 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{
//获取属性文件中的内容
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 {
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException 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(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;
}
if(rs != null){
try {
rs.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
rs = null;
}
}
}

//JDBCUtilsText

/*
* JDBC工具类的测试类
*/
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class JDBCUtilsText {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
//加载驱动
JDBCUtils.loadDriver();
//获得连接
conn = JDBCUtils.getConnection();
//sql操作
stmt = conn.createStatement();
String sql = "select * from user";
rs = stmt.executeQuery(sql);
//遍历结果集
while(rs.next()){
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}

}
}

5.JDBC的SQL注入及解决方式

sql注入的概述:SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库

sql注入攻击实例:

String sql = "select * from user_table where username=
' "+userName+" ' and password=' "+password+" '";

--当输入了上面的用户名和密码,上面的SQL语句变成:
SELECT * FROM user_table WHERE username=
'’or 1 = 1 -- and password='’

"""
--分析SQL语句:
--条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;

--然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都--能正确执行,用户轻易骗过系统,获取合法身份。
--这还是比较温柔的,如果是执行
SELECT * FROM user_table WHERE
username='' ;DROP DATABASE (DB Name) --' and password=''
--其后果可想而知…
"""

JDBC的SQL注入漏洞解决

需要采用PreparedStatement对象解决SQL注入漏洞,这个对象将SQL预先进行编译1,使用?作为占位符,?所代表的内容就是SQL所固定的,再次传入变量(包含SQL的关键字),这个时候也不会识别这些关键字

使用PrepareStatement对象进行增删改查:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JDBCUtilsText {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
//加载驱动
JDBCUtils.loadDriver();
//获得连接
conn = JDBCUtils.getConnection();

//预编译sql操作

//添加操作
/String sql1 = "insert into user(username, password, role) value(?, ?, ?)";
pstmt = conn.prepareStatement(sql1);
pstmt.setString(1, "赵露思");
pstmt.setString(2, "888");
pstmt.setString(3, "actor");
/

//删除操作
/String sql2 = "delete from user where role = ?";
pstmt.setString(1, "doctor");
/

//修改操作
String sql3 = "update user set password = ? where username = ?";
pstmt = conn.prepareStatement(sql3);
pstmt.setString(1, "000000");
pstmt.setString(2, "赵露思");
int column = pstmt.executeUpdate();
if(column > 0){
System.out.println("操作成功");
}

//查询操作
/String sql4 = "select * from user where username = ? and password = ?";
pstmt = conn.prepareStatement(sql4);
pstmt.setString(1, "luer");
pstmt.setString(2, "666");
rs = pstmt.executeQuery();
if(rs.next()){
System.out.println(rs.getString("username"));
System.out.println(rs.getString("password"));
}
/

}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
}

6.JDBC的批处理

批量插入数据

import java.sql.Connection;
import java.sql.PreparedStatement;

public class JDBCDemo2 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try{
JDBCUtils.loadDriver();
conn = JDBCUtils.getConnection();
String sql = "insert into user(username, password, role) values(?, '123', 'student')";
pstmt = conn.prepareStatement(sql);

for(int i = 1; i <= 10000; i ++){
pstmt.setString(1, "luyi" + i);
//添加到批处理
pstmt.addBatch();
//每添加1000条就执行一次批处理并释放
if(i % 1000 == 0){
//执行批处理
pstmt.executeBatch();
//清空批处理
pstmt.clearBatch();
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}

}
}

//注意:db.properiter属性文件中url需要传递一个参数表示允许批处理,修改后如下:
url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC&rewriteBatchedStatements=true

7.JDBC的事务管理

事务的概念:事务是指逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败

JDBC的事务管理案例(银行转账案例):

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class AffairDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;

try{
//加载驱动
JDBCUtils.loadDriver();
//获得连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//编写sql语句,用luyi账户给luer账户转账
String sql = "update account set money = money + ? where name = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, -100);
pstmt.setString(2, "luyi");
//执行sql
pstmt.executeUpdate();

//使其报错
int i = 1/0;

//给luer账号加100
pstmt.setInt(1, 100);
pstmt.setString(2, "luer");
pstmt.executeUpdate();
//提交事务
conn.commit();
}catch(Exception e){
//事务回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
}
}

//如果没有添加事务,程序里面的这个报错就会使得luyi转账没了100,但是luer却没有收到这100块

8.连接池

概述:连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用;连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接,把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中,从内存中获取和归还的效率要远远高于创建和销毁的效率

自定义连接池

概述:通过实现DataSource接口实现自定义连接池

代码示例:

//MyDataSource.java

package cn.luyi.demo1;

/*
* 自定义连接池
*/
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

public class MyDataSource implements DataSource {
//将一些连接存入到内存中,可以定义一个集合,用于存储连接对象,这个集合就相当于连接池
private List connList = new ArrayList();
//在初始化的时候提供一些连接,添加连接到内存中
public MyDataSource(){
//初始化连接
for(int i = 1; i <= 3; i ++){
connList.add(JDBCUtils.getConnection());
}
}
//从连接池中获得连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0);
return conn;
}
/*
* 将连接归还到连接池的方法
*/
public void addBack(Connection conn){
connList.add(conn);
}

@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub

}

@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 T unwrap(Class iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}

@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}

}

//MyDataSourceText.java

package cn.luyi.demo1;

/*
* 连接池测试类
*/
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class MyDataSourceText {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
MyDataSource mdb = null;

try{
//加载驱动
JDBCUtils.loadDriver();
//从连接池获取连接
mdb = new MyDataSource();
conn = mdb.getConnection();

stmt = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql,获取结果集
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//释放资源
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;

//归还连接到连接池
mdb.addBack(conn);
}
}
}

以上是我们对自定义连接池的基本实现,但是我们的代码存在两点不合理的地方:

  • 使用接口的实现类完成构造,而不是接口来完成构造
  • 额外提供了连接归还连接池的方法,也正是因为有这个额外的方法,我们无法用接口来完成构造

那么,我们怎么样才能不提供额外的方法,还能实现连接归还连接池呢?

回想之前我们释放Connection时调用的是close方法,那我们是否有办法把close完成的释放资源的功能改为归还连接的功能呢?

答案是肯定的,把原有的close方法的逻辑改为归还连接,也就是增强一个类中的方法,我们有三种方法:

1.采用继承的方式,继承这种方法最简单,但是是有使用条件的:必须能够控制这个类的构造,也就是可以new这个类创建对象

2.采用装饰者模式:使用条件

  • 增强的类和被增强的类实现相同的接口
  • 在增强的类中获得被增强的类的引用

3.采用动态代理模式

采用装饰者模式实现把原有的close方法的逻辑改为归还连接代码示例:

//ConnectionImpl.java

package cn.luyi.demo1;

/*
* 模板类
*/
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class ConnectionImpl implements Connection{

private Connection conn;
public ConnectionImpl(Connection conn){
super();
this.conn = conn;
}
@Override
public T unwrap(Class iface) throws SQLException {
return conn.unwrap(iface);
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return conn.isWrapperFor(iface);
}

@Override
public Statement createStatement() throws SQLException {
return conn.createStatement();
}

@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
}

@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return conn.prepareCall(sql);
}

@Override
public String nativeSQL(String sql) throws SQLException {
return nativeSQL(sql);
}

@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {

}

@Override
public boolean getAutoCommit() throws SQLException {
return conn.getAutoCommit();
}

@Override
public void commit() throws SQLException {
conn.commit();
}

@Override
public void rollback() throws SQLException {
conn.rollback();
}

@Override
public void close() throws SQLException {
conn.close();
}

@Override
public boolean isClosed() throws SQLException {
return conn.isClosed();
}

@Override
public DatabaseMetaData getMetaData() throws SQLException {
return conn.getMetaData();
}

@Override
public void setReadOnly(boolean readOnly) throws SQLException {
conn.setReadOnly(readOnly);
}

@Override
public boolean isReadOnly() throws SQLException {
return conn.isReadOnly();
}

@Override
public void setCatalog(String catalog) throws SQLException {
conn.setCatalog(catalog);
}

@Override
public String getCatalog() throws SQLException {
return conn.getCatalog();
}

@Override
public void setTransactionIsolation(int level) throws SQLException {
conn.setTransactionIsolation(level);
}

@Override
public int getTransactionIsolation() throws SQLException {
return conn.getTransactionIsolation();
}

@Override
public SQLWarning getWarnings() throws SQLException {
return conn.getWarnings();
}

@Override
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}

@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return conn.createStatement();
}

@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
}

@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
}

@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return conn.getTypeMap();
}

@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
conn.setTypeMap(map);
}

@Override
public void setHoldability(int holdability) throws SQLException {
conn.setHoldability(holdability);
}

@Override
public int getHoldability() throws SQLException {
return conn.getHoldability();
}

@Override
public Savepoint setSavepoint() throws SQLException {
return conn.setSavepoint();
}

@Override
public Savepoint setSavepoint(String name) throws SQLException {
return conn.setSavepoint();
}

@Override
public void rollback(Savepoint savepoint) throws SQLException {
conn.rollback();
}

@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
conn.releaseSavepoint(savepoint);
}

@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}

@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
}

@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}

@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return conn.prepareStatement(sql, autoGeneratedKeys);
}

@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return conn.prepareStatement(sql, columnIndexes);
}

@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return conn.prepareStatement(sql, columnNames);
}

@Override
public Clob createClob() throws SQLException {
return conn.createClob();
}

@Override
public Blob createBlob() throws SQLException {
return conn.createBlob();
}

@Override
public NClob createNClob() throws SQLException {
return conn.createNClob();
}

@Override
public SQLXML createSQLXML() throws SQLException {
return conn.createSQLXML();
}

@Override
public boolean isValid(int timeout) throws SQLException {
return conn.isValid(timeout);
}

@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
conn.setClientInfo(name, value);
}

@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
conn.setClientInfo(properties);
}

@Override
public String getClientInfo(String name) throws SQLException {
return getClientInfo(name);
}

@Override
public Properties getClientInfo() throws SQLException {
return conn.getClientInfo();
}

@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return conn.createArrayOf(typeName, elements);
}

@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return conn.createStruct(typeName, attributes);
}

@Override
public void setSchema(String schema) throws SQLException {
conn.setSchema(schema);
}

@Override
public String getSchema() throws SQLException {
return conn.getSchema();
}

@Override
public void abort(Executor executor) throws SQLException {
conn.abort(executor);
}

@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
conn.setNetworkTimeout(executor, milliseconds);
}

@Override
public int getNetworkTimeout() throws SQLException {
return conn.getNetworkTimeout();
}

}

//MyConnectionWrapper.java
package cn.luyi.demo1;

/*
* 装饰者模式增强类中的close方法
*/
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class MyConnectionWrapper extends ConnectionImpl {
private Connection conn;
private List connList;

public MyConnectionWrapper(Connection conn, List connList) {
super(conn);
this.conn = conn;
this.connList = connList;
}
//增强了close方法:将释放资源功能改为归还连接功能
@Override
public void close() throws SQLException {
connList.add(conn);
}
}

//MyDataSource.java

package cn.luyi.demo1;

/*
* 自定义连接池
*/
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

public class MyDataSource implements DataSource {
//将一些连接存入到内存中,可以定义一个集合,用于存储连接对象,这个集合就相当于连接池
private List connList = new ArrayList();
//在初始化的时候提供一些连接,添加连接到内存中
public MyDataSource(){
//初始化连接
for(int i = 1; i <= 3; i ++){
connList.add(JDBCUtils.getConnection());
}
}
//从连接池中获得连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0);
//使用装饰者模式把close重写为归还连接的方法
MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connList);
return connWrapper;
}

@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub

}

@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 T unwrap(Class iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}

@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}

}

//MyDataSourceText.java

package cn.luyi.demo1;

/*
* 连接池测试类
*/
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

public class MyDataSourceText {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
DataSource mdb = null;

try{
//加载驱动
JDBCUtils.loadDriver();
//从连接池获取连接
mdb = new MyDataSource();
conn = mdb.getConnection();

stmt = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql,获取结果集
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//释放资源
JDBCUtils.release(rs, stmt, conn);

}
}
}

开源连接池Druid

Druid概述:Druid是阿里旗下的开源连接池产品,使用非常简单,可以与spring框架进行快速整合,在很多的开源网站上都可以找到它,下载它的jar包就可以使用了

Druid的使用(两种方式):

//db.properties

driverClassName = com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC&rewriteBatchedStatements=true
username=root
password=123

//DruidDemo.java

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

public class DruidDemo {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
//加载驱动
JDBCUtils.loadDriver();

//使用Druid连接池获取Connection对象
DruidDataSource dataSource = new DruidDataSource();
///方式一:手动设置数据库连接的参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/my?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123");
conn = dataSource.getConnection();
/
//方式二:从配置文件里读取
Properties properties = new Properties();
properties.load(new FileInputStream("src/db.properties"));
DataSource datasource = DruidDataSourceFactory.createDataSource(properties);
conn = datasource.getConnection();

stmt = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql,获取结果集
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//释放资源
JDBCUtils.release(rs, stmt, conn);

}

开源连接池C3P0

概述:C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等,下载它的jar包就可以使用了,需要注意的是C3P0版本0.9.1之前是只有一个包的,而版本0.9.2之后就被分离了一个mchange-commons-java包出来,所以使用版本0.9.2以上就需要导入两个包

C3P0的连接池使用:

//c3p0-config.xml





com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/my?serverTimezone=UTC
root
123

5
10
3000




//C3P0Demo.java

package cn.luyi.demo1;

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

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Demo {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
//加载驱动
JDBCUtils.loadDriver();

//使用C3P0连接池获取Connection对象
///方式一:手动设置参数
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/my?serverTimezone=UTC");
dataSource.setUser("root");
dataSource.setPassword("123");
conn = dataSource.getConnection();
/

//方式二:使用配置文件
ComboPooledDataSource dataSource = new ComboPooledDataSource();
conn = dataSource.getConnection();

stmt = conn.createStatement();
//编写sql语句
String sql = "select * from user";
//执行sql,获取结果集
rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.print(rs.getString("username") + " ");
System.out.print(rs.getString("password") + " ");
System.out.println(rs.getString("role"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//释放资源
JDBCUtils.release(rs, stmt, conn);

}
}
}

连接池版的工具类

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils2 {
//创建一个连接池,但是这个连接池只需要创建一次即可
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();

/*
* 获得连接的方法
/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
/

* 获得连接池
/
public static DataSource getDataSource(){
return dataSource;
}
/

* 释放资源的方法
*/
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(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;
}
if(rs != null){
try {
rs.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
rs = null;
}
}
}

9.DBUtils

概述:Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能,下载它的jar包即可使用

为什么要学习DBUtils?

因为JDBC手写比较麻烦,而且有非常多的代码是类似的,比如获得连接,预编译SQL,释放资源登,那么可以将这些代码抽取出来放到工具类中,大大简化JDBC的编程

DBUtils核心类QueryRunner

有两套常用方法:

1.在一般情况下如果执行CRUD的操作:

  • QueryRunner(DataSource ds):带参构造方法
  • int update(String sql, Object...args):增删改操作
  • T query(String sql, ResultSetHandler rsh, Object...args):查询操作

2.如果有事务管理的话则用这一套:

  • QuerryRunner():无参构造方法
  • int update(Connection conn, String sql, Object...args):增删改操作
  • T query(Connection conn, String sql, ResultSetHandler rsh, Object...args):查询操作

批量处理方法:

  • int[] batch(Connection conn, String sql, Object[][] params)
  • int[] batch(String sql, Object[][] params)

DbUtils类

主要方法:

  • static void commitAndCloseQuietly(Connection conn):提交事务,会把异常也处理了
  • static void rollbackAndCloseQuiety(Connection conn):回滚事务,会把异常也处理了

代码示例1(DBUtils的增删改):

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

import org.apache.commons.dbutils.QueryRunner;

public class DBUtilsDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
//insert();
//delete();
update();

}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils2.release(rs, pstmt, conn);
}
}
/*
*添加数据操作
/
public static void insert() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("insert into user value(null, ?, ?, ?)", "黄伊", "123", "student");
}
/

* 删除操作
/
public static void delete() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("delete from user where username = ?", "luer");
}
/

* 修改操作
*/
public static void update() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("update user set password = ? where username = ?", "520", "黄伊");
}
}

代码示例2(DBUtils的查询操作):

//User.java
package cn.luyi.demo1;

public class User {
private String username;
private String password;
private String role;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", role=" + role + "]";
}

}

// DBUtilsDemo2.java
package cn.luyi.demo1;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

public class DBUtilsDemo2 {
public static void main(String[] args) throws SQLException {
//querySingle();
queryMany();
}

/*
* 单条查询
*/
public static void querySingle() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
User user = queryRunner.query("select * from user where username = ?", new ResultSetHandler(){

@Override
public User handle(ResultSet rs) throws SQLException {
User user = new User();
if(rs.next()){
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setRole(rs.getString("role"));
}
return user;
}

}, "lusan");

System.out.println(user);
}
/*
* 多条查询
*/
public static void queryMany() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
//用一个集合来装User对象
List list = queryRunner.query("select * from user", new ResultSetHandler<List>(){

@Override
public List handle(ResultSet rs) throws SQLException {
//创建一个集合用于封装数据
List list = new ArrayList();
while(rs.next()){
User user = new User();
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setRole(rs.getString("role"));
list.add(user);
}
return list;
}
});
for(User user : list){
System.out.println(user);
}

}
}

ResultHandler的实例

前面我们通过使用DBUtils对数据库进行增删改查,可以看出确实省了很多代码,但是我们在使用查询时,仍然需要一个个手动封装数据到对象中,有没有什么办法可以帮我们封装好数据呢?答案是有的,ResultHandler的不同的实现类就帮我们做了这样的事情,接下来我们就来学习ResultHandler的实例吧

1.ArrayHandler和ArrayListHandler

  • ArrayHandler将一条记录封装到一个数组当中,这个数组就是Object[]
  • ArrayListHandler:将多条数据封装到一个装有Object[]的List集合中

代码示例:

import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;

public class DBUtilsDemo3 {
public static void main(String[] args) throws SQLException {
//method1();
method2();
}
/*
* ArrayHandler:将一条记录封装到一个Object数组中
/
public static void method1() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
Object[] objs = queryRunner.query("select * from user where id = ?", new ArrayHandler(), 3);
System.out.println(Arrays.toString(objs));
}
/

* ArrayListHandler:将多条数据封装到一个装有Object数组的list集合中
*/
public static void method2() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
List<Object[]> list = queryRunner.query("select * from user", new ArrayListHandler());

for(Object[] obj:list){
System.out.println(Arrays.toString(obj));
}
}
}

2.BeanHandler和BeanListHandler

  • BeanHandler:将一条记录封装到一个javaBean中
  • BeanListHandler:将多条记录封装到一个装有javaBean的list集合中

代码示例:

//User.java

public class User {
private String username;
private String password;
private String role;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", role=" + role + "]";
}

}

//DBUtilsDemo4.java

import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

public class DBUtilsDemo4 {
public static void main(String[] args) throws SQLException {
//method1();
method2();
}
/*
* BeanHandler的使用
*/
public static void method1() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
User user = queryRunner.query("select * from user where id = ?", new BeanHandler(User.class), 3);

System.out.println(user);
}

/*
* BeanListHandler的使用
*/
public static void method2() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
List list = queryRunner.query("select * from user", new BeanListHandler(User.class));
for(User user:list){
System.out.println(user);
}
}
}

3.MapHandler和MapListHandler

  • MapHandler:将一条记录封装到一个集合中,Map的key是列名,Map的value就是表中列的记录
  • MapListHandler:将多条记录封装到一个装有Map的List集合中

代码示例:

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;

public class DBUtilsDemo5 {
public static void main(String[] args) throws SQLException {
//method1();
method2();
}
/*
* MapHandler的使用
*/
public static void method1() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
Map<String, Object> map = queryRunner.query("select * from user where id = ?", new MapHandler(), 3);

System.out.println(map);
}
/*
* MapListHandler的使用
*/
public static void method2() throws SQLException{
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
List<Map<String, Object>> list = queryRunner.query("select * from user", new MapListHandler());

for(Map<String, Object> map:list){
System.out.println(map);
}
}
}
4.ColumnListHandler、ScalarHandler、KeyedHandler(了解)

  • ColumnListHandler:将数据中的某列封装到List集合中
  • ScalarHandler:将单个值进行封装
  • KeyedHandler:将一条记录封装到一个Map集合中,将多条记录封装到一个装有Map集合的Map集合中去,而且外面的Map的key是可以指定的

代码示例:

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.KeyedHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

public class DBUtilsDemo6 {
public static void main(String[] args) throws SQLException {
//method1();
//method2();
method3();
}

/*
* ColumnListHandler的使用
*/
public static void method1() throws SQLException {
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
List list = queryRunner.query("select username, password from user", new ColumnListHandler("username"));
for (Object obj : list) {
System.out.println(obj);
}
}

/*
* ScalarHandler的使用
/
public static void method2() throws SQLException {
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
Object obj = queryRunner.query("select count(
) from user", new ScalarHandler());
System.out.println(obj);
}
/*
* KeyedHandler的使用
*/
public static void method3() throws SQLException {
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
//指定键为username
Map<Object, Map<String, Object>> map = queryRunner.query("select * from user", new KeyedHandler("username"));
for(Object key: map.keySet()){
System.out.println(key + "---" + map.get(key));
}
}
}

posted @ 2020-08-02 19:17  luyi001  阅读(149)  评论(0)    收藏  举报