JDBC入门教程

JDBC 概念集

概述

Java Database Connectivity (Java 语言连接数据库)

JDBC 的本质是SUN司指定的一条接口(interface)
java.sql.*

面向接口调用、面向接口写实现类,都属于面向接口编程。

为什么要面向接口编程?

解耦合:降低程序的耦合度,提高程序的扩展力。
​ 多态机制就是非常典型的面向抽象(接口)编程。(不要面向具体编程)

为什么SUN制定套JDBC接口?

​ 因为每一个数据库的底层实现原理不一样。
​ Oracle、MySQL、MS SqlServer 等等等都有自己的原理。
​ 每一个数据库产品都有自己独特嗯实现原理。

JDBC开发前的准备工作

先从官网下载对应的驱动 jar 包,然后将其配置到环境变量 classpath 中
使用 IDEA 工具时不需要配置以上环境变量。
IDEA 有自己的配置方式。

JDBC编程六步概述★

  1. 注册驱动:告诉 Java 程序即将要连接的是哪个品牌的数据库
  2. 获取连接:表示 JVM 的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的。使用完只后一定要关闭通道。
  3. 获取数据库操作对象(专门执行 sql 语句的对象)
  4. 执行 SQL 语句:DQL、DML …
  5. 处理查询结果:只有当第四步执行的是 select 语句的时候,才有这第五步
  6. 释放资源:使用完资源之后,一定要关闭资源。Java 和数据库属于进程间的通信,开起之后一定要关闭。

JDBC 基础

注册驱动与获取连接

链接:如何在idea中使用JDBC?

演示

注册驱动
// 多态,父类型引用指向子类型对象
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);

####获取连接

Connection connection = null;
try...catch
String url= "jdbc:mysql://127.0.0.1:3306/mysql?serverTimezone=UTC";
//“mysql”是数据库的名字,“?”后面的条件,建议也加上?useSSL=false
// String url = "jdbc:mysql://114.55.106.121:3306/learning";
String user = "------";
String password = "------";
connection = DriverManager.getConnection(url,user,password);
// 连接成功!连接对象 = com.mysql.cj.jdbc.ConnectionImpl@79698539
connection.close();

URL:统一资源定位符(网络中某个资源的绝对路径)。URL包括:协议、IP、PORT、资源名

例如:http://182.61.200.7:80/ind ex.html

  1. http:// 通信协议
  2. 182.61.200.7 服务器的IP地址
  3. 80 服务器上软件的端口
  4. index.html 服务器上的某个资源名

什么是通信协议?有什么用?
通信协议是通信之前就提前定好的数据传输格式
数据包具体怎么传输数据,格式是提前定好的

可能遇见的报错

时区问题

The server time zone value '�й���׼ʱ��' is unrecognize.

原因:
使用了Mysql Connector/J 6.x以上的版本,然后就报了时区的错误

解决办法:

​ 在配置url的时候不能简单写成:jdbc:mysql://localhost:3306/dbName

​ 而是要写成 :jdbc:mysql://localhost:3306/dbName?serverTimezone=UTC

Deprecated 问题

Loading class com.mysql.jdbc.Driver'.`` This is deprecated.

原因:
提示信息表明数据库驱动com.mysql.jdbc.Driver’已经被弃用了、应当使用新的驱动com.mysql.cj.jdbc.Driver’

解决办法:
.com.mysql.jdbc.Driver修改为.com.mysql.jc.jdbc.Driver

执行 SQL 语句与释放资源

执行SQL语句

  1. 获取数据库操作对象
    Statement:专门用来执行 SQL 语句的对象

    Statement stmt = null;
    stmt = connection.createStatement();
    stmt.close();
    
  2. 执行 SQL 语句

    Statement**.excuteUpdate(String sql)** :专门执行 DML 语句,返回值为“数据库中受影响的条数”

    String sql = "insert into employees(employee_id,last_name) values(99,'Cai')";
    int count = stmt.executeUpdate(sql);
    

