LongJL 发表于 2021-7-24 11:19

【自学笔记】 Java基础 - JDBC

# JDBC基础

阅读这篇笔记你需要掌握反射和Mysql这些知识。

## 概念
JDBC ( Java DataBase Connectivity java数据库连接)是-种用于执行SQL语句的JavaAPI ,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。其实就是java官方提供的一套规范(接口),用于帮助开发人员快速实现不同关系型数据库的连接。


## 快速入门
- 导入jar包
- 注册驱动
- 获取数据库连接
- 获取执行者对象
- 执行sql语句并返回结果
- 处理结果
- 释放资源

```Java
public class JDBCDemo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
//      -导入jar包
//      - 注册驱动
      Class.forName("com.mysql.jdbc.Driver");
//      - 获取数据库连接
      Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/qq", "root", "root");
//      - 获取执行者对象
      Statement statement = connection.createStatement();
//      - 执行sql语句并返回结果
      String sql = "SELECT * FROM user";
      ResultSet resultSet = statement.executeQuery(sql);
//      - 处理结果
      while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");

            System.out.print(id + "");
            System.out.println(username);
      }
//      - 释放资源
      resultSet.close();
      statement.close();
      connection.close();
    }
}
```

## 功能类解释

### DriverManager
DriverManager取得管理对象
1. 注册驱动
        - 注册给定的驱动程序:`static void registerDriver(Driver driver)`
        - 写代码使用:`Class.forName("com.mysql.jdbc.Driver")`
        - 在`com.mysqlJdbc.Driver`类中存在静态代码块,里面就已经实现注册驱动
   ```Java
   static {
           try {
                   DriverManager.registerDriver(new Driver());
   } catch (SQLException var1) {
                   throw new RuntimeException("Can't register driver!");
           }
   }
   ```
        - 注意事项
                1. 我们不需要提供`DriverManager`调用静态方法`registerDriver()`,以为只要Driver类被使用,则会执行其静态代码块完成驱动注册
                2. mysql5之后可以省略注册驱动步骤。在jar包中,存在一个`java.sql.Driver`配置文件,文件中指定了`com.mysql.jdbc.Driver`

2. 获取数据库连接
                   获取数据库连接对象:`static Connection getConnection(String url,String user,String password)`
                      参数:
                              url:指定连接的路径。语法:`"jdbc:mysql://ip地址(域名):端口号/数据库名称"`               
                              user:用户名
                              password:密码
                      返回值:`Connection`数据库连接对象



### Connection

Connection数据库连接对象

1. 获取执行者对象
           获取普通执行者对象:`Statement createStatement();`
           获取预编译执行者对象:`PreparedStatement prepareStatement(Sting sql);`

2. 管理事务
           开启事务:`setAutoCommit(boolean autoCommit); `参数为false,则开启事务
           提交事务:commit();
           回滚事务:rollback();
3. 释放资源
        `void close()`
       

### Statement
Statement执行sql语句对象

1. 执行DML(增删改)语句
        `int executeUpdate(String sql)`:
                返回值int,返回影响的行数
                参数sql:可以执行insert、update、delete语句
        2.执行DQL语句
        `ResultSet executeQuery(String sql)`
                返回值ResultSet:封装查询结果
                参数sql:可以执行select语句
        3.释放资源
        `void close()`

### ResultSet
ResultSet结果集对象

1. 判断结果集当作是否还要数据`boolean next()`;
        有数据返回true,并将索引向下移动一行
        没有数据返回false

2.获取结果集中的数据:XXX getXXX("列名")
        xxx代表数据类型(要获取某列数据,这一列的数据类型)
        例如:`String getString("name)`; `int getInt("age")`
3.释放资源
        `void close()`
       
       
## 工具类
1. 编写配置文件,在src目录下创建config.properties配置文件
        config.properties:
                driverClass=com.mysql.jdbc.Driver
                url=jdbc:mysql://127.0.0.1:3306/qq
                username=root
                password=root

