JavaWeb-JDBC

JavaWeb-JDBC

\[操作步骤 \begin{cases} \text{注册驱动——加载Driver类}\\ \text{获取连接——得到Connection对象}\\ \text{获取执行SQL语句的对象——Statement/PreparedStatement/CallableStatement}\\ \text{执行增删改查——发送SQL给MySQL执行}\\ \text{处理得到的结果}\\ \text{释放资源——关闭相关连接} \end{cases} \]

MySQL类型和Java类型相互转换
SQL类型 Java类型
tinyint byte
smallint short
int integer
bigint long
char/varchar/longvarchar String
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp

JDBC相关对象

DriverManager驱动管理

DriverManager用于管理JDBC驱动,它有两个功能

  • 注册驱动:告诉程序该使用哪一个数据库驱动JAR
  • 获取数据库连接
    • URL指定连接的路径 jdbc:mysql://IP地址:端口号/数据库名
    • 如果连接的是本地MySQL服务器,并且默认端口是3306,则URL可以简写 jdbc:mysql:///数据库名
// 将要分析的源码(在MySQL驱动5以后,源码META-INF/services/java.sql.Driver中增添了驱动地址,所以一下这行代码可省略)
Class.forName("com.mysql.cj.jdbc.Driver");

// 通过源码发现,在com.mysql.cj.jdbc.Driver类中存在静态代码块
static {
    try {
        DriverManager.registerDriver(new Driver()); // 加载驱动类到内存的这行代码可省略
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}
DriverManager常用方法
常用方法 返回值 说明
getConnection(String url) static Connection 获取参数URL对应的数据库的链接
getConnection(String url, Properties info) static Connection 获取参数对应的数据库的链接
getConnection(String url, String user, String password) static Connection 获取参数对应的数据库的链接
getDriver(String url) static Driver 获取给定参数URL的驱动程序
getDrivers() static Enumeration 获取所有调用者可以访问的所有当前加载的JDBC驱动程序检索枚举
getLoginTimeout() static int 获取尝试登录数据库时驱动程序可以等待的最长时间(秒)
setLoginTimeout(int seconds) static void 设置驱动程序在识别驱动程序后尝试连接到数据库时等待的最长时间(秒)
getLogWriter() static PrintWriter 检索日志记录器
setLogWriter(PrintWriter out) static void 设置 DriverManager和所有驱动程序使用的记录/跟踪 PrintWriter对象
deregisterDriver(Driver driver) static void 从 DriverManager的注册驱动程序列表中删除指定的驱动程序
registerDriver(Driver driver) static void 注册与给定的驱动程序 DriverManager
registerDriver(Driver driver, DriverAction da) static void 注册与给定的驱动程序 DriverManager
println(String message) static void 打印到当前JDBC日志流的消息

Connection连接对象

Connection代表数据库连接对象,每个Connection都代表一个物理连接会话,要想访问数据库,必须先获得数据库连接

  • 获取执行SQL的对象

    • 获取Statement对象
    • 获取PreparedStatement对象
    • 获取CallableStatement对象,该对象用于调用存储过程
  • 管理事务

    • 开启事务:setAutoCommit(boolean autoCommit),调用该方法时设置参数为false即可,就能开启事务
    • 提交事务:commit()
    • 回滚事务:rollback()
Connection常用方法
常用方法 返回值 说明
createStatement() Statement 返回一个Statement对象
createStatement(int resultSetType, int resultSetConcurrency) Statement 返回一个Statement对象
createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) Statement 返回一个Statement对象
prepareStatement(String sql) PreparedStatement 返回一个PreparedStatement对象
prepareStatement(String sql, int autoGeneratedKeys) PreparedStatement 返回一个PreparedStatement对象
prepareStatement(String sql, int[] columnIndexes) PreparedStatement 返回一个PreparedStatement对象
prepareStatement(String sql, int resultSetType, int resultSetConcurrency) PreparedStatement 返回一个PreparedStatement对象
prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) PreparedStatement 返回一个PreparedStatement对象
prepareStatement(String sql, String[] columnNames) PreparedStatement 返回一个PreparedStatement对象
prepareCall(String sql) CallableStatement 返回一个CallableStatement对象
prepareCall(String sql, int resultSetType, int resultSetConcurrency) CallableStatement 返回一个CallableStatement对象
prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) CallableStatement 返回一个CallableStatement对象
setAutoCommit(boolean autoCommit) void 设置为false,则会关闭自动提交,打开事务
getAutoCommit() boolean 检索此Connection对象的当前自动提交模式
commit() void 提交事务
rollback() void 回滚事务
rollback(Savepoint savepoint) void 将事物回滚到指定的保存点
setTransactionIsolation(int level) void 设置事务的隔离级别
getTransactionIsolation() int 获取此Connection对象的当前事务隔离级别
setSavepoint() Savepoint 创建一个保存点
setSavepoint(String name) Savepoint 以指定名字来创建一个保存点
releaseSavepoint(Savepoint savepoint) void 删除/释放保存点
close() void 关闭资源
abort(Executor executor) void 终止打开的连接
getNetworkTimeout() int 获取数据库连接的超时时间
setNetworkTimeout(Executor executor, int milliseconds) void 设置数据库连接的超时时间
getSchema() String 获取该Connection访问的数据库Schema
setSchema(String schema) void 设置要访问的给定模式名称
getMetaData() DatabaseMetaData 获取元数据
isClosed() boolean 检索此Connection对象是否已关闭
isReadOnly() boolean 检索此Connection对象是否处于只读模式
setReadOnly(boolean readOnly) void 将此连接设置为只读模式,作为驱动程序的提示以启用数据库优化
isValid(int timeout) boolean 如果连接尚未关闭并且仍然有效,则返回true