注意: JDBC 中的 SQL 语句不提供分号结尾,否则容易报错

关闭资源

为了保证资源一定释放,因此将其放在finally语句中,并且依照从小到大分别对其try…catch

try {
	if (stmt != null) {
    	stmt.close();
	}
} catch (SQLException e) {
    e.printStackTrace();
}
try {
    if (connection != null) {
		connection.close();
	}
} catch (SQLException e) {
	e.printStackTrace();
}

注意事项

  1. 【注册、连接、获取对象、执行语句、处理语句】都可以一齐放在 SQLException的异常处理中,【资源关闭】则单独放在finally语句中。
  2. 关闭资源时,遵循从小到大的顺序关闭,且分别 try…catch
  3. 需要提前在 try…catch 外面声明 Connection、Statement 为null,否则关闭会受影响。

插入、删除与更新(DML)

excuteUpdate(insert|delete|update)  ---> int
package per.deletedemo;

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

public class Main {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        try {
            DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployees?serverTimezone=UTC", "root", "212270");
            String sql = "delete from employees where employee_id = 99";
            stat = conn.createStatement();
            assert stat != null;
            int count = stat.executeUpdate(sql);
            System.out.println(count == 1 ? "删除成功" : "删除失败");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

以类加载的方式注册驱动

注册驱动的另一种方式(常用方式)

// 第一种注册方式
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

// 第二种注册方式 --> 通过反射来进行加载 --> 更为常用
Class.forName("com.mysql.cj.jdbc.Driver");

为什么更为常用?
因为参数是一个字符串,即可以写到 xxx.properties 文件中

注意 第二种方法不需要结束返回值,因为我们只需要他的类加载动作完成其静态方法注册驱动

从属性资源文件中读取连接数据库信息

ResourceBundle bundel = ResourceBundle.getBundle("jdbc");
String driver = bundel.getString("driver");
String url = bundel.getString("url");
String user = bundel.getString("user");
String password = bundel.getString("password");

注意 连接中不建议把连接数据库的信息写死到 Java 程序中。

注意 在 properties 文件中,以键值对形式保存,不加双引号!!!

处理查询结果集(DQL)

excuteQuery(select) ---> ResultSet
boolean next() throws SQLException
    // 如果新的当前行有效,则返回 true
    // 如果不存在下一行,则返回 false
String getString(Integer index|String str) throws SQLException
    // 不管什么值,都以 String 返回
    // JDBC 中的下标从 1 开始
    // 也可以直接用查询结果的列名获取
Integer getInt(...)
Double getDouble(...)

注意 若 select 使用了别名,则 getString() 以列名查找时必须以别名为参数。(即以返回的列表为准)

ResultSet rs = null;
rs = stmt.executeQuery(sql);
// 开始读取 rs
String e1 = null;
Integer e2 = null;
while (rs.next()) {
	e1 = rs.getString(1); // 1可以用列名代替
	e2 = rs.getInt(2);    // 数字可以用select中的列名代替 
}
rs.close();

JDBC 实战

用户登录业务介绍