2.编写jdbc工具类
```Java
public class JDBCUtils {

    // 1.私有化构造方法
    private JDBCUtils(){

    }

    // 2.声明所需要的配置变量
    private static String driverClass;
    private static String url;
    private static String username;
    private static String password;
    private static Connection con;

    // 3.提供静态代码块,读取配置文件的信息为变量赋值,注册驱动
    static {
      // 读取配置文件的信息为变量赋值
      try {
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties");
            Properties properties = new Properties();
            properties.load(is);
            driverClass= properties.getProperty("driverClass");
            url= properties.getProperty("url");
            username= properties.getProperty("username");
            password= properties.getProperty("password");

            // 注册驱动
            Class.forName(driverClass);
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    // 4.提供获取数据库连接方法
    public static Connection getConnection(){
      try {
            con = DriverManager.getConnection(url,username,password);
      } catch (SQLException e) {
            e.printStackTrace();
      }

      return con;
    }

    // 5.提供释放资源的方法
    public static void close(Connection con, Statement stat, ResultSet rs){
      if (con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
      }

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

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

    // 5.提供释放资源的方法
    public static void close(Connection con, Statement stat){
      if (con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
      }

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

    }
}

```

## SQL注入攻击解决
- PreparedStatement预编译执行者对象
        1. SQL语句之前,将SQL语句进行提取编译。明确SQL语句的格式后,就不会在改变,剩余的内容都会认为时参数。
        2. SQL语句中的参数使用?占位符
- 为?占位符赋值的方法:setXxx(参数1,参数2)
        Xxx:数据类型
        参数1:?的位置编号(编号从1开始)
        参数2:?的实际参数
        ```Java
      string sq1 = "DELETE FROM user WHERE name=?"
      pstm = conn.preparestatement(sq1) ;
      pstm. setstring(1,"张三");
        ```
- 执行SQL语句
        1. 执行inset、update、delete语句:int executeUpdate();
        2. 执行select语句:ResultSet executeQuery();

```Java
public class JDBCDemo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
//      -导入jar包
//      - 注册驱动
//      Class.forName("com.mysql.jdbc.Driver");
//      - 获取数据库连接
      Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/qq", "root", "root");
//      - 获取执行者对象
      
//      - 执行sql语句并返回结果
      String sql = "SELECT * FROM user WHERE name = ?";
      PreparedStatement preparedStatement = connection.prepareStatement(sql);
      preparedStatement.setString(1,"张三");
      ResultSet resultSet = preparedStatement.executeQuery(sql);
//      - 处理结果
      while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");

            System.out.print(id + "");
            System.out.println(username);
      }
//      - 释放资源
      resultSet.close();
      preparedStatement.close();
      connection.close();
    }
}
```

## 事务管理
管理事务功能类:Connection
- 开启事务:`setAutoCommit(boolean autoCommit):参数为false,则开始事务
- 提交事务:commit();
- 回滚事务:rollback();

## 连接池
### 概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。可以有效的提升数据库操作的性能

### 自定义数据库连接池

#### DataSource
##### 概述
`Javax.sql.DataSource`接口:数据源(数据库连接池)。Java官方提供的数据库连接池规范(接口)

##### 步骤
    ① 定义一一个类,实现DataSource接口。
    ② 定义一个容器,用于保存多个Connection连接对象。
    ③ 定义静态代码块,通过JDBC工具类获取10个连接保存到容器中。
    ④ 重写getConnection方法,从容器中获取一个连接并返回。
    ⑤ 定义getSize方法,用于获取容器的大小并返回。

```Java
/**
* 自定义数据库连接池
*/

