JDBC

来源:互联网 发布:人力资源数据分析模型 编辑:程序博客网 时间:2024/06/08 15:56

1、JDBC的概念

JDBC:Java Database Connectivity
Java和数据库的连接

SUN公司推出的一套用于Java应用程序访问数据库的规范(一组接口或抽象类)

java.sql包
Javax.sql包

2、JDBC的应用

⑴ 应用程序访问数据库的原因
因为需要应用程序的数据持久化到数据库,所以需要通过一些方法实现数据库数据的存取

⑵ JDBC的好处
① 提高了维护性
② 减轻了程序员的开发压力,只需要记住一套方法即可

⑶ 具体实现步骤
① 加载驱动
② 建立连接
③ 执行SQL语句
④ 关闭连接

3、加载驱动

⑴ 首先需要导入jar包:
① 将mysql的驱动jar包复制到项目的根路径下
② 选中jar包→右键→Build Path→Add to Build Path

⑵ 加载驱动的方式有两种:
① 静态加载:通过DriverManager的registerDriver方法
② 动态加载:通过Class.forName方法

具体看下面DriverManager的registerDriver方法中的描述

4、JDBC URL

JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔
jdbc:子协议:子名称

协议:JDBC URL中的协议总是jdbc
子协议:子协议用于标识一个数据库驱动程序
子名称:包含主机名(对应服务端的ip地址),端口号,数据库名
JDBC URL
连接MySQL数据库的URL:
jdbc:mysql://localhost:3306/数据库名
可以简写成:jdbc:mysql:///数据库名

5、DriverManager【驱动管理类】

registerDriver

静态方法,使用方法:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());

注册驱动。但是该方法不建议使用
因为在com.mysql.jdbc.Driver类中,有一个静态代码块,源码:
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException(“Can’t register driver!”);
        }
    }
可以看到这里又调用了DriverManager.registerDriver()方法,相当于调用了两次这个方法,创建了两次对象,根本没有必要这样,所以该方法不建议使用

推荐使用反射的方式,动态地加载驱动
Class.forName(“com.mysql.jdbc.Driver”);

静态加载驱动的不足:
⑴ 静态加载属于编译加载,依赖性太强
⑵ Driver会加载两遍,影响效率
而动态加载则解决了这两个不足:
⑴ 依赖性被降低
⑵ Driver只会需要加载一遍

getConnection

获取连接对象,静态重载方法

⑴ public static Connection getConnection(String url)
需要传入一个数据库 url,url后面追加?user=用户名&password=密码
注意:?user=用户名&password=密码是固定格式,不能更改
示例:jdbc:mysql://localhost:3306/?user=用户名&password=密码

⑵ public static Connection getConnection(String url, java.util.Properties info)
需要传入一个数据库url和一个Properties对象。

注意:Properties对象需要加载一个包含user和password两个键值对的配置文件

⑶ public static Connection getConnection(String url,
String user, String password)
需要传入一个数据库url和数据库的用户名以及密码

获取Connection对象的四种方式

方式一