  1. 需求:
    模拟用户登录功能的实现
  2. 需求描述:
    程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码。
    用户提交用户名和密码之后,提交信息,java程序收集到用户信息
    Java程序连接数据库验证用户名和密码是否合法,合法则显示登录成功
  3. 数据的准备:
    在实际开发中,表的设计会使用专业的建模工具—>PowerDesigner
    使用 PD 工具来进行数据库表的设计

使用PowerDesigner进行物理建模

image-20200504101919052image-20200504102042590

用户登录–SQL注入

问题引出

String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");

String sql = "select * from t_user where loginName ='" + loginName
                    + "' and loginPwd='" + loginPwd + "'";
rs = stmt.executeQuery(sql);

if (rs.next()) {
	loginSuccess = true;
}

方法存在的漏洞:这种现象称为 sql 注入(安全隐患)

用户名:jake
密码:kejk' or '1' = '1
// 返回 true

导致sql注入的根本原因
用户输入的信息中含有 sql 语句的关键字,并且这些关键字参与 sql 语句的编译过程。
导致 sql 语句的原意被扭曲,进而达到 sql 注入。

String sql = "select * from t_user where loginName ='jake' and loginPwd='kejk' or '1' = '1' ";
// 以上正好完成了sql语句的拼接
// 以下代码的含义是,发送SQL语句给DBMX,DBMX进行sql编译
rs = stmt.executeQuery(sql);
// 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲

如何解决?

解决思路:

只需要用户提供的信息不参与 sql 的编译过程
要想用户消息不参与 sql 语句的编译,那么必须使用 java.sql.PreparedStatement接口,其继承了java.sql,Statement,属于预编译的数据库操作对象

// Statement stmt = null;
PreparedStatement ps = null;

// stmt = conn.createStatement();
// String sql = "select * from t_user where loginName ='" 
//     + loginName +"' and loginPwd='" + loginPwd + "'";
// rs = stmt.executeQuery(sql);
String sql = "select * from t_user where loginName = ? and loginPwd= ? ";
ps = conn.prepareStatement(sql);	// 传框架
ps.setString(1, loginName);		// 开始传值
ps.setString(2, loginPwd);
rs = ps.executeQuery();		// 开始执行

注意

  1. 使用 conn.prepareStatement(sql)将 sql 语句的框架发送给 DBMS 进行预编译
  2. sql 语句中使用占位符?,一个占位符代表一个值
    占位符不要使用单引号括起来
  3. 通过ps.setString(index,value)给占位符赋值
    注意:JDBC中的下标 index 从 1 开始

Statement & PreparedStatement

sql 注入问题效率编译阶段做类型的安全检查?
Statement未解决略低(编译一次执行一次)不会
PreparedStatement已解决略高(编译一次可执行n次)

综上所述:
PreparedStatement 使用较多,只有极少数情况会使用 Statement。

什么情况下必须使用 Statement 呢?
业务方面必须支持 SQL注入 的时候,Statement支持 SQL注入,支持 sql 语句的拼接。
比如选择“升序ASC”和“降序DESC”时,需要在用户端完成 sql 语句的拼接。

传值时使用PreparedStatement,在进行sql语句拼接的时候使用Statement

使用PreparedStatement完成增删改

String sql ="insert into t_user(loginName,loginPwd) values(?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1,"cai");
ps.setString(2,"123");
int count = ps.executeUpdate();
System.out.println(count);

JDBC的事务自动提交

介绍

JDBC 中的事务是自动提交的。
即只要执行任意一条 DML 语句,则会自动提交。这是 JDBC 默认的事务行为。
但是在实际的开发中,通常都是 N 条 DML 语句共同联合才能完成的
必须保证这些 DML 语句在同一个事务中同时成功或同时失败。

功能使用

try{
conn.setAutoCommit(false);	// 关闭自动提交,开始事务
/*
 * 这里是事务内容,即需要提交的代码
 */
conn.commit();		// 提交代码,完成事务
} catch (Exception e) {
//--------出现 Exception 则回滚事务----------
    if (conn == null) {
        try {
            conn.rollback();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
    e.printStackTrace();
} finally {...}
conn.setAutoCommit(false);
conn.commit();
Exception ---> conn.rollback();

隔离级别

获取隔离级别:

conn.getTransactionIsolation(); ---> Integer

设置隔离级别:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBl1g40Y-1610006137627)(image-20200508220902395.png)]

JDBC工具类的封装★

package per.util;

import java.sql.*;

/**
 * JDBC工具类,简化JDBC编程
 */
public class DBUTil {
    
    // 静态代码块在类加载时执行一次,并且只执行一次
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 工具类中的构造方法都是私有的
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
     */
    private DBUTil() {
    }