Statement

Statement用于执行静态SQL语句并返回其生成的结果对象.该对象既可以执行DML语句,也可以执行DDL/DCL语句,还可用于执行SQL查询

Statement常用方法
常用方法 返回值 说明
executeQuery(String sql) ResultSet 执行给定的SQL语句,该语句返回单个ResultSet对象
executeUpdate(String sql) int 执行给定的SQL语句,这可能是INSERT/UPDATE/DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句
executeUpdate(String sql, int autoGeneratedKeys) int 执行给定的SQL语句
executeUpdate(String sql, int[] columnIndexes) int 执行给定的SQL语句
executeUpdate(String sql, String[] columnNames) int 执行给定的SQL语句
execute(String sql) boolean 执行给定的SQL语句,这可能会返回多个结果
execute(String sql, int autoGeneratedKeys) boolean 执行给定的SQL语句
execute(String sql, int[] columnIndexes) boolean 执行给定的SQL语句
execute(String sql, String[] columnNames) boolean 执行给定的SQL语句
executeBatch() int[] 将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组
executeLargeBatch() default long[] 将一批命令提交到数据库以执行,并且所有命令都执行成功,返回一个更新计数的数组
executeLargeUpdate(String sql) default long 执行给定的SQL语句
executeLargeUpdate(String sql, int autoGeneratedKeys) default long 执行给定的SQL语句
executeLargeUpdate(String sql, int[] columnIndexes) default long 执行给定的SQL语句
executeLargeUpdate(String sql, String[] columnNames) default long 执行给定的SQL语句
close() void 关闭资源
closeOnCompletion() void 当所有依赖于该Statement的Result关闭时,该Statement会自动关闭
cancel() void 如果DBMS和驱动程序都支持中止SQL语句,则取消此 Statement对象
isClosed() boolean 判断是否关闭
isCloseOnCompletion() boolean
addBatch(String sql) void 添加批处理
clearBatch() void 清除批处理
getConnection() Connection 获取Connection对象
getResultSet() ResultSet 获取ResultSet对象
代码案例:增删改
Class.forName("com.mysql.cj.jdbc.Driver");
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     Statement stmt = conn.createStatement();
) {
    String sql = "insert into user values(null,'张三','男','2022-02-01')";
    int rows = stmt.executeUpdate(sql);
    System.out.println(rows > 0 ? "成功" : "失败");
} catch (Exception e) {
    e.printStackTrace();
}
代码案例:查询
Class.forName("com.mysql.cj.jdbc.Driver");
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     Statement stmt = conn.createStatement();
) {
    String sql = "select * from user";
    ResultSet rs = stmt.executeQuery(sql);
    while (rs.next()) {
        int id = rs.getInt(1);
        String name = rs.getString(2);
        String gender = rs.getString(3);
        Date birthDay = rs.getDate(4);
        System.out.println(id + "\t" + name + "\t" + gender + "\t" + birthDay);
    }
} catch (Exception e) {
    e.printStackTrace();
}

PreparedStatement

PreparedStatement执行的SQL语句中的参数用问号来表示,调用PreparedStatement对象的setXXX(int index,XXX value)方法来设置这些参数.setXXX()方法有两个参数,第一个参数是要设置SQL语句中的参数索引(从1开始),第二个参数是设置的SQL语句中的参数值

  • 调用 executeQuery() : 返回ResultSet对象
  • 调用 executeUpdate() : 执行增删改操作

