java基础-10 Jdbc

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方法描述
voidaddBatch​()

向这个 PreparedStatement对象的一批命令添加一组参数。

booleanexecute​()

执行此 PreparedStatement对象中的SQL语句,可能是任何类型的SQL语句。

ResultSetexecuteQuery​()

执行此 PreparedStatement对象中的SQL查询,并返回查询生成的 ResultSet对象。

intexecuteUpdate​()

执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,比如INSERTUPDATEDELETE ; 或不返回任何内容的SQL语句,例如DDL语句。

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

voidaddBatch​(String sql)

将给定的SQL命令添加到此 Statement对象的当前命令列表中。

voidclearBatch​()

清空这个 Statement对象当前的SQL命令列表。

int[]executeBatch​()

将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组。

voidclearBatch​()

清空这个 Statement对象当前的SQL命令列表。

/*
 * 批处理的操作
 */
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方法描述
voidaddBatch​()

向这个 PreparedStatement对象的一批命令添加一组参数。

voidclearBatch​()

清空这个 Statement对象当前的SQL命令列表。

voidclearBatch​()

清空这个 Statement对象当前的SQL命令列表。

ResultSetexecuteQuery​()

执行此 PreparedStatement对象中的SQL查询,并返回查询生成的 ResultSet对象。

intexecuteUpdate​()

执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,比如INSERTUPDATEDELETE ; 或不返回任何内容的SQL语句,例如DDL语句。

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的事务管理

  1. 事务的概念:

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

  • 事务的提交与回滚

Modifier and Type方法
voidsetAutoCommit​(boolean autoCommit)

将此连接的自动提交模式设置为给定状态。

voidsetAutoCommit​(boolean autoCommit)

将此连接的自动提交模式设置为给定状态。

voidcommit​()

使上次提交/回滚之后所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。

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!");
    }
}

 

 

  • 连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。

连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。

为什么使用连接池:连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)。

连接池原理:

连接池的实现步骤:

  1. 编写一个类实现DataSource接口
  2. 重写getConnection方法
  3. 初始化多个连接在内存中
  4. 编写归还连接的方法
自定义连接池
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方法的逻辑改为归还。(增强一个类中的方法)。

  1. 如何增强一个类中的方法
    1. 一种:采用继承的方式:

***** 继承这种增强是最简单,但是是有使用条件的:必须能够控制这个类的构造!!!

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的编程。

  1. 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");
    }
  1. 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}
}
-------------------------
*/

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Toroidals

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值