    public static Connection getConnection() throws SQLException {
        // 外部调用有 try...catch ,故此处只需扔出异常
        return DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"
                , "root", "212270");
    }
    /**
     * 关闭资源
     *
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs   结果集
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {		// 判断 null 之后
            try {				// 如果没有使用到 ResultSet,则可直接第三个参数为 null
                rs.close();		// 由此可以避免使用重载,便于简化程序
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试工具类

public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        // 获取连接
        conn = DBUTil.getConnection();
        // 获取预编译的数据库操作对象
        String sql = "select last_name from employees where last_name like ? limit 0,5";
        ps = conn.prepareStatement(sql);
        ps.setString(1,"_a%");
        rs = ps.executeQuery();
        while(rs.next()){
            System.out.println(rs.getString("last_name"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        // 释放资源
        DBUTil.close(conn,ps,rs);
    }
}

JDBC的增删改通用写法

写法一

// 传入个数可变
public static void comUpdate(String sql, Object... args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 获取连接
            conn = DBUTil.getConnection();
            // 获取数据库对象
            ps = conn.prepareStatement(sql);
            // 传值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            // 执行
            int count = ps.executeUpdate();
            System.out.println("Changed Rows = " + count);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            DBUTil.close(conn,ps,null);
        }
    }
String sql = "delete from employees where last_name = ?";
comUpdate(sql,"cai");

写法二

/**
 * 通用的查询操作,v2.0,考虑了事务
 * @param conn 连接对象
 * @param clazz 对象
 * @param sql SQL语句
 * @param args 传值
 * @param <T> 对象的类型
 * @return 返回一个包含信息的对象
 */
public static  <T>T getInstance(Connection conn,Class<T> clazz,String sql,Object... args){
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        ps = conn.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i+1,args[i]);
        }
        rs = ps.executeQuery();
        // 获取结果集的元数据
        ResultSetMetaData rsmd = rs.getMetaData();
        // 通过ResultSetMetadata获取结果集中的列数
        int columnCount = rsmd.getColumnCount();
        if(rs.next()){
            T t = clazz.newInstance();
            // 处理结果集一行数据中的每一个列
            for (int i = 0; i < columnCount; i++) {
                // 获取列值
                Object columnValue = rs.getObject(i + 1);
                // 获取每个列的列名
                String columnLabel = rsmd.getColumnLabel(i + 1);
                // 给 t 对象指定的 columnName属性赋值为columnValue:通过反射
                Field field = clazz.getDeclaredField(columnLabel);
                field.setAccessible(true);
                field.set(t, columnValue);
            }
            return t;
        }
    } catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
        e.printStackTrace();
    } finally {
        DBUTil.close(null,ps,rs);

    }
    return null;
}

操作Blob类型字段

Blob类型数据

  • MYSQLE中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
  • 插入BLOB类型的数据必须使用 Preparedstatement,因为BLOB类型的数据无法使用字符串拼接写的。
  • My5QL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
类型最大字节
TinyBlob255 b
Blob65 Kb
MediumBlob16 Mb
LongBlob4 Gb
  • 实际使用中根据需要存入的数据大小定义不同的BLOB类型。
  • 注意:如果存储的文件过大,数据库的性能会下降。
  • 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysq的安装目录下,找 my inn文件加上如下的配置参数: max allowed packet=16M
    注意:修改了myin文件之后,需要重新启动 mysql服务

##上传Blob

ps = conn.PreparedStatment(sql);

//ps.setBlob(index,inputStream);
InputStream ins = new FileInputStream(new File("path"));	// FileNotFoundException
ps.setBlob(index,ins);

ins.close(); // IOException

下载Blob

// 从数据库读入 Blob
while(rs.next()){
    Blob photo = rs.getBlob("photo");
    InputStream ins = photo.getBinaryStream();
    OutputStream ous = new FileOutputStream(new File("path"));
    // 开始读数据
    byte[] buffer = new byte[1024];
    Integer len = 0;
    while((len = ins.read(buffer)) != -1){
        ous.write(buffer,0,len);
    }
}