注意:execute()/executeQuery()/executeUpdate() 三个方法执行SQL语句,都不需要传递参数,因为PreparedStatement已存储了预编译的SQL语句

Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "update user set name = ? where id = 1";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     PreparedStatement pstmt = conn.prepareStatement(sql);
) {
    pstmt.setString(1, "zhangsan");
    int rows = pstmt.executeUpdate();
    System.out.println(rows > 0 ? "成功" : "失败");
} catch (Exception e) {
    e.printStackTrace();
}

CallableStatement

调用存储过程使用CallableStatement,可以通过Connection的prepareCall()方法来创建CallableStatement对象,创建该对象时需要传入调用存储过程的SQL语句.调用存储过程的SQL语句总是这种形式: {call 存储过程名(?,?,?)} ,其中的问号作为存储过程参数的占位符

create procedure add_pro(a int, b int, out sum int)
begin
    set sum = a + b;
end;
代码案例:存储过程
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "{call add_pro(?,?,?)}";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     CallableStatement cstmt = conn.prepareCall(sql);
) {
    cstmt.setInt(1, 4);
    cstmt.setInt(2, 5);
    cstmt.registerOutParameter(3, Types.INTEGER); // 注册第三个参数是int类型
    cstmt.execute(); // 执行存储过程
    String sum = cstmt.getString(3); // 获取存储过程传出的参数值
    System.out.println(sum);
} catch (Exception e) {
    e.printStackTrace();
}

ResultSet结果集对象

ResultSet结果集对象,该对象包含访问查询结果的方法,ResultSet可以通过列索引或列名获得列数据.它包含了多个常用方法来移动记录指针

ResultSet常用方法
常用方法 返回值 说明
close() void 释放ResultSet对象
absolute(int row) boolean 将结果集的记录指针移动到第row行;如果row为负数,则移动到倒数第row行;如果移动后的记录指针指向一条有效记录,则方法返回true
beforeFirst() void 将ResultSet的记录指针定位到首行之前;这是ResultSet结果集记录指针的初始状态
afterLast() void 将ResultSet的记录指针定位到最后一行之后
first() boolean 将ResultSet的记录指针定位到首行;如果移动后的记录指针指向一条有效记录,则方法返回true
last() boolean 将ResultSet的记录指针定位到最后一行;如果移动后的记录指针指向一条有效记录,则方法返回true
previous() boolean 将ResultSet的记录指针定位到上一行;如果移动后的记录指针指向一条有效记录,则方法返回true
next() boolean 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据);如果是,则返回false,如果不是则返回true
getXXX(int columnIndex) int 参数int代表列的编号,从1开始
getXXX(String columnLabel) String 参数int代表列的名称

处理Blob类型数据

Blob(Binary Long Object)是二进制长对象的意思,Blob常用语存储大文件,典型的Blob内容是一张图片或一个声音文件,由于它们的特殊性,必须使用特殊的方式来存储.使用Blob可以把图片声音等文件的二进制数据保存在数据库里,并可以从数据库里恢复指定文件

如果需要将图片插入数据库,显然不能直接通过普通的SQL语句来完成,因为有一个关键的问题——Blob常量无法表示,所以需要使用PreparedStatement把Blob数据插入数据库(因为Blob数据时无法使用字符串拼写的),该对象有一个setBinaryStream(int parameterIndex,InputStream x)方法为指定参数传入二进制输入流,从而可以实现将Blob数据保存到数据库;当需要从ResultSet中取出Blob数据时,可以调用ResultSet的getBlob(int columnIndex)方法,该方法将返回一个Blob对象,Blob对象提供了getBinaryStream()方法来获取该Blob数据的输入流,也可以使用Blob对象提供的getBytes()方法直接取出该Blob对象封装的二进制数据

如果在指定了相关的Blob类型以后,还报错"XXX too large",那么在MySQL的安装目录下,找my.ini文件加上如下的配置参数即可 max_allowed_packet=16M .同时在修改了my.ini文件之后,需要重启MySQL服务

MySQL中四种Blob类型 大小/字节
TinyBlob 最大 255
Blob 最大 64K
MediumBlob 最大 16M
LongBlob 最大 4G

注意 : 如果存储的文件过大,数据库的性能会下降

