Jdbc
1、SQL语言分为五大类:
DDL( Data Definition Language 数据定义语言 ) - Create、Alter、Drop 这些语句自动提交,无需用Commit提交。
DQL( Data Query Language 数据查询语言 ) - Select 查询语句不存在提交问题。
DML( Data Manipulation Language 数据操纵语言 ) - Insert、Update、Delete 这些语句需要Commit才能提交。
DTL( 事务控制语言 Data Transaction Language ) - Commit、Rollback 事务提交与回滚语句。
DCL( 数据控制语言 Data Control Language ) - Grant、Revoke 授予权限与回收权限语句。
-
连接mysql数据库
public class MysqlDemo {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
// 注册 JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spark","root","123456");
// 执行查询
System.out.println(" 实例化Statement对象...");
stmt = conn.createStatement();
String sql;
sql = "select user_id, name from name limit 5";
ResultSet rs = stmt.executeQuery(sql);
// 展开结果集数据库
while(rs.next()){
// 通过字段检索
int user_id = rs.getInt("user_id");
String name = rs.getString("name");
// 输出数据
System.out.print("user_id: " + user_id);
System.out.print(", 姓名: " + name);
System.out.print("\n");
}
}catch(SQLException se){
// 处理 JDBC 错误
se.printStackTrace();
}catch(Exception e){
// 处理 Class.forName 错误
e.printStackTrace();
}finally{
// 关闭资源
try{
if(stmt!=null) stmt.close();
}catch(SQLException se2){
}// 什么都不做
try{
if(conn!=null) conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("end!");
}
}
-
PreparedStatement
表示预编译的SQL语句的对象。SQL语句预编译并存储在一个PreparedStatement
对象中。 然后可以使用该对象多次有效地执行此语句。
Modifier and Type | 方法 | 描述 |
---|---|---|
void | addBatch() | 向这个 |
boolean | execute() | 执行此 |
ResultSet | executeQuery() | 执行此 |
int | executeUpdate() | 执行在该SQL语句 |
public class MysqlDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
try {
// 注册 JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spark", "root", "123456");
// 插入数据
System.out.println(" 实例化PreparedStatement对象...");
String sql = "insert test values(?, ?)";
prep = conn.prepareStatement(sql);
prep.setInt(1, 1);
prep.setString(2, "李茂贞");
int i = prep.executeUpdate();
if (i > 0) {
System.out.println(i);
System.out.println("插入数据成功");
}
// 查询数据
sql = "select * from test";
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println("用户id: " + resultSet.getInt("user_id") + ", 姓名: " + resultSet.getString("name"));
}
statement.close();
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
/*
连接数据库...
实例化PreparedStatement对象...
1
插入数据成功
用户id: 1, 姓名: 李茂贞
end!
*/
-
jdbc批处理
将一批SQL一起执行。
-
Statement
void | addBatch(String sql) | 将给定的SQL命令添加到此 |
---|---|---|
void | clearBatch() | 清空这个 |
int[] | executeBatch() | 将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组。 |
void | clearBatch() | 清空这个 |
/*
* 批处理的操作
*/
public class MysqlDemo {
public static void main(String[] args) {
Connection conn = null;
Statement prep = null;
try {
// 注册 JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spark", "root", "123456");
prep = conn.createStatement();
// sql
String sql1 = "create database test";
String sql2 = "use test";
String sql3 = "create table student(id int, name varchar(50))";
String sql4 = "insert student values(1, '李茂贞')";
String sql5 = "insert student values(2, '袁天罡')";
//添加到批处理
prep.addBatch(sql1);
prep.addBatch(sql2);
prep.addBatch(sql3);
prep.addBatch(sql4);
prep.addBatch(sql5);
//批量执行
int[] results = prep.executeBatch();
for (int i : results) {
if (!(i > 0)) {
System.out.println("sql执行失败!");
}
}
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
/*
连接数据库...
end!
*/
-
PreparedStatement
1、 PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象。
2、作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数
3、使用“?”占位符提升代码的可读性和可维护性.
4、使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。
5、PreparedStatement可以防止SQL注入式攻击。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行
Modifier and Type | 方法 | 描述 |
---|---|---|
void | addBatch() | 向这个 |
void | clearBatch() | 清空这个 |
void | clearBatch() | 清空这个 |
ResultSet | executeQuery() | 执行此 |
int | executeUpdate() | 执行在该SQL语句 |
int[] | executeBatch() | 将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组。 |
注意:批处理一定要加上这个参数,否则耗费时间会非常多 jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
加上次参数插入一万条记录只需要1.7秒,不加此参数需要几分钟
/*
* 批处理的操作
*/
public class MysqlDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
try {
long startTime = System.currentTimeMillis();
// 注册 JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true", "root", "123456");
String sql = "insert into student values(?, ?)";
prep = conn.prepareStatement(sql);
// sql
for (int i = 1; i < 10000; i++) {
//拼接sql
prep.setInt(1, i);
prep.setString(2, "李茂贞" + i);
//添加到批处理
prep.addBatch();
if (i %1000 ==0){
//执行批处理
prep.executeBatch();
//清空批处理
prep.clearBatch();
}
}
//批量执行
int[] results = prep.executeBatch();
for (int i : results) {
if (!(i > 0)) {
System.out.println("sql执行失败!");
}
}
long endTime = System.currentTimeMillis();
System.out.println("共耗时 " + (endTime-startTime) + " 毫秒");
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
/*
连接数据库...
共耗时 1756 毫秒
end!
*/
-
JDBC的事务管理
- 事务的概念:
事务指的是逻辑上的一组操作,组成这组操作各个逻辑单元要么全都成功,要么全都失败。
-
事务的提交与回滚
Modifier and Type | 方法 | 描 |
---|---|---|
void | setAutoCommit(boolean autoCommit) | 将此连接的自动提交模式设置为给定状态。 |
void | setAutoCommit(boolean autoCommit) | 将此连接的自动提交模式设置为给定状态。 |
void | commit() | 使上次提交/回滚之后所做的所有更改都将永久性,并释放此 |
package com.jdbc;
import java.sql.*;
/*
* 事务管理的测试类
* */
public class MsyqlTransaction {
public static void main(String[] args){
Connection conn = null;
PreparedStatement prep = null;
try {
long startTime = System.currentTimeMillis();
// 注册 JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true", "root", "123456");
// 开启事务
conn.setAutoCommit(false);
// 编写SQL语句:
String sql = "update acount set amount=amount+? where id=?";
// 预编译SQL:
prep = conn.prepareStatement(sql);
// 设置参数:
// 用1账号给2账号转1000元
prep.setDouble(1, -5000);
prep.setInt(2, 1);
// 执行SQL:扣除aaa账号1000元
prep.executeUpdate();
//int i = 1 / 0;
// 给bbb账号加1000
prep.setDouble(1, 5000);
prep.setInt(2, 2);
prep.executeUpdate();
// 提交事务:
conn.commit();
long endTime = System.currentTimeMillis();
System.out.println("共耗时 " + (endTime-startTime) + " 毫秒");
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
-
连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。
为什么使用连接池:连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)。
连接池原理:
连接池的实现步骤:
- 编写一个类实现DataSource接口
- 重写getConnection方法
- 初始化多个连接在内存中
- 编写归还连接的方法
自定义连接池
public class MyDataSource implements DataSource {
// 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
private ArrayList<Connection> connections = new ArrayList<>();
// 在初始化的时候提供一些连接
public MyDataSource() {
// 初始化连接
for(int i=0;i<5;i++){
// 向集合中存入连接:
connections.add(Mysql.getConnection()); //Mysql.getConnection()自定义的获取连接的方法
}
}
@Override
public Connection getConnection() throws SQLException {
// 从连接池中获得连接
Connection conn = connections.remove(0);
return conn;
}
// 编写一个归还连接的方法:
public void addBack(Connection conn){
connections.add(conn);
}
...
public class Test {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement prep = null;
MyDataSource myDataSource = new MyDataSource();
try {
long startTime = System.currentTimeMillis();
//获取连接
conn = myDataSource.getConnection();
String sql = "select * from acount";
prep = conn.prepareStatement(sql);
// sql
ResultSet rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", 姓名:" + rs.getString("name") + ",金额:" + rs.getDouble("amount"));
}
rs.close();
long endTime = System.currentTimeMillis();
System.out.println("共耗时 " + (endTime - startTime) + " 毫秒");
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) {
myDataSource.addBack(conn);
};
} catch (Exception se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
/*
连接数据库...
连接数据库...
连接数据库...
连接数据库...
连接数据库...
id:1, 姓名:李茂贞1,金额:0.0
id:2, 姓名:李茂贞2,金额:20000.0
id:3, 姓名:李茂贞3,金额:10000.0
id:4, 姓名:李茂贞4,金额:10000.0
id:5, 姓名:袁天罡,金额:10000.0
共耗时 986 毫秒
end!
*/
-
自定义连接池的问题与解决方案
需要新建实现类,额外提供了方法归还连接
解决分析的思路:原来在Connection中是有一个close方法的,colse方法完成了连接的销毁。能不能做一个事情,将原有的连接的close方法改为归还。
现在要做的事情就是将原有的close方法的逻辑改为归还。(增强一个类中的方法)。
- 如何增强一个类中的方法
- 一种:采用继承的方式:
***** 继承这种增强是最简单,但是是有使用条件的:必须能够控制这个类的构造!!!
class Man{
public void run(){
System.out.println(“跑…”);
}
}
class SuperMan extends Man{
public void run(){
System.out.println(“飞…”);
}
}
第二种:采用装饰者模式:
***** 装饰者模式使用条件:
* 一、增强的类和被增强的类实现相同的接口
* 二、在增强的类中获得被增强的类的引用
interface Waiter{
public void server();
}
public class Waitress implements Waiter{
public void server(){
System.out.println(“服务中…”);
}
}
public class WaitressWrapper implements Waiter{
private Waiter waiter;
public WaitressWrapper(Waiter waiter){
this.waiter = waiter;
}
public void server(){
System.out.println(“微笑…”)
waiter.server();
}
}
/*
* 模板类
* */
public class ConnectionWrapper implements Connection {
private Connection conn;
public ConnectionWrapper(Connection conn) {
super();
this.conn = conn;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return conn.isWrapperFor(iface);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return conn.unwrap(iface);
}
...模板类索所有方法原样调用
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);
}
}
public class MyDataSource implements DataSource {
// 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
private ArrayList<Connection> connections = new ArrayList<>();
// 在初始化的时候提供一些连接
public MyDataSource() {
// 初始化连接
for(int i=0;i<5;i++){
// 向集合中存入连接:
connections.add(Mysql.getConnection()); //Mysql.getConnection()自定义的获取连接的方法
}
}
@Override
public Connection getConnection() throws SQLException {
// 从连接池中获得连接
Connection conn = connections.remove(0);
MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connections);
return connWrapper;
}
...
public class Test {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement prep = null;
MyDataSource myDataSource = new MyDataSource();
try {
long startTime = System.currentTimeMillis();
//获取连接
conn = myDataSource.getConnection();
String sql = "select * from acount";
prep = conn.prepareStatement(sql);
// sql
ResultSet rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", 姓名:" + rs.getString("name") + ",金额:" + rs.getDouble("amount"));
}
rs.close();
long endTime = System.currentTimeMillis();
System.out.println("共耗时 " + (endTime - startTime) + " 毫秒");
} catch (SQLException se) {
// 处理 JDBC 错误
se.printStackTrace();
} catch (Exception e) {
// 处理 Class.forName 错误
e.printStackTrace();
} finally {
// 关闭资源
try {
if (prep != null) prep.close();
} catch (SQLException se2) {
}// 什么都不做
try {
if (conn != null) {
conn.close();
};
} catch (Exception se) {
se.printStackTrace();
}
}
System.out.println("end!");
}
}
/*
连接数据库...
连接数据库...
连接数据库...
连接数据库...
连接数据库...
id:1, 姓名:李茂贞1,金额:0.0
id:2, 姓名:李茂贞2,金额:20000.0
id:3, 姓名:李茂贞3,金额:10000.0
id:4, 姓名:李茂贞4,金额:10000.0
id:5, 姓名:袁天罡,金额:10000.0
共耗时 99 毫秒
end!
*/
第三种:动态代理的方式
-
Druid开源连接池的使用
Druid阿里旗下开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。
Druid手动设置数据库连接参数
/*
手动设置数据库连接参数
*/
public class DruidDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
//使用Druid
DruidDataSource rdds = new DruidDataSource();
//手动设置数据库连接参数
rdds.setDriverClassName("com.mysql.jdbc.Driver");
rdds.setUrl("jdbc:mysql://localhost:3306/test");
rdds.setUsername("root");
rdds.setPassword("123456");
//获取连接
conn = rdds.getConnection();
String sql = "select * from acount";
//预编译sql
prep = conn.prepareStatement(sql);
rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", name:" + rs.getString("name") + ", amount:" + rs.getDouble("amount"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/*
11月 14, 2019 5:52:46 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
id:1, name:李茂贞1, amount:0.0
id:2, name:李茂贞2, amount:20000.0
id:3, name:李茂贞3, amount:10000.0
id:4, name:李茂贞4, amount:10000.0
id:5, name:袁天罡, amount:10000.0
Process finished with exit code 0
*/
Druid读取配置文件设置数据库连接参数
dgconfig.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=5
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
#maxIdle=20 //已弃用
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
/*
加载配置文件设置数据库连接参数
*/
public class DruidDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
//读取配置文件
Properties properties = new Properties();
properties.load(new FileReader("src\\com\\druid\\dgconfig.properties"));
//使用Druid
DataSource dataSource =new DruidDataSourceFactory().createDataSource(properties);
/*DruidDataSource rdds = new DruidDataSource();
//手动设置数据库连接参数
rdds.setDriverClassName("com.mysql.jdbc.Driver");
rdds.setUrl("jdbc:mysql://localhost:3306/test");
rdds.setUsername("root");
rdds.setPassword("123456");*/
//获取连接
conn = dataSource.getConnection();
String sql = "select * from acount";
//预编译sql
prep = conn.prepareStatement(sql);
rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", name:" + rs.getString("name") + ", amount:" + rs.getDouble("amount"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/*
11月 15, 2019 8:47:47 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
id:1, name:李茂贞1, amount:0.0
id:2, name:李茂贞2, amount:20000.0
id:3, name:李茂贞3, amount:10000.0
id:4, name:李茂贞4, amount:10000.0
id:5, name:袁天罡, amount:10000.0
Process finished with exit code 0
*/
-
C3P0开源连接池
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。
c3p0手动设置参数的方式
/*
*手动设置参数
* */
public class C3p0Demo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
//获取连接
//创建连接池
ComboPooledDataSource pooledDataSource = new ComboPooledDataSource();
pooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
pooledDataSource.setJdbcUrl("jdbc:mysql:///test");
pooledDataSource.setUser("root");
pooledDataSource.setPassword("123456");
//从连接池中获取连接
conn = pooledDataSource.getConnection();
String sql = "select * from acount";
//预编译sql
prep = conn.prepareStatement(sql);
rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", name:" + rs.getString("name") + ", amount:" + rs.getDouble("amount"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/*
11月 15, 2019 9:12:18 上午 com.mchange.v2.log.MLog <clinit>
信息: MLog clients using java 1.4+ standard logging.
11月 15, 2019 9:12:19 上午 com.mchange.v2.c3p0.C3P0Registry banner
信息: Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
11月 15, 2019 9:12:19 上午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hgemf5a6u4yeap1jpwzow|c39f790, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hgemf5a6u4yeap1jpwzow|c39f790, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql:///test, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {password=******, user=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
id:1, name:李茂贞1, amount:0.0
id:2, name:李茂贞2, amount:20000.0
id:3, name:李茂贞3, amount:10000.0
id:4, name:李茂贞4, amount:10000.0
id:5, name:袁天罡, amount:10000.0
Process finished with exit code 0
*/
c3p0配置文件设置参数的方式
注意:ComboPooledDataSource会去默认路径下查找配置文件c3p0-config.xml,默认路径为src目录下
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--默认配置-->
<default-config>
<property name="initialPoolSize">5</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">20</property>
</default-config>
<!--配置连接池mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">20</property>
</named-config>
<!--配置连接池2-->
......
<!--配置连接池3-->
......
<!--配置连接池4-->
</c3p0-config>
更细致化的有以下配置:
<!--acquireIncrement:链接用完了自动增量3个。 -->
<property name="acquireIncrement">3</property>
<!--acquireRetryAttempts:链接失败后重新试30次。-->
<property name="acquireRetryAttempts">30</property>
<!--acquireRetryDelay;两次连接中间隔1000毫秒。 -->
<property name="acquireRetryDelay">1000</property>
<!--autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。 -->
<property name="autoCommitOnClose">false</property>
<!--automaticTestTable:c3p0测试表,没什么用。-->
<property name="automaticTestTable">Test</property>
<!--breakAfterAcquireFailure:出错时不把正在提交的数据抛弃。-->
<property name="breakAfterAcquireFailure">false</property>
<!--checkoutTimeout:100毫秒后如果sql数据没有执行完将会报错,如果设置成0,那么将会无限的等待。 -->
<property name="checkoutTimeout">100</property>
<!--connectionTesterClassName:通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>
<!--factoryClassLocation:指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可。-->
<property name="factoryClassLocation">null</property>
<!--forceIgnoreUnresolvedTransactions:作者强烈建议不使用的一个属性。-->
<property name="forceIgnoreUnresolvedTransactions">false</property>
<!--idleConnectionTestPeriod:每60秒检查所有连接池中的空闲连接。-->
<property name="idleConnectionTestPeriod">60</property>
<!--initialPoolSize:初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。 -->
<property name="initialPoolSize">3</property>
<!--maxIdleTime:最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。-->
<property name="maxIdleTime">60</property>
<!--maxPoolSize:连接池中保留的最大连接数。 -->
<property name="maxPoolSize">15</property>
<!--maxStatements:最大链接数。-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection:定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection"></property>
<!--numHelperThreads:异步操作,提升性能通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads">3</property>
<!--overrideDefaultUser:当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0的数据源时。Default: null-->
<property name="overrideDefaultUser">root</property>
<!--overrideDefaultPassword:与overrideDefaultUser参数对应使用的一个参数。Default: null-->
<property name="overrideDefaultPassword">password</property>
<!--password:密码。Default: null-->
<property name="password"></property>
<!--preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意: 测试的表必须在初始数据源的时候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>
<!--propertyCycle:用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>
<!--testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。Default: false -->
<property name="testConnectionOnCheckout">false</property>
<!--testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
<!--user:用户名。Default: null-->
<property name="user">root</property>
<!--usesTraditionalReflectiveProxies:动态反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>
代码演示
public class C3p0Auto {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
//创建连接池,ComboPooledDataSource去默认路径下查找配置文件c3p0-config.xml
//加载默认配置
//ComboPooledDataSource dataSource = new ComboPooledDataSource();
//加载msyql配置
ComboPooledDataSource pooledDataSource = new ComboPooledDataSource("mysql");
//从连接池中获取连接
conn = pooledDataSource.getConnection();
String sql = "select * from acount";
//预编译sql
prep = conn.prepareStatement(sql);
rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", name:" + rs.getString("name") + ", amount:" + rs.getDouble("amount"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
c3p0工具类
public class C3p0Util {
// 创建一个连接池:但是这个连接池只需要创建一次即可。
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
//获得连接的方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//获取连接池的方法
public static ComboPooledDataSource getDataSource() {
return dataSource;
}
//归还连接
public static void close(Connection conn, PreparedStatement prep) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//归还连接
public static void close(Connection conn, PreparedStatement prep, ResultSet rs) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
//从工具类的连接池中获取连接
conn = C3p0Util.getConnection();
String sql = "select * from acount";
//预编译sql
prep = conn.prepareStatement(sql);
rs = prep.executeQuery();
while (rs.next()) {
System.out.println("id:" + rs.getInt("id") + ", name:" + rs.getString("name") + ", amount:" + rs.getDouble("amount"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
C3p0Util.close(conn, prep, rs);
}
}
}
-
DBUtils
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
为什么要学习DBUtils
因为JDBC手写比较麻烦,而且有非常多的代码是类似的。比如获得连接,预编译SQL,释放资源等..那么可以将这些代码抽取出来放到工具类中。将类似的代码进行抽取。大大简化JDBC的编程。
-
DBUtils增删改
public class C3p0Util {
// 创建一个连接池:但是这个连接池只需要创建一次即可。
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
//获得连接的方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//获取连接池的方法
public static DataSource getDataSource() {
return dataSource;
}
...
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//插入数据
queryRunner.update("insert into acount values(6, ?, ?)", "李淳风", 20000);
//修改数据
queryRunner.update("update acount set amount=? where id=?", 30000, 5);
//删除数据
queryRunner.update("delete from acount where id=2");
}
-
DBUtils查询
匿名内部类的方式实现ResultSetHandler重写handle方法
public class Acount {
private int id;
private String name;
private double amount;
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 getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
@Override
public String toString() {
return "Acount{" +
"id=" + id +
", name='" + name + '\'' +
", amount=" + amount +
'}';
}
}
public class dbutilsDemo {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//查询一条数据
Acount acount = queryRunner.query("select * from acount where id=?", new ResultSetHandler<Acount>() {
@Override
public Acount handle(ResultSet resultSet) throws SQLException {
Acount acount1 = new Acount();
while (resultSet.next()) {
acount1.setId(resultSet.getInt("id"));
acount1.setName(resultSet.getString("name"));
acount1.setAmount(resultSet.getInt("amount"));
}
return acount1;
}
}, 3);
System.out.println(acount);
System.out.println("-----------------------");
//查询多条数据
List<Acount> listAcount = queryRunner.query("select * from acount", new ResultSetHandler<List<Acount>>() {
@Override
public List<Acount> handle(ResultSet rs) throws SQLException {
List<Acount> list = new ArrayList<>();
while (rs.next()) {
Acount acount1 = new Acount();
acount1.setId(rs.getInt("id"));
acount1.setName(rs.getString("name"));
acount1.setAmount(rs.getInt("amount"));
list.add(acount1);
}
return list;
}
});
for (Acount acount1 : listAcount) System.out.println(acount1);
System.out.println("-----------------------");
}
}
/*
Acount{id=3, name='李茂贞3', amount=10000.0}
-----------------------
Acount{id=1, name='李茂贞1', amount=0.0}
Acount{id=3, name='李茂贞3', amount=10000.0}
Acount{id=4, name='李茂贞4', amount=10000.0}
Acount{id=5, name='袁天罡', amount=30000.0}
Acount{id=6, name='李淳风', amount=20000.0}
-----------------------
*/
-
ResultSetHandler实现类 BeanHandler和BeanListHandler(重要)
BeanHandler
将一条记录封装到一个JavaBean中。
BeanListHandler
将多条记录封装到一个装有JavaBean的List集合中。
将结果集封装成对应类的对象返回
public class Acount {
private int id;
private String name;
private double amount;
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 getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
@Override
public String toString() {
return "Acount{" +
"id=" + id +
", name='" + name + '\'' +
", amount=" + amount +
'}';
}
}
public class dbutilsDemo {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//查询一条数据
Acount acountResult = queryRunner.query("select * from acount where id=?", new BeanHandler<Acount>(Acount.class), 5);
System.out.println(acountResult);
//查询多条数据
List<Acount> acounts = queryRunner.query("select * from acount", new BeanListHandler<Acount>(Acount.class));
for (Acount acount : acounts) System.out.println(acount);
System.out.println("-----------------------");
}
}
/*
Acount{id=5, name='袁天罡', amount=30000.0}
Acount{id=1, name='李茂贞1', amount=0.0}
Acount{id=3, name='李茂贞3', amount=10000.0}
Acount{id=4, name='李茂贞4', amount=10000.0}
Acount{id=5, name='袁天罡', amount=30000.0}
Acount{id=6, name='李淳风', amount=20000.0}
-----------------------
*/
-
ResultSetHandler实现类 ArrayHandler和ArrayListHandler
ArrayHandler
将一条记录封装到一个数组当中。这个数组应该是Object[]。
ArrayListHandler
将多条记录封装到一个装有Object[]的List集合中。
public class dbutilsDemo {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//查询一条数据
Object[] ArrObj = queryRunner.query("select * from acount where id=?", new ArrayHandler(), 3);
System.out.println(Arrays.toString(ArrObj));
System.out.println("-----------------------");
//查询多条数据
List<Object[]> listObjArr = queryRunner.query("select * from acount", new ArrayListHandler());
for (Object[] objArr : listObjArr) System.out.println(Arrays.toString(objArr));
System.out.println("-----------------------");
}
}
/*
[3, 李茂贞3, 10000]
-----------------------
[1, 李茂贞1, 0]
[3, 李茂贞3, 10000]
[4, 李茂贞4, 10000]
[5, 袁天罡, 30000]
[6, 李淳风, 20000]
-----------------------
*/
-
ResultSetHandler实现类 MapHandler和MapListHandler
MapHandler
将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值。
MapListHandler
将多条记录封装到一个装有Map的List集合中。
public class dbUtilsMap {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//查询一条记录
Map<String, Object> mapResult = queryRunner.query("select * from acount where id=?", new MapHandler(), 5);
System.out.println(mapResult);
System.out.println("-------------------------");
//查询多条记录
List<Map<String, Object>> listMap = queryRunner.query("select * from acount", new MapListHandler());
for(Map<String, Object> map1: listMap) System.out.println(map1);
}
}
/*
{amount=30000, name=袁天罡, id=5}
-------------------------
{amount=0, name=李茂贞1, id=1}
{amount=10000, name=李茂贞3, id=3}
{amount=10000, name=李茂贞4, id=4}
{amount=30000, name=袁天罡, id=5}
{amount=20000, name=李淳风, id=6}
*/
-
ResultSetHandler实现类 ColumnListHandler、ScalarHandler、KeyedHandler
ColumnListHandler
将数据中的某列封装到List集合中。
public class dbUtilsMap {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//ColumnListHandler 将数据中的某列封装到List集合中。
List<Object> names = queryRunner.query("select id,name,amount from acount", new ColumnListHandler("name"));
System.out.println(names);
System.out.println("-------------------------");
}
}
/*
[李茂贞1, 李茂贞3, 李茂贞4, 袁天罡, 李淳风]
-------------------------
*/
ScalarHandler
将单个值封装。
public class dbUtilsMap {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//ScalarHandler 将单个值封装。
Object count = queryRunner.query("select count(*) from acount", new ScalarHandler());
System.out.println(count);
System.out.println("-------------------------");
}
}
/*
5
-------------------------
*/
KeyedHandler(了解)
将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。
public class dbUtilsMap {
public static void main(String[] args) throws SQLException {
QueryRunner queryRunner = new QueryRunner(C3p0Util.getDataSource());
//KeyedHandler(了解)将查询结果封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。
Map<Object, Map<String, Object>> mapResult = queryRunner.query("select * from acount", new KeyedHandler("id"));
System.out.println(mapResult);
System.out.println("-------------------------");
}
}
/*
//{1={amount=0, name=李茂贞1, id=1}, 3={amount=10000, name=李茂贞3, id=3}, 4={amount=10000, name=李茂贞4, id=4}, 5={amount=30000, name=袁天罡, id=5}, 6={amount=20000, name=李淳风, id=6}}
{
1={amount=0, name=李茂贞1, id=1},
3={amount=10000, name=李茂贞3, id=3},
4={amount=10000, name=李茂贞4, id=4},
5={amount=30000, name=袁天罡, id=5},
6={amount=20000, name=李淳风, id=6}
}
-------------------------
*/