ins.close();
ous.close();

批量插入数据

方式一、PreparedStatement

String sql = "insert into learning.temp_test(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 100000; i++) {
    ps.setString(1, "name" + i);
    ps.executeUpdate();
}
// 耗费时间约为 400s

方式二、Butch()

conn = DBUTil.getConnection();
String sql = "insert into learning.temp_test(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 100000; i++) {
    ps.setString(1, "name" + i);
    // ---------------------------------------------
    // 1、攒 sql
    ps.addBatch();
    if (i % 500 == 0) {
        // 2、执行
        ps.executeBatch();
        // 3、清空
        ps.clearBatch();
    }
}
// 耗费时间约为 16s左右

注意 MySQL服务器默认是关闭批处理的,我们需要一个参数让服务器开启对批处理的支持。
?rewriteBatchedStatements=true写在配置文件的url后面

注意 批处理需要使用MySQL版本驱动为及以上的:mysq1-connector-java-5.1.37-bin.jar

方式三:结合事务提交

conn = DBUTil.getConnection();
// 开启事务
conn.setAutoCommit(false);
String sql = "insert into learning.temp_test(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 100000; i++) {
    ps.setString(1, "name" + i);
    ps.addBatch();
    if (i % 500 == 0) {
        ps.executeBatch();
        ps.clearBatch();
    }
}
// 提交事务
conn.commit();
// conn.rollback();

// 耗费时间约为5s

Java&SQL 数据类型

##Java与SQL对应数据类型转换表

Java类型SQL类型
booleanBIT
byteTINYINT
shortSMALLINT
intINTEGER
longBIGINT
StringCHAR, VARCHAR, LONGVARCHAR
byte arrayBINARY, VAR BINARY
java.sql.DateDATE
java.sql.TimeTIME
java.sql.TimestampTIMESTAMP

##Java 如何存取MySQL datetime类型

Java 如何存取MySQL datetime类型

提取数据

​ 如果要从MySQL中获取yyyy-MM-dd HH:mm:ss日期格式,首先必须使用 rs.getTimestamp("insert_dt")方法,其中"insert_dt" 是数据库时间字段,类型为datetime;然后通过SimpleDateFormat 时间格式化类,将取出来的时间转为String类型

先来对比rs获取不同日期时间格式的方法

System.out.println(rs.getDate("insert_dt"));	// 2018-03-19
System.out.println(rs.getTime("insert_dt"));	// 22:03:21
System.out.println(rs.getTimestamp("insert_dt")); // 2018-03-19 22:03:21.0

可以看到通过getTimestamp获取的日期格式最后还有一位数,需要将rs.getTimestamp("insert_dt")转为String类型

 String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
     .format(rs.getTimestamp("insert_dt"));
 System.out.println(timeStamp);
// 2018-03-19 21:51:57

存储数据

从前端或者自己模拟一个日期格式,转为String即可

Date date = new Date();  
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = format.format(date);
DBUtil.update(sql,dateStr);

DAO及其实现类

  • DAO: Data Access Object,访问数据信息的类和接口
    包括了对数据的CRUD( Create、 Retrival、 Update、Delete)
    而不包含任何业务相关的信息。有时也称作: BASEDAO
  • 作用:为了实现功能的模块化,更有利于代码的维护和升级。

内容较多,直接看Idea代码及注释理解

数据库连接池

JDBC数据库连接池的必要性

在使用开发基于数据库的Web程序时,传统的模式基本是按以下步骤:

  1. 在主程序(如 servlet、 beans)中建立数据库连接
  2. 进行 sql 操作
  3. 断开数据库连接

这种开发模式存在的问题

  • 普通的DBC数据库连接使用 Drivermanager来获取,每次向数据库建立连接的时候都要将 Connection
    加载到内存中,再验证用户名和密码(得花费0.05S~1s的时间)。需要数据库连接的时候,就向数据库要求
    一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用。若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
  • 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存池漏,最终将导致重启数据库。(何为java的内存泄漏?内存不能被回收 销毁)
  • 这种开发不能控制被创建的连接对象数,系统资源会被亳无顾及的分配出去,如连接过多,也可能导致内
    存泄漏,服务器崩溃。