代码案例:插入数据
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "insert into user values (null,?,?,?,?)";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     PreparedStatement pstmt = conn.prepareStatement(sql);
) {
    pstmt.setObject(1, "zhangsan");
    pstmt.setObject(2, "女");
    pstmt.setObject(3, "2022-01-01");
    FileInputStream fis = new FileInputStream(new File("C:\\Users\\zhang\\Desktop\\avatar.jpg"));
    pstmt.setBlob(4, fis);
    pstmt.execute();
} catch (Exception e) {
    e.printStackTrace();
}
代码案例:读取数据
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "select photo from user where name = ?";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     PreparedStatement pstmt = conn.prepareStatement(sql);
) {
    pstmt.setObject(1, "zhangsan");
    ResultSet rs = pstmt.executeQuery();
    while (rs.next()) {
        Blob photo = rs.getBlob("photo");
        InputStream input = photo.getBinaryStream();
        FileOutputStream fos = new FileOutputStream("photo.jpg");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = input.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}
代码案例:批处理
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "insert into user(name,gender,birthday) values(?,?,?)";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
     PreparedStatement pstmt = conn.prepareStatement(sql);
) {
    for (int i = 0; i < 100; i++) {
        pstmt.setObject(1, "name_" + i);
        pstmt.setObject(2, "男");
        pstmt.setObject(3, "2022-01-01");
        pstmt.addBatch(); // 添加批处理
        pstmt.executeBatch(); // 执行批处理
        pstmt.clearBatch(); // 清空批处理
    }
} catch (Exception e) {
    e.printStackTrace();
}

数据库连接池

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由商用服务器(如WebLogic,WebSphere)等提供实现,也有一些开源组织提供实现(如C3P0和DBCP等)

  • C3P0 : 是一个开源组件提供的一个数据库连接池,速度相对较慢,稳定性还可以,Hibernate官方推荐使用
  • DBCP : 是Apache提供的数据库连接池,Tomcat服务器自带DBCP连接池,速度相对C3P0较快,但因自身存在BUG,Hibernate3已不再提供支持
  • Druid : 是阿里提供的数据库连接池,据说是集C3P0/DBCP优点于一身的数据库连接池

DataSource通常被称为数据源,它包含连接池和连接池管理两部分.DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库卡访问速度

C3P0

使用步骤:

  • 导入JAR包
    • c3p0-0.9.5.5.jar
    • mchange-commons-java-0.2.19.jar
    • mysql-connector-java-8.0.27.jar
  • 定义配置文件
    • 名称 : c3p0-config.xml 或者 c3p0.properties
    • 位置 : 直接将文件放在src目录下即可
  • 创建数据库连接对象 ComboPooledDataSource
  • 获取连接
配置文件:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--默认配置-->
    <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <!--当数据库连接池中的连接数不够时,C3P0一次向数据库服务器申请的连接数-->
        <property name="acquireIncrement">5</property>
        <!--C3P0数据库连接池中初始化时的连接数-->
        <property name="initialPoolSize">5</property>
        <!--C3P0数据库连接池维护的最大连接数-->
        <property name="maxPoolSize">10</property>
        <!--C3P0数据库连接池超时时间-->
        <property name="checkoutTimeout">3000</property>
    </default-config>

    <!--配置连接池mysql-->
    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/CoupleSpace</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </named-config>
</c3p0-config>
代码案例:C3P0
DataSource dataSource = new ComboPooledDataSource();
for (int i = 0; i < 10; i++) {
    Connection conn = dataSource.getConnection();
    System.out.println(i + " : " + conn);
    if(i == 5){
        conn.close();
    }
}

DBCP

DBCP需要的JAR包

  • commons-dbcp2-2.9.0.jar
  • commons-pool2-2.10.0.jar
  • commons-logging-1.2.jar

创建 dbcp.properties 文件

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
Properties prop = new Properties();
InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
prop.load(input);
DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);
Connection conn = dataSource.getConnection();
System.out.println(conn);

Druid

Druid所需JAR包

  • druid-1.2.8.jar
  • javax.annotation-api-1.3.2.jar

创建 druid.properties 文件;文件名可以是任意的,也可以放在任意目录下

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
Properties prop = new Properties();
InputStream input = Main.class.getClassLoader().getResourceAsStream("druid.properties");
prop.load(input);
DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);
Connection conn = dataSource.getConnection();
System.out.println(conn);
上一篇:jdbc数据库连接池C3P0和Druid德鲁伊,Java与MySQL数据库交互


下一篇:微服务架构 | 4.2 基于 Feign 与 OpenFeign 的服务接口调用