public class MyDataSource implements DataSource {
    // 准备容器,用于保存多个连接对象
    // 通过Collections.synchronizedList获取线程安全的集合对象
    private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());

    // 定义静态代码块,通过工具类获取10个连接对象
    static {
      for (int i = 0; i < 10; i++) {
            Connection connection = JDBCUtils.getConnection();
            pool.add(connection);
      }
    }

    // 重写getConnection方法,用于获取连接对象
    @Override
    public Connection getConnection() throws SQLException {
      if (pool.size()>0){
            Connection con = pool.remove(0);
            return con;
      }else{
            throw new RuntimeException("连接数量已用尽");
      }
    }

    // 定义getSize方法,获取连接池容器的大小
    public int getSize(){
      return pool.size();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
      return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
      return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
      return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
      return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
      return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
      return null;
    }
}

public class DataSourceTest {
    public static void main(String[] args) throws SQLException {
      // 创建连接池对象
      MyDataSource dataSource = new MyDataSource();

      // 通过连接池对象获取连接对象
      Connection connection = dataSource.getConnection();

      // 查询学学生表的信息
      String sql = "SELECT * FROM user";
      PreparedStatement preparedStatement = connection.prepareStatement(sql);
      ResultSet resultSet = preparedStatement.executeQuery();

      while (resultSet.next()){
            System.out.print(resultSet.getInt("id"));
            System.out.println(resultSet.getString("username"));
      }

      JDBCUtils.close(connection,preparedStatement);
    }
}

```
##### 归还连接

###### 继承方式(不能用,理解思想即可)
- 通过打印连接对象,发现DriverManager获取的连接实现类是JDBC4Connection
- 那我们就可以自定义一个类,继承JDBC4Connection这个类,重写close(方法,完成连接对象的归还

- 实现步骤
    ①定义一个类,继承JDBC4Connection.
    ②定义Connection连接对象和连接池容器对象的成员变量。
    ③通过有参构造方法完成对成员变量的赋值。
    ④重写close方法,将连接对象添加到池中。

```Java
public class MyConnection1 extends JDBC4Connection {
    private Connection connection;
    private List<Connection> pool;

    public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url, Connection connection, List<Connection> pool) throws SQLException {
      super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
      this.connection = connection;
      this.pool = pool;
    }

    @Override
    public void close() throws SQLException{
      pool.add(connection);
    }
}
```

###### 装饰设计模式
- 我们可以自定义-个类实现Connection接口。这样就具备了和JDBC4Connection相同的行为了
- 重写close(方法,完成连接的归还。其余的功能还调用mysql驱动包实现类原有的方法即可
- 实现步骤:
        ①定义一个类,实现Connection接口
    ②定义Connection连接对象和连接池容器对象的成员变量
    ③通过有参构造方法完成对成员变量的赋值
    ④重写close(方法,将连接对象添加到池中
    ⑤剩余方法,只需要调用mysql驱动包的连接对象完成即可
    ⑥在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装

注:这里把开头的几个做了个模板,后面的没做
```Java
package com.BJ.JDBC.JDBCPool.Demo3;