数据库连接池技术

  • 为解决传统开发中的数据库连接问题,可以采用数据库连接池技术
  • 数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从"缓冲池"中取出一个,使用完毕之后再放回去。
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
  • 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中

工作原理

image-20200509144551696

数据库连接池的优点

  1. 资源重用
  2. 更快的系统反应速度
  3. 新的资源分配手段
  4. 统一的连接管理,避免数据库连接泄露

多种开源的数据库连接池

JDBC的数据库连接池使用 Javax. sql Datasource来表示, Datasource只是一个接口,该接口通常由服务器weblogic, Websphere, Tomcat)提供实现,也有一些开源组织提供实现

C3P0

是一个开源组织提供的一个数据库连接池,速度相对较慢稳定性还可以。 hibernate官方推荐使用

DBCP

是Apache提供的数据库连接池。Tomcat自带DBCP连接池。数独相对C3P0较快,但因自身存在bug,Hibernate3已经不再提供支持

Druid(德鲁伊)

(开发中主要使用)是阿里提供的数据库连接池,据说是集DBCP、C3P0、 Proxool 优点于一身的数据库连接池,但是速度不确定是否有 Bonecp快

C3P0的两种数据库实现方式

try {
    // 获取c3p0数据库连接池
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass("com.mysql.jdbc.Driver");
    cpds.setJdbcUrl("jdbc:mysql://localhost:3306/myemployees?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=UTC");
    cpds.setUser("root");
    cpds.setPassword("212270");

    // 通过设置相关的参数对数据库连接池进行管理
    // 设置初始的数据库连接池中的连接数
    cpds.setInitialPoolSize(10);

    Connection conn = cpds.getConnection();
    System.out.println(conn);

    // 销毁连接池(一般不关连接池)
    DataSources.destroy(cpds);
} catch (Exception e){
    e.printStackTrace();
}

方式二

ComboPooledDataSource cpds = new ComboPooledDataSource("helloC3P0");
try {
    Connection connection = cpds.getConnection();
    System.out.println(connection);
} catch (SQLException e) {
    e.printStackTrace();
}
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
    <named-config name="helloC3P0">
        <!--  提供获取连接的4个基本信息  -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/myemployees?useUnicode=true&amp;characterEncoding=utf8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=false&amp;serverTimezone=UTC
        </property>
        <property name="user">root</property>
        <property name="password">212270</property>

        <!--  进行数据库连接池管理的基本信息  -->
        <!--  当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数  -->
        <property name="acquireIncrement">5</property>
        <!--  c3p0数据库连接池中初始化时的连接数  -->
        <property name="initialPoolSize">10</property>
        <!--  数据库连接池中维护的最少(多)连接数  -->
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <!--  数据库连接池中维护的Statement的最大个数  -->
        <property name="maxStatements">50</property>
        <!--  每个连接最多可使用的Statement个数  -->
        <property name="maxStatementsPerConnection">2</property>
    </named-config>
</c3p0-config>

Apache-DBUtils

package per.connection.queryrunner;

import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.*;
import per.bean.Customer;
import per.util.DBUTil;
import per.util.DBUTilDruid;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author CaiKe
 * @create 2020/5/10
 * commons-dbutils 是 Apache组织提供的一个开源的 JDBC 工具类库,封装了针对于数据库的增删查改操作
 */