import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;public class Test {  public static void main(String[] args) throws ClassNotFoundException, SQLException {    // 1.加载驱动    Class.forName("com.mysql.jdbc.Driver");    // 2.建立对象【传入一个数据库 url,url后面追加?user=用户名&password=密码】    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/?user=用户名&password=密码");    // 3.关闭连接    connection.close();    System.out.println(connection);  }}

方式二

【info.properties配置文件】
user=用户名
password=密码

【连接代码】

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;public class Test {  public static void main(String[] args) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException {    // 1.加载驱动    Class.forName("com.mysql.jdbc.Driver");    // 2.加载配置文件    Properties info = new Properties();    info.load(new FileInputStream("src\\info.properties"));    // 3.建立对象【传入一个数据库url和一个Properties对象】    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/", info);    // 4.关闭连接    connection.close();    System.out.println(connection);  }}

方式三

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;public class Test {  public static void main(String[] args) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException {    // 1.加载驱动    Class.forName("com.mysql.jdbc.Driver");    // 2.建立对象【传入一个数据库url,用户名以及密码】    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/", "用户名", "密码");    // 3.关闭连接    connection.close();    System.out.println(connection);  }}

方式四【推荐】

【info.properties配置文件】
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306
user=用户名
password=密码

【连接代码】

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;public class Test {  public static void main(String[] args) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException {    // 1.加载配置文件    Properties info = new Properties();    info.load(new FileInputStream("src\\info.properties"));    String driverClass = info.getProperty("driverClass"); // 驱动全类名    String url = info.getProperty("url"); // 数据库url    String user = info.getProperty("user"); // 用户名    String password = info.getProperty("password"); // 密码    // 2.加载驱动    Class.forName(driverClass);    // 3.建立对象【传入一个数据库url和一个Properties对象】    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/", user, password);    // 4.关闭连接    connection.close();    System.out.println(connection);  }}

6、Connection【连接接口】

createStatement

Statement createStatement() throws SQLException;
用于获取执行SQL命令的对象

prepareStatement

PreparedStatement prepareStatement(String sql) throws SQLException;
用于获取预编译执行SQL命令的对象

7、Statement【执行SQL命令对象接口】

execute

boolean execute(String sql) throws SQLException;
可以执行任意SQL语句。返回值类型为boolean
如果执行查询,则返回true;如果执行增删改,则返回false

executeUpdate

int executeUpdate(String sql) throws SQLException;
执行增删改SQL语句。返回值类型为int
(1)对于SQL数据操作语言(DML)语句,返回行计数【也就是受影响的行数】
(2)对于什么都不返回的 SQL 语句,返回 0

executeQuery

ResultSet executeQuery(String sql) throws SQLException;
执行查询SQL语句。返回值类型为ResultSet【也就是结果集】

使用示例

【mytab数据表】
mysql> DESC mytab;
+——-+————-+
| Field | Type |
+——-+————-+
| id | int(11) |
| name | varchar(10) |
+——-+————-+

注意:这里使用了工具类【JDBCConnection 见下方】

【测试增删改】

  @Test  public void testUpdate() throws SQLException {    // 1.获取连接对象    Connection connection = JDBCConnection.getConnection();    // 2.获取执行SQL命令对象    Statement statement = connection.createStatement();    // String insertSql = "INSERT INTO test.mytab VALUES(1, '张三')";    // String updateSql = "UPDATE test.mytab SET name = '李四' WHERE id = 1";    // String deleteSql = "DELETE FROM test.mytab WHERE id = 1";    // 3.执行SQL语句    // int rows = statement.executeUpdate(insertSql);    // int rows = statement.executeUpdate(updateSql);    // int rows = statement.executeUpdate(deleteSql);    // if (0 < rows) {    // System.out.println("操作成功!");    // } else {    // System.out.println("操作失败!");    // }    // String truncateSql = "TRUNCATE TABLE test.mytab";    // int rows = statement.executeUpdate(truncateSql);    // System.out.println(rows); // 0    // boolean execute = statement.execute(insertSql); // false    // boolean execute = statement.execute(updateSql); // false    // boolean execute = statement.execute(deleteSql); // false    // boolean execute = statement.execute(truncateSql); // false    // System.out.println(execute);    // 4. 关闭连接等对象    JDBCConnection.closeConnection(null, statement, connection);  }

【先插入一些记录】
INSERT INTO mytab
SELECT 1, ‘张三’ UNION
SELECT 2, ‘李四’ UNION
SELECT 3, ‘王五’ UNION
SELECT 4, ‘赵六’ UNION
SELECT 5, ‘田七’;

【测试查询】

  @Test  public void testQuery() throws SQLException {    // 1.获取连接对象    Connection connection = JDBCConnection.getConnection();    // 2.获取执行SQL语句对象    Statement statement = connection.createStatement();    String sql = "SELECT * FROM test.mytab";    // 3.执行SQL语句,获取结果集对象【ResultSet】    ResultSet set = statement.executeQuery(sql);    while (set.next()) {      int id = set.getInt("id");      String name = set.getString(2);      System.out.println("Id: " + id + "\tName: " + name);    }    // 4.关闭连接等对象    JDBCConnection.closeConnection(set, statement, connection);  }

【输出结果】
Id: 1 Name: 张三
Id: 2 Name: 李四
Id: 3 Name: 王五
Id: 4 Name: 赵六
Id: 5 Name: 田七

8、PreparedStatement【预编译执行SQL命令对象接口】

PreparedStatement是Statement的子接口
public interface PreparedStatement extends Statement {}

execute

boolean execute() throws SQLException;
可以执行任意SQL语句。返回值类型为boolean
如果执行增删改语句,则返回false;如果执行查询语句,则返回true

executeUpdate

int executeUpdate() throws SQLException;
可以执行增删改SQL语句。返回值类型为int【即受影响的列的数量】

executeQuery

ResultSet executeQuery() throws SQLException;
可以执行查询SQL语句。返回值类型为ResultSet【即结果集】

setXXX(int parameterIndex, XXX x)

设置指定占位符的值,值的类型为XXX
第一个参数为占位符的索引位置,第二个参数为值
注意:占位符的索引从1开始

以String为例:
void setString(int parameterIndex, String x) throws SQLException;

setObject

void setObject(int parameterIndex, Object x) throws SQLException;
设置指定占位符的值,值的类型为Object
注意:占位符的索引位置从1开始

PreparedStatement和Statement的对比

⑴ PreparedStatement不用拼接SQL语句,而是用占位符(?)代替了插入的值的位置。有效的避免了SQL注入的问题
⑵ Statement是编译多次,运行多次
PreparedStatement采用了预编译功能。只需编译一次,就能运行多次,其效率高
⑶ PreparedStatement提高了代码的可阅读性,减少了语法的错误

使用示例

【mytab2数据表】
mysql> DESC mytab2;
+——–+————-+
| Field | Type |
+——–+————-+
| id | int(11) |
| name | varchar(10) |
| age | int(11) |
| gender | char(1) |
+——–+————-+

注意:这里使用了工具类【JDBCConnection 见下方】

【测试增删改】

  @Test  public void testUpdate() throws SQLException {    // 1.获取连接对象    Connection connection = JDBCConnection.getConnection();    //    String insertSql = "INSERT INTO test.mytab2 VALUES(?, ?, ?, ?)";    //    // 2.获取预处理执行SQL命令对象    //    PreparedStatement ps = connection.prepareStatement(insertSql);    //    // 3.填充占位符    //    ps.setInt(1, 1);    //    ps.setString(2, "张三");    //    ps.setInt(3, 17);    //    ps.setString(4, "男");    //    String updateSql = "UPDATE test.mytab2 SET name = ? WHERE id = ?";    //    PreparedStatement ps = connection.prepareStatement(updateSql);    //    ps.setString(1, "李四");    //    ps.setString(2, "1"); // 可以看到这里放String类型也是可以的    String deleteSql = "DELETE FROM test.mytab2 WHERE id = ?";    PreparedStatement ps = connection.prepareStatement(deleteSql);    ps.setObject(1, 1); // 填充类型为Object    // 4.执行SQL语句    int rows = ps.executeUpdate();    if (0 < rows) {        System.out.println("执行成功!");    } else {        System.out.println("执行失败!");    }    // 5.关闭连接等对象    JDBCConnection.closeConnection(null, ps, connection);  }

【先插入一些记录】
INSERT INTO mytab2
SELECT 1, ‘张三’, 19, ‘男’ UNION
SELECT 2, ‘李四’, 17, ‘女’ UNION
SELECT 3, ‘王五’, 21, ‘女’ UNION
SELECT 4, ‘赵六’, 14, ‘男’ UNION
SELECT 5, ‘田七’, 16, ‘女’;

【测试查询】

@Testpublic void testQuery() throws SQLException {  // 1.获取连接对象  Connection connection = JDBCConnection.getConnection();  String sql = "SELECT * FROM test.mytab2 WHERE age BETWEEN ? AND ?";  // 2.获取预处理SQL执行命令对象  PreparedStatement ps = connection.prepareStatement(sql);  // 3.填充占位符  ps.setInt(1, 14);  ps.setInt(2, 17);  // 4.得到结果集  ResultSet rs = ps.executeQuery();  while (rs.next()) {    int id = rs.getInt(1);    String name = rs.getString(2);    int age = rs.getInt(3);    String gender = rs.getString(4);    System.out.println(id + "\t" + name + "\t" + age + "\t" + gender);  }  // 5.关闭连接等对象  JDBCConnection.closeConnection(rs, ps, connection);}

【输出结果】
2 李四 17 女
4 赵六 14 男
5 田七 16 女

9、ResultSet【结果集接口】

next

boolean next() throws SQLException;
先判断指针的下面是否还有数据,如果有数据则返回true,同时指针下移一行;如果没有数据则返回false
ResultSet光标最初位于第一行之前;第一次调用next方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推
当调用next方法返回false时,光标位于最后一行的后面

afterLast

void afterLast() throws SQLException;
将指针移动到最后一行的后面。
注意:如果结果集中不包含任何行,则此方法无效。

previous

boolean previous() throws SQLException;
先判断指针的上面是否还有数据,如果有数据则返回true,同时指针上移一行;如果没有数据,则返回false
当调用 previous 方法返回 false 时,光标位于第一行之前

getXXX(String columnLabel)

根据列名,返回该列的值。返回值类型为XXX
以String为例:
String getString(String columnLabel) throws SQLException;

getObject(String columnLabel)

Object getObject(String columnLabel) throws SQLException;
根据列名,返回该列的值。返回值类型为Object

getXXX(int columnIndex)

根据列的索引,返回该列的值。返回值类型为XXX
注意:索引从1开始

以String为例:
String getString(int columnIndex) throws SQLException;

getObject(int columnIndex)

Object getObject(int columnIndex) throws SQLException;
根据列的索引,返回该列的值。返回值类型为XXX

getMetaData

ResultSetMetaData getMetaData() throws SQLException;
获取ResultSetMetaData对象

使用示例

仍使用上面用过的 【mytab2数据表】

  @Test  public void testQuery() throws SQLException {    // 1.获取连接对象    Connection connection = JDBCConnection.getConnection();    String sql = "SELECT * FROM test.mytab2";    // 2.获取预处理SQL命令执行对象    PreparedStatement ps = connection.prepareStatement(sql);    // 3.执行查询    ResultSet rs = ps.executeQuery();    System.out.println("倒序查询");    // 将指针移到末尾列的下面    rs.afterLast();    while (rs.previous()) {      Object id = rs.getObject(1);      String name = rs.getString(2);      String age = rs.getString(3);      Object gender = rs.getObject(4);      System.out.println(id + "\t" + name + "\t" + age + "\t" + gender);    }    System.out.println("正序查询");    while (rs.next()) {      int id = rs.getInt(1);      String name = rs.getString(2);      int age = rs.getInt("age");      String gender = rs.getString("gender");      System.out.println(id + "\t" + name + "\t" + age + "\t" + gender);    }    // 4.关闭连接等对象    JDBCConnection.closeConnection(rs, ps, connection);  }

【输出结果】
倒序查询
5 田七 16 女
4 赵六 14 男
3 王五 21 女
2 李四 17 女
1 张三 19 男
正序查询
1 张三 19 男
2 李四 17 女
3 王五 21 女
4 赵六 14 男
5 田七 16 女

10、ResultSetMetaData

可以获取ResultSet对象上的各列的类型或名称等详细信息

获取ResultSetMetaData对象

ResultSetMetaData getMetaData() throws SQLException;
使用ResultSet的getMetaData方法

getColumnName

String getColumnName(int column) throws SQLException;
获取指定列的名称。
注意:索引从1开始

getColumnLabel

String getColumnLabel(int column) throws SQLException;
获取指定列的别名。
注意:索引从1开始

getColumnCount

int getColumnCount() throws SQLException;
获取当前ResultSet对象中的列数。即查询结果集的列数

getColumnTypeName

String getColumnTypeName(int column) throws SQLException;
获取指定列的数据库类型。
注意:索引从1开始

isNullable

int isNullable(int column) throws SQLException;
获取指定列是否可以为空。0为不能为空;1为可以为空
注意:索引从1开始

isAutoIncrement

boolean isAutoIncrement(int column) throws SQLException;
获取指定列是否为自增长列。true为自增长列;false为不是自增长列。
注意:索引从1开始

使用示例

【mytab数据表】
CREATE TABLE mytab(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(10)
);

INSERT INTO mytab
VALUES(NULL, ‘张三’);

【使用ResultSetMetaData】

Connection connection = JDBCConnection.getConnection();  String sql = "SELECT * FROM test.mytab WHERE id = ?";  PreparedStatement ps = connection.prepareStatement(sql);  ps.setInt(1, 1);  ResultSet rs = ps.executeQuery();  ResultSetMetaData rsmd = rs.getMetaData();  if (rs.next()) {    int columnCount = rsmd.getColumnCount();    for (int i = 1; i <= columnCount; i++) {      System.out.println("列名" + rsmd.getColumnName(i));      System.out.println("列的别名" + rsmd.getColumnLabel(i));      System.out.println("是否可以为空" + rsmd.isNullable(i));      System.out.println("是否是自增长列" + rsmd.isAutoIncrement(i));      System.out.println("==========");    }  }  JDBCConnection.closeConnection(rs, ps, connection);

【输出结果】
列名id
列的别名id
是否可以为空0
是否是自增长列true
==========
列名name
列的别名name
是否可以为空1
是否是自增长列false
==========

11、批处理

概念

当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率

相关方法

addBatch


void addBatch() throws SQLException;
【PreparedStatement的方法】
添加需要批处理的SQL语句。即将一组参数添加到此PreparedStatement对象的批处理命令中

executeBatch

int[] executeBatch() throws SQLException;
【Statement的方法】
将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组

clearBatch

【Statement的方法】
清空缓存的数据。即清空此Statement对象的当前 SQL 命令列表

操作步骤

⑴ 导入jar包。需要使用mysql-connector-java-5.1.37-bin.jar才有效果
⑵ 修改数据库url。需要在url的后面追加:?rewriteBatchedStatements=true
⑶ 调用相关方法即可

使用示例

【配置文件】
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///?rewriteBatchedStatements=true
user=用户名
password=密码

【实现代码】

import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;import com.test.utils.JDBCConnection;public class Test {  public static void main(String[] args) throws SQLException {    long begin = System.currentTimeMillis();    Connection connection = JDBCConnection.getConnection();    String sql = "INSERT INTO test.mytab VALUES(?, ?)";    PreparedStatement ps = connection.prepareStatement(sql);    for (int i = 0; i < 500; i++) {      ps.setInt(1, i);      ps.setString(2, "n" + i);      ps.addBatch(); // 添加SQL到批处理命令中      if (0 == i % 100) { // 每100次        ps.executeBatch(); // 执行批处理        ps.clearBatch(); // 清空缓存数据      }    }    JDBCConnection.closeConnection(null, ps, connection);    long end = System.currentTimeMillis();    System.out.println("总耗时:" + (end - begin));  }}

12、事务

特性

ACID:原子性、一致性、隔离性、永久性
要么全部失败,要么全部执行

相关方法

setAutoCommit


void setAutoCommit(boolean autoCommit) throws SQLException;
【Connection的方法】
设置是否取消自动提交,并开启事务。true为开启;默认为false。
开启后,SQL语句将聚集到事务中,直到调用commit方法或rollback方法为止。

commit

【Connection的方法】
void commit() throws SQLException;
提交事务

rollback

【Connection的方法】
void rollback() throws SQLException;
回滚事务

使用示例

【account数据表】
CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(10) NOT NULL,
    balance DOUBLE
);

INSERT INTO account
SELECT NULL, ‘张三’, 500 UNION
SELECT NULL, ‘李四’, 500;

【实现代码】

Connection connection = JDBCConnection.getConnection();connection.setAutoCommit(false); // 取消自动提交Statement s1 = connection.createStatement();Statement s2 = connection.createStatement();try {  s1.executeUpdate("UPDATE test.account SET balance = balance + 500 WHERE id = 1");  int i = 1 / 0; // 模拟异常  s2.executeUpdate("UPDATE test.account SET balance = balance + 500 WHERE id = 2");} catch (Exception e) {  connection.rollback(); // 回滚事务【当遇到异常时】}connection.commit(); // 提交事务JDBCConnection.closeConnection(null, s1, null);JDBCConnection.closeConnection(null, s2, connection);

13、MySQL BLOB类型

概念

    BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据

    MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
MySQL的四种BLOB类型

    实际使用中根据需要存入的数据大小定义不同的BLOB类型。需要注意的是:如果存储的文件过大,数据库的性能会下降

相关方法

setBlob


【PreparedStatement的方法】
void setBlob(int parameterIndex, InputStream inputStream) throws SQLException;
填充占位符,第二个参数设置为InputStream对象,即要写入数据库的文件

getBlob


Blob getBlob(int columnIndex) throws SQLException;
【ResultSet的方法】
获取Blob对象

getBinaryStream


java.io.InputStream getBinaryStream () throws SQLException;
【Blob的方法】
以输入流的形式获取此Blob对象指定的BLOB值。

错误的避免

⑴ 数据库的字符集要用utf8
    可以通过MySQL安装目录下的:my.ini配置文件来修改字符集
    有两处需要修改:
        default-character-set=utf8
        character-set-server=utf8

⑵ 写入的文件的大小不要超过具体的BLOB列的上限大小
⑶ 注意指向要保存到数据库的文件的输入流要关闭

使用示例

【myblob数据表】
CREATE TABLE myblob(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(10) NOT NULL,
    pic BLOB
);

【存入文件】

@Test  public void testOutputBlob() throws SQLException, IOException {    Connection connection = JDBCConnection.getConnection();    String sql = "INSERT INTO test.myblob VALUES(null, ?, ?)";    PreparedStatement ps = connection.prepareStatement(sql);    ps.setString(1, "pic1");    // 文件输入流    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("pics\\pic1.jpg"));    ps.setBlob(2, bis);    ps.executeUpdate();    // 关闭输入流    bis.close();    JDBCConnection.closeConnection(null, ps, connection);  }

【读取文件】

  @Test  public void testInputBlob() throws SQLException, IOException {    Connection connection = JDBCConnection.getConnection();    String sql = "SELECT pic FROM test.myblob WHERE id = ?";    PreparedStatement ps = connection.prepareStatement(sql);    ps.setInt(1, 1);    ResultSet rs = ps.executeQuery();    if (rs.next()) {      Blob blob = rs.getBlob(1);      InputStream is = blob.getBinaryStream();      BufferedInputStream bis = new BufferedInputStream(is);      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("photos\\photo1.jpg"));      byte[] buffer = new byte[1024];      int len;      while (-1 != (len = bis.read(buffer))) {        bos.write(buffer, 0, len);      }      bos.close();      bis.close();    }    JDBCConnection.closeConnection(null, ps, connection);  }

14、数据库连接池

问题的引入

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

解决的办法

    为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
    数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

概念

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource是一个接口
DataSource通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把DataSource称为连接池
DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。

优点

⑴ 资源重用
⑵ 更快的系统反映速度
⑶ 新的资源分配手段
⑷ 统一的连接管理,避免数据库连接泄漏

开源的数据库连接池

⑴ DBCP 数据库连接池
⑵ C3P0 数据库连接池

15、DBCP

概念

    DBCP是Apache软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。
    如需使用该连接池实现,应在系统中增加如下两个jar文件:
        Commons-dbcp.jar:连接池的实现
        Commons-pool.jar:连接池实现的依赖库

    数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可
    当数据库访问结束后,程序还是像以前一样关闭数据库连接:connection.close();但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

构造方法

BasicDataSource


BasicDataSource()
创建一个BasicDataSource对象

相关方法

createDataSource


public static DataSource createDataSource(Properties properties) throws Exception {}
得到一个DataSource对象。静态方法通过BasicDataSourceFactory类来调用。需要传入一个Properties对象,并加载配置文件

setDriverClassName


public synchronized void setDriverClassName(String driverClassName) {}
设置驱动全类名

setUrl


public synchronized void setUrl(String url) {}
设置数据库URL

setUsername


public void setUsername(String username) {}
设置用户名

setPassword


public void setPassword(String password) {}
设置密码

setMaxActive

public synchronized void setMaxActive(int maxActive) {}
设置连接池的最大活动连接数

setMinIdle


public synchronized void setMinIdle(int minIdle) {}
设置连接池中的最小的空闲连接数

setInitialsize

public synchronized void setInitialSize(int initialSize) {}
设置连接池中的初始连接数

使用示例

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSource;import org.apache.commons.dbcp.BasicDataSourceFactory;import org.junit.Test;public class TestDBCP {  /**   * 连接方式一【通过代码设置参数】   */  @Test  public void testDBCP1() throws SQLException {    // 1.创建数据库连接池对象    BasicDataSource bds = new BasicDataSource();    /*     * 2.设置配置信息    */    ///////////////////基本配置///////////////////    // 设置驱动全类名    bds.setDriverClassName("com.mysql.jdbc.Driver");    // 设置数据库URL    bds.setUrl("jdbc:mysql:///");    // 设置用户名    bds.setUsername("用户名");    // 设置密码    bds.setPassword("密码");    ///////////////////其他配置///////////////////    // 设置连接池的最大活动连接数    bds.setMaxActive(10);    // 设置连接池中的最小的空闲连接数    bds.setMinIdle(5);    // 设置连接池中的初始连接数    bds.setInitialSize(5);    // 3.获取连接对象    Connection connection = bds.getConnection();    System.out.println(connection);    // 4.关闭连接对象【不是断开和数据库服务的连接,而是重新放到数据库连接池中】    connection.close();  }  /**   * 连接方式二【通过配置文件设置参数】   */  @Test  public void testDBCP2() throws Exception {    // 1.加载配置文件    Properties properties = new Properties();    properties.load(new FileInputStream("src\\dbcp.properties"));    // 2.创建数据库连接池对象    DataSource ds = BasicDataSourceFactory.createDataSource(properties);    // 3.获取连接对象    Connection connection = ds.getConnection();    System.out.println(connection);    // 4.关闭连接对象【不是断开和数据库服务的连接,而是重新放到数据库连接池中】    connection.close();  }}

【dbcp.properties】
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///
username=用户名
password=密码

maxActive=10
minIdle=5
initialSize=5

16、C3P0

构造方法

⑴ ComboPooledDataSource();
创建一个ComboPooledDataSource对象

⑵ ComboPooledDataSource(java.lang.String configName);
创建一个ComboPooledDataSource对象。需要将c3p0-config.xml配置文件中的的name传入构造器参数中

注意:c3p0-config的名字不能变;而配置文件中的name可以更改

相关方法


setDriverClass


setDriverClass(String driverClass);
设置驱动全类名

setJdbcUrl


setJdbcUrl(String jdbcUrl);
设置数据库URL

setUser

setUser(String user);
设置用户名

setPassword


setPassword(String password);
设置密码

setAcquireIncrement


setAcquireIncrement(int acquireIncrement);
设置每次向数据库服务器申请的连接数

setInitialPoolSize

setInitialPoolSize(int initialPoolSize);
设置初始的连接数

setMinPoolSize


setMinPoolSize(int minPoolSize);
设置连接池保留的最小连接数

setMaxPoolSize


setMaxPoolSize(int maxPoolSize);
设置连接池保留的最大连接数

setMaxStatements


setMaxStatements(int maxStatements);
设置可连接的最多的命令对象数

setMaxStatementsPerConnection


setMaxStatementsPerConnection(int maxStatementsPerConnection);
设置每个连接对象可连接的最多的命令对象数

使用示例

import java.beans.PropertyVetoException;import java.sql.Connection;import java.sql.SQLException;import org.junit.Test;import com.mchange.v2.c3p0.ComboPooledDataSource;public class TestC3P0 {  /**   * 连接方式一【通过代码设置参数】   */  @Test  public void testC3P0_1() throws PropertyVetoException, SQLException {    // 1.创建数据库连接池对象    ComboPooledDataSource cpds = new ComboPooledDataSource();    /*     * 2.设置配置信息    */    /////////////////// 基本配置///////////////////    cpds.setDriverClass("com.mysql.jdbc.Driver");    cpds.setJdbcUrl("jdbc:mysql:///");    cpds.setUser("root");    cpds.setPassword("1230");    /////////////////// 其他配置///////////////////    // 设置每次向数据库服务器申请的连接数     cpds.setAcquireIncrement(10);    // 设置初始的连接数    cpds.setInitialPoolSize(5);    // 设置连接池保留的最小连接数    cpds.setMinPoolSize(5);    // 设置连接池保留的最大连接数    cpds.setMaxPoolSize(20);    // 设置可连接的最多的命令对象数    cpds.setMaxStatements(10);    // 设置每个连接对象可连接的最多的命令对象数    cpds.setMaxStatementsPerConnection(5);    // 3.获取连接对象    Connection connection = cpds.getConnection();    System.out.println(connection);    // 4.关闭连接对象【不是断开和数据库服务的连接,而是重新放到数据库连接池中】    connection.close();  }  /**   * 连接方式二【通过配置文件设置参数】   */    @Test  public void testC3P0_2() throws SQLException{    // 1.创建数据库连接池对象【加载配置文件】    ComboPooledDataSource cpds = new ComboPooledDataSource("myc3p0cfg");    // 2.获取连接对象    Connection connection  = cpds.getConnection();    System.out.println(connection);    // 3.关闭连接对象【不是断开和数据库服务的连接,而是重新放到数据库连接池中】    connection.close();  }}

【c3p0-config.xml】

<c3p0-config><named-config name="myc3p0cfg">    <property name="driverClass">com.mysql.jdbc.Driver</property>    <property name="jdbcUrl">jdbc:mysql:///</property>    <property name="user">用户名</property>    <property name="password">密码</property>    <!-- 每次向数据库服务器申请的连接数 -->    <property name="acquireIncrement">10</property>    <!-- 初始的连接数 -->    <property name="initialPoolSize">5</property>    <!-- 连接池保留的最小连接数 -->    <property name="minPoolSize">5</property>    <!-- 连接池保留的最大连接数 -->    <property name="maxPoolSize">20</property>    <!-- 可连接的最多的命令对象数 -->    <property name="maxStatements">10</property>    <!-- 每个连接对象可连接的最多的命令对象数 -->    <property name="maxStatementsPerConnection">5</property></named-config>

17、DBUtils工具类

功能

可以实现任何SQL语句的执行以及任何数据表的增删改查

构造方法

QueryRunner();
创建一个QueryRunner对象

相关方法

query


public T query(Connection conn, String sql, ResultSetHandler rsh, Object… params) throws SQLException {}
执行查询语句。第一个参数为Connection对象;第二个参数为SQL语句;第三个参数为ResultSetHandler实现类对象;第四个参数为可变参数,用于填充SQL占位符

update


public int update(Connection conn, String sql, Object… params) throws SQLException {}
执行更新或删除语句。第一个参数为Connection对象;第二个参数为SQL语句;第三个参数为可变参数,用于填充SQL占位符

insert


public T insert(Connection conn, String sql, ResultSetHandler rsh, Object… params) throws SQLException {}
执行插入语句。第一个参数为Connection对象;第二个参数为SQL语句;第三个参数为ResultSetHandler实现类对象;第四个参数为可变参数,用于填充SQL占位符

ResultSetHandler接口

BeanHandler


public BeanHandler(Class type) {}
封装一个实体对象,用于查询一行记录。需要传入JavaBean对象的Class类型的对象

BeanListHandler


public BeanListHandler(Class type) {}
封装多个实体对象到List集合中,用于查询多行记录。需要传入JavaBean对象的Class类型的对象

ScalarHandler


public ScalarHandler() {}
查询第一行第一列的值。不管结果是一个值还是一行或多行记录。

使用示例

【person数据表】
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10) NOT NULL,
age INT
);

INSERT INTO person
SELECT NULL, ‘张三’, 17 UNION
SELECT NULL, ‘李四’, 20 UNION
SELECT NULL, ‘王五’, 14;

【Person类】

package com.test.entity;public class Person {  private int id;  private String name;  private int age;  /*   get 和 set 方法  */  public Person() {  }  public Person(int id, String name, int age) {    this.id = id;    this.name = name;    this.age = age;  }  @Override  public String toString() {    return id + "\t" + name + "\t" + age;  }}

【实现代码】

import java.sql.Connection;import java.sql.SQLException;import java.util.List;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.apache.commons.dbutils.handlers.ScalarHandler;import org.junit.Test;import com.test.entity.Person;import com.test.utils.JDBCConnection;public class TestDBUtils {  // 创建QueryRunner对象【此类的线程是安全的】  QueryRunner qr = new QueryRunner(); /**   * 查询单行结果   */  @Test  public void testBeanHandler() throws SQLException {    Connection connection = JDBCConnection.getConnection();    String sql = "SELECT * FROM test.person WHERE id = ?";    Person person = qr.query(connection, sql, new BeanHandler<Person>(Person.class), 1);    System.out.println(person);    connection.close();  }  /**   * 查询多行结果   */  @Test  public void testBeanListHandler() throws SQLException {    Connection connection = JDBCConnection.getConnection();    String sql = "SELECT * FROM test.person";    List<Person> persons = qr.query(connection, sql, new BeanListHandler<Person>(Person.class));    int size = persons.size();    for (int i = 0; i < size; i++) {      System.out.println(persons.get(i));    }    connection.close();  }  /**   * 增删改   *    * 查询一个结果   */  @Test  public void testScalanHandler() throws SQLException {    Connection connection = JDBCConnection.getConnection();    // String sql = "INSERT INTO test.person VALUES(NULL, ?, ?)";    // Object obj = qr.insert(connection, sql, new ScalarHandler<>(), "赵六", 12);    // String sql = "UPDATE test.person SET name = ? WHERE id = ?";    // int update = qr.update(connection, sql, "John", 4);    // String sql = "DELETE FROM test.person WHERE id = ?";    // int update = qr.update(connection, sql, 4);    // System.out.println(update > 0 ? "操作成功!" : "操作失败!");    String sql = "SELECT COUNT(*) FROM test.person";    Object obj = qr.query(connection, sql, new ScalarHandler<>());    System.out.println(obj);    connection.close();  }}

18、JDBCConnection

【db.properties】
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///
user=用户名
password=密码

【工具类代码】

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JDBCConnection {  private static String url; // 数据库URL  private static String user; // 用户名  private static String password; // 密码  static {    Properties properties = new Properties();    try {      // 加载配置文件      properties.load(new FileInputStream("src\\db.properties"));      url = properties.getProperty("url");      user = properties.getProperty("user");      password = properties.getProperty("password");      String driverClass = properties.getProperty("driverClass");      // 加载驱动      Class.forName(driverClass);    } catch (FileNotFoundException e) {      e.printStackTrace();    } catch (IOException e) {      e.printStackTrace();    } catch (ClassNotFoundException e) {      e.printStackTrace();    }  }  /**   * 获取Connection对象   *    * @return 连接对象   */  public static Connection getConnection() throws SQLException {    return DriverManager.getConnection(url, user, password);  }  /**   * 关闭连接等对象   *    * @param set 结果集对象   * @param statement 执行SQL命令对象   * @param connection 连接对象   */  public static void closeConnection(ResultSet set, Statement statement,     Connection connection) {    if (null != set) {      try {        set.close();      } catch (SQLException e) {        e.printStackTrace();      }    }    if (null != statement) {      try {        statement.close();      } catch (SQLException e) {        e.printStackTrace();      }    }    if (null != connection) {      try {        connection.close();      } catch (SQLException e) {        e.printStackTrace();      }    }  }}

19、DAO

概念

    Data Access Object
    一个访问数据信息的类,其包含了对数据的CRUE(create, retrieve, update, delete),而不包含任何业务相关的操作。实现功能的模块化,更有利于代码的维护和升级

示例

package com.test.dao;import java.sql.Connection;import java.sql.SQLException;import java.util.List;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.apache.commons.dbutils.handlers.ScalarHandler;import com.test.utils.JDBCConnection;public class DAO<T> {  private static QueryRunner qr = new QueryRunner();  /**   * 更新或删除操作   * @param sql SQL语句   * @param objs 填充占位符的元素   * @return 受影响的行数   */  public static int update(String sql, Object... objs) throws SQLException {    Connection connection = JDBCConnection.getConnection();    int update = qr.update(connection, sql, objs);    JDBCConnection.closeConnection(null, null, connection);    return update;  }  /**   * 查询一行记录   * @param clazz JavaBean对象的Class类型的对象   * @param sql SQL语句   * @param objs 填充占位符的元素   * @return 一行记录   */  public static<T> T query(Class<T> clazz, String sql, Object... objs) throws SQLException {    Connection connection = JDBCConnection.getConnection();    T t = qr.query(connection, sql, new BeanHandler<T>(clazz), objs);    JDBCConnection.closeConnection(null, null, connection);    return t;  }  /**   * 查询多行记录   * @param clazz JavaBean对象的Class类型的对象   * @param sql SQL语句   * @param objs 填充占位符的元素   * @return 多行记录   */  public static<T> List<T> queryAll(Class<T> clazz, String sql, Object... objs) throws SQLException {    Connection connection = JDBCConnection.getConnection();    List<T> list = qr.query(connection, sql, new BeanListHandler<T>(clazz), objs);    JDBCConnection.closeConnection(null, null, connection);    return list;  }  /**   * 查询一个记录   * @param sql SQL语句   * @param objs 填充占位符的元素   * @return 一个记录   */  public static<E> E query(String sql, Object... objs) throws SQLException {    Connection connection = JDBCConnection.getConnection();    E e = qr.query(connection, sql, new ScalarHandler<E>(), objs);    JDBCConnection.closeConnection(null, null, connection);    return e;  }}

20、相关jar包

⑴ MySQL驱动:mysql-connector-java-5.1.7-bin.jar
注:批处理时用的驱动:mysql-connector-java-5.1.37-bin.jar

⑵ DBCP:
① commons-dbcp-1.4.jar
② commons-pool-1.5.5.jar

⑶ C3P0:c3p0-0.9.1.2.jar

⑷ DBUtils:commons-dbutils-1.6.jar



【源码】

⑴ MySQL驱动:mysql-connector-java-5.1.7 【包含jar包和源码文件夹】

⑵ DBCP:
① commons-dbcp-1.4-src
② commons-pool-1.5.5-src

⑶ C3P0:c3p0-0.9.1.2.src

⑷ DBUtils:commons-dbutils-1.6-src

1 0
原创粉丝点击