import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class MyConnection2 implements Connection {
    private Connection connection;
    private List<Connection> pool;

    public MyConnection2(Connection connection, List<Connection> pool) {
      this.connection = connection;
      this.pool = pool;
    }

    @Override
    public Statement createStatement() throws SQLException {
      return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
      return connection.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
      return connection.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
      return null;
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {

    }

    @Override
    public boolean getAutoCommit() throws SQLException {
      return false;
    }

    @Override
    public void commit() throws SQLException {

    }

    @Override
    public void rollback() throws SQLException {

    }

    @Override
    public void close() throws SQLException {
      pool.add(connection);
    }

    @Override
    public boolean isClosed() throws SQLException {
      return false;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
      return null;
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {

    }

    @Override
    public boolean isReadOnly() throws SQLException {
      return false;
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {

    }

    @Override
    public String getCatalog() throws SQLException {
      return null;
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {

    }

    @Override
    public int getTransactionIsolation() throws SQLException {
      return 0;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
      return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
      return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
      return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
      return null;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
      return null;
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

    }

    @Override
    public void setHoldability(int holdability) throws SQLException {

    }

    @Override
    public int getHoldability() throws SQLException {
      return 0;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
      return null;
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
      return null;
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {

    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
      return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
      return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
      return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
      return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
      return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
      return null;
    }

    @Override
    public Clob createClob() throws SQLException {
      return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
      return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
      return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
      return null;
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
      return false;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {

    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {

    }

    @Override
    public String getClientInfo(String name) throws SQLException {
      return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
      return null;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
      return null;
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
      return null;
    }

    @Override
    public void setSchema(String schema) throws SQLException {

    }

    @Override
    public String getSchema() throws SQLException {
      return null;
    }

    @Override
    public void abort(Executor executor) throws SQLException {

    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {

    }

    @Override
    public int getNetworkTimeout() throws SQLException {
      return 0;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
      return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
      return false;
    }
}

```
###### 适配器设计模式
- 我们可以提供一个适配器类,实现Connection接口,将所有方法进行实现(除了close方法)
- 自定义连接类只需要继承这个适配器类,重写需要改进的close()方法即可
- 实现步骤
        ①定义一个适配器类,实现Connection接口。
    ②定义Connection连接对象的成员变量。
    ③通过有参构造方法完成对成员变量的赋值。
    ④重写所有方法(除了close ) ,调用mysq驱动包的连接对象完成即可.
    ⑤定义一一个连接类,继承适配器类。
    ⑥定义Connection连接对象和连接池容器对象的成员变量,并通过有参构造进行赋值。
    ⑦重写close0方法,完成归还连接。
    ⑧在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。


###### 动态代{过}{滤}理模式
- 我们可以通过Proxy来完成对Connection实现类对象的代{过}{滤}理
- 代{过}{滤}理过程中判断如果执行的是close方法,就将连接归还池中。如果是其他方法则调用连接对象原来的功能即可

- 动态代{过}{滤}理:在不改变目标对象方法的情况下对方法进行增强
    - 组成:
      被代{过}{滤}理的对象:真实的对象
      代{过}{滤}理对象:内存中的一个对象
    - 要求:
      代{过}{滤}理对象必须和被代{过}{滤}理对象实现相同的接口
    - 实现
      Proxy.newProxyInstance()

- 实现步骤
    ①定义一个类,实现DataSource接口
    ②定义一个容器,用于保存多个Connection连接对象
    ③定义静态代码块,通过JDBC工具类获取10个连接保存到容器中
    ④重写getConnection方法,从容器中获取一个连接
    ⑤通过Proxy代{过}{滤}理,如果是close方法,就将连接归还池中。如果是其他方法则调用原有功能
    ⑥定义getSize方法,用于获取容器的大小并返回



### C3P0

#### 步骤
    ①导入jar包。
    ②导入配置文件到src目录下。
    ③创建C3PO连接池对象。
    ④获取数据库连接进行使用。
   
    注:C3P0的配置文件会自动加载,但是必须交c3p0-config.xml或c3p0-config.properties(配置文件可以去百度找,基本上都是一样的)

```Java
    public class C3P0Test1 {
    public static void main(String[] args) throws SQLException {
      // 创建c3p0的数据库连接池对象
      DataSource dataSource = new ComboPooledDataSource();
      // 通过连接池对象获取数据库连接
      Connection connection = dataSource.getConnection();
      // 执行操作
      String sql = "SELECT * FROM user";
      PreparedStatement preparedStatement = connection.prepareStatement(sql);
      ResultSet resultSet = preparedStatement.executeQuery(sql);
//      - 处理结果
      while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            System.out.print(id + "");
            System.out.println(username);
      }
//      - 释放资源
      resultSet.close();
      preparedStatement.close();
      connection.close();
    }
}


<c3p0-config>
    <!--使用默认的配置读取连接池对象-->
    <default-config>
      <!--连接参数-->
      <property name="driverClass">com.mysql.jdbc.Driver</property>
      <property name="jdbcUrl">jdbc:mysql://localhost/qq</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>
      <!-- 超时时间 -->
      <property name="checkoutTimeout">3000</property>
    </default-config>
   
</c3p0-config>


```

### Druid
    ①导入jar包。
    ②编写配置文件,放在src目录下。
    ③通过Properties集合加载配置文件。
    ④通过Druid连接池工厂类获取数据库连接池对象。
    ⑤获取数据库连接进行使用。
    注:Druid不会自动加载配置文件,需要我们手动加载,但是文件的名称可以自定义

```Java
/**
* 1.通过Properties集合,加载配置文件
* 2.通过Druid连接池工厂类获取数据库连接池对象
* 3.通过连接池对象获取数据库连接
*/
public class DruidTest1 {
    public static void main(String[] args) throws Exception {
      // 获取配置文件
      InputStream is = DruidTest1.class.getClassLoader().getResourceAsStream("druid.properties");
      // 1.通过Properties集合,加载配置文件
      Properties properties = new Properties();
      properties.load(is);

      // 通过Druid连接池工厂类获取数据库连接池对象
      DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

      // 3.通过连接池对象获取数据库连接
      Connection connection = dataSource.getConnection();

      // 执行操作
      String sql = "SELECT * FROM user";
      PreparedStatement preparedStatement = connection.prepareStatement(sql);
      ResultSet resultSet = preparedStatement.executeQuery(sql);
//      - 处理结果
      while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");

            System.out.print(id + "");
            System.out.println(username);
      }
//      - 释放资源
      resultSet.close();
      preparedStatement.close();
      connection.close();
    }
}


配置文件:(文件名:druid.properties)
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/qq
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数量
maxActive=10
# 超时时间
maxWait=3000
```

### 数据库连接池工具类抽取

```Java
/**
* 数据库连接池的工具类
*/
public class DataSourceUtils {
    // 1.私有化构造方法
    private DataSourceUtils(){}
    // 2.声明数据源变量
    private static DataSource dataSource;
    // 3.提供静态代码块,完成配置文件的加载和获取数据库连接池对象
    static {
      try{
            // 获取配置文件
            InputStream is = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            // 通过Properties集合,加载配置文件
            Properties properties = new Properties();
            properties.load(is);

            // 通过Druid连接池工厂类获取数据库连接池对象
            dataSource= DruidDataSourceFactory.createDataSource(properties);

      }catch (Exception e){
            e.printStackTrace();
      }
    }
    // 4.提供一个获取数据库连接的方法

    public static Connection getConnection(){
      Connection con = null;

      try {
            con = dataSource.getConnection();
      } catch (SQLException throwables) {
            throwables.printStackTrace();
      }

      return con;
    }

    // 5.提供一个获取数据连接池对象的方法
    public staticDataSource getDataSource(){
      returndataSource;
    }

    // 6.释放资源
    public static void close(Connection con, Statement stat, ResultSet rs){
      if (con!=null){
            try {
                con.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
      }
      if (stat!=null){
            try {
                stat.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
      }
      if (rs!=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
      }
    }

    public static void close(Connection con, Statement stat){
      if (con!=null){
            try {
                con.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
      }
      if (stat!=null){
            try {
                stat.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
      }
    }
}
```

老子就是拽 发表于 2021-7-24 11:56

666666{:1_899:}

layuai 发表于 2021-7-24 12:35

可以,内容要点基本提到,具体内容还要个人自学一下

风夜 发表于 2021-7-24 12:50

好东西了,我收藏了。

16du 发表于 2021-7-24 13:21

huhu1982 发表于 2021-7-24 13:23

谢谢分享

LongJL 发表于 2021-7-24 13:28

16du 发表于 2021-7-24 13:21
大佬写的好,排版怎么这么工整的呢?

{:301_998:}排版用markdown啊

137RickSanchez 发表于 2021-7-24 13:30

感谢分享,刚好需要。

xilongy 发表于 2021-7-24 13:50

感谢分享,学习中。

Ajin1989 发表于 2021-7-24 13:51

谢谢了 我要多看看
页: [1] 2
查看完整版本: 【自学笔记】 Java基础 - JDBC