public class QueryRunnerTest {
    public static void main(String[] args) {
        try {
//            testListQuery();
//            testMapQuery();
//            testMapListQuery();
//            testGetValue();
            testSelfDefinedQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 增删改
     *
     * @throws SQLException SQL
     */
    public static void testInsert() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "insert into customers(name,email,birth) values(?,?,?)";
        int count = runner.update(conn, sql, "李莉莉", "lylyly@126.com", "2018-02-19");
        System.out.println("Changed Rows:" + count);

        DBUTil.close(conn, null, null);
    }

    /**
     * BeanHandler:是ResultSetHandler接口的实现类,用于封装表中的一条记录
     *
     * @throws SQLException SQl
     */
    public static void testQuery() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "select id,name,email,birth from customers where id = ?";
        //  BeanHandler 即对象Handler,传入对象Customer
        BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
        Customer cus = runner.query(conn, sql, handler, 5);
        System.out.println(cus);

        DBUTil.close(conn, null, null);
    }

    /**
     * BeanListHandler:用于封装表中的多条对象记录构成的集合
     *
     * @throws SQLException SQL
     */
    public static void testListQuery() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "select id,name,email,birth from customers where id < ?";
        //  BeanListHandler 即对象Handler,传入对象Customer,返回一个List
        BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
        List<Customer> customerList = runner.query(conn, sql, handler, 5);
        System.out.println(customerList);

        DBUTil.close(conn, null, null);
    }

    /**
     * MapHandler:将字段即值作为Map中的键和值
     *
     * @throws SQLException SQL
     */
    public static void testMapQuery() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "select id,name,email,birth from customers where id = ?";
        MapHandler handler = new MapHandler();
        Map<String, Object> map = runner.query(conn, sql, handler, 5);
        System.out.println(map);

        DBUTil.close(conn, null, null);
    }

    /**
     * MapList:对应表中的多条记录,每条记录封装为 map
     *
     * @throws SQLException SQL
     */
    public static void testMapListQuery() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "select id,name,email,birth from customers where id < ?";
        MapListHandler handler = new MapListHandler();
        List<Map<String, Object>> mapList = runner.query(conn, sql, handler, 5);
        for (Map<String, Object> map : mapList) {
            System.out.println(map);
        }

        DBUTil.close(conn, null, null);
    }

    /**
     * ScalarHandler:用于查询特殊值
     *
     * @throws SQLException SQL
     */
    public static void testGetValue() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();
        String sql = "select count(*) from customers";

        ScalarHandler<Object> handler = new ScalarHandler<>();
        Long count = (Long) runner.query(conn, sql, handler);
        System.out.println(count);

        DBUTil.close(conn, null, null);
    }


    /**
     * ResultSetHandler:自己造一个返回,需要覆写返回方法
     * @throws SQLException SQL
     */
    public static void testSelfDefinedQuery() throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = DBUTilDruid.getConnection();

        String sql = "select id,name,email,birth from customers where id = ?";
        ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
            @Override
            public Customer handle(ResultSet resultSet) throws SQLException {
                System.out.println("这部分是自定义的返回值。");
//                return new Customer(14,"haha","haha@125.com",new Date(4324324324L));

                if(resultSet.next()){
                    int id = resultSet.getInt("id");
                    String name = resultSet.getString("name");
                    String email = resultSet.getString("email");
                    Date birth = resultSet.getDate("birth");
                    return new Customer(id,name,email,birth);
                } else{
                    return null;
                }
            }
        };
        Customer cus = runner.query(conn, sql, handler, 4);
        System.out.println(cus);
        DBUTil.close(conn, null, null);
        DbUtils.closeQuietly(conn);
    }
}

= DBUTilDruid.getConnection();

    String sql = "select id,name,email,birth from customers where id = ?";
    ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
        @Override
        public Customer handle(ResultSet resultSet) throws SQLException {
            System.out.println("这部分是自定义的返回值。");

// return new Customer(14,“haha”,“haha@125.com”,new Date(4324324324L));

            if(resultSet.next()){
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                Date birth = resultSet.getDate("birth");
                return new Customer(id,name,email,birth);
            } else{
                return null;
            }
        }
    };
    Customer cus = runner.query(conn, sql, handler, 4);
    System.out.println(cus);
    DBUTil.close(conn, null, null);
    DbUtils.closeQuietly(conn);
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值