JDBC总结


1. JDBC简介

JDBC(Java Database Connectivity):Java数据库连接。官方定义了一套操作所有关系型数据库的接口,各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们使用这套接口JDBC编程,而真正执行的代码是驱动jar包中的实现类。

2. JDBC基本使用

使用步骤:

  1. 导入驱动jar包如:mysql-connector-java-5.1.46.jar
  2. 注册驱动:Class.forName(“com.mysql.jdbc.Driver”);
  3. 使用DriverManager静态方法获取数据库的连接对象Connection:getConnection(“url”, “user”, “password”);
  4. 定义SQL语句:SQL的参数使用?作为占位符
  5. 使用Connection对象获取SQL语句执行对象Statement:prepareStatement(String sql)
  6. 使用PreparedStatement对象setXxx方法给?赋值:setObject(int parameterIndex, Object x)
  7. 执行SQL语句,获取结果集对象ResultSet
  8. 处理结果集
  9. 释放资源:结果集对象、SQL语句执行对象、连接对象

2.1 DriverManager

  • 驱动管理对象
  1. 注册驱动:告诉程序该使用哪一个数据库驱动jar

    1. 注册给定的驱动程序DriverManager
    static void registerDriver(Driver driver)
    示例:
    DriverManager.registerDriver(new Driver());
    
    2.com.mysql.jdbc.Driver类中存在静态代码块:
    static {
        try {
            // 第一次使用Driver时就会注册驱动
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    通过上面静态代码块可以看出:
    只要加载Driver到内存就可以注册驱动了:
    Class.forName("com.mysql.jdbc.Driver");
    
    注:mysql5之后的驱动jar包可以省略注册驱动的步骤
    
  2. 获取数据库连接:

    static Connection getConnection(String url, String user, String password)
    
    url:指定连接的路径
    语法:连接方式:数据库厂商名://IP(域名):端口号/数据库名
    例如:url=jdbc:mysql://localhost:3306/db01
    若MySQL版本太高,与JDBC不兼容:
    url=jdbc:mysql://localhost:3306/db01?useSSL=false
    
    user:用户名
    
    password:密码
    

2.2 Connection

  • 数据库连接对象
  1. 获取执行sql的对象:

    Statement createStatement()
    PreparedStatement prepareStatement(String sql)
    
  2. 管理事务:

    1. 开启事务:setAutoCommit(boolean autoCommit)
    参数为false代表取消自动提交,即开启事务
    一般在执行SQL之前开启事务
    
    2. 提交事务:commit()
    一般当所有SQL都执行完后提交事务
    
    3. 回滚事务:rollback()
    一般在catch中回滚事务
    抓异常时,最好选得大一些
    
    4. 设置当前连接的事务隔离级别
    void setTransactionIsolation(int level)
    1Connection.TRANSACTION_READ_UNCOMMITTED
    2Connection.TRANSACTION_READ_COMMITTED
    4Connection.TRANSACTION_REPEATABLE_READ
    8Connection.TRANSACTION_SERIALIZABLE
    
  3. 获取包含数据库元数据的对象:

    DatabaseMetaData包含此Connection对象所连接的数据库的元数据
    DatabaseMetaData getMetaData()
    

2.3 Statement

  • 执行sql的对象
  1. 执行SQL的方法:

    // 可以执行任意的SQL(了解)
    boolean execute(String sql)
    
    // 执行DML(insert、update、delete)、DDL(create,alter、drop)
    // 返回值:DML返回影响的行数,DDL返回0;可以判断DML语句是否执行成功,返回值>0的则执行成功,反之则失败
    int executeUpdate(String sql)
    
    // 执行DQL(select)
    ResultSet executeQuery(String sql)
    
  2. 子类PreparedStatement:

    1. SQL注入问题:在拼接SQL语句时,有一些SQL的特殊关键字参与字符串的拼接,会造成安全性问题

    2. 使用PreparedStatement对象可以解决SQL注入问题

    3. 预编译的SQL:参数使用?作为占位符

    4. 获取包含参数元数据的对象:

      ParameterMetaData getParameterMetaData()
      

2.4 ResultSet

  • 结果集对象,封装查询结果

  • 常用方法:

    // 游标向下移动一行并判断当前行是否是最后一行末尾(是否有数据)
    // 最初光标位于第一行之前
    boolean next()
    
    // 获取数据
    // Xxx代表数据类型
    getXxx(参数)
    参数为int:列的编号,从1开始
    参数为String:列名称
    
    使用示例:
    while (resultSet.next()) {
        System.out.println(resultSet.getInt(1));
    }
    
    // 获取包含结果集元数据的对象
    ResultSetMetaData getMetaData()
    

2.5 自定义JDBC工具类

  1. 自定义JDBC工具类:

    resources下的jdbcutils.properties文件内容示例:
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/db01?useSSL=false
    username=root
    password=123456
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.*;
    import java.util.Properties;
    
    /**
     * 自定义JDBC工具类
     *
     * @author 刘浩鹏
     * @date 2020/11/28
     */
    public class JdbcUtils {
        private static String driverClassName;
        private static String url;
        private static String username;
        private static String password;
    
        // 使用静态代码块:只会执行一次
        static {
            // 读取配置文件,获取值
            InputStream is = null;
            try {
                // 1. 创建Properties对象
                Properties properties = new Properties();
                // 2. 加载配置文件
                is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbcutils.properties");
                properties.load(is);
                // 3. 获取键的值并赋值
                driverClassName = properties.getProperty("driverClassName");
                url = properties.getProperty("url");
                username = properties.getProperty("username");
                password = properties.getProperty("password");
                // 4. 注册驱动
                Class.forName(driverClassName);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        // 获取连接,返回连接对象
        public static Connection getConnection() {
            Connection connection = null;
            // 使调用者想处理就处理,不想处理就继续由上层调用/JVM处理
            try {
                connection = DriverManager.getConnection(url, username, password);
            } catch (SQLException e) {
                e.printStackTrace();
                // 将编译期异常转成运行期异常抛出,调用者想处理就处理
                throw new RuntimeException(e.getMessage());
            }
            return connection;
        }
    
        // 释放资源:Connection、Statement、ResultSet,哪一个没有则传递null
        public static void close(Connection connection, Statement statement, ResultSet resultSet) {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  2. 使用自定义的JDBC工具类:

    在数据库创建测试表user:
    -- 创建user表
    CREATE TABLE user(
    	id INT PRIMARY KEY AUTO_INCREMENT COMMENT "id",
    	username VARCHAR(255) NOT NULL DEFAULT "" COMMENT "用户名",
    	password VARCHAR(16) NOT NULL DEFAULT "123456" COMMENT "密码",
    	balance DECIMAL(16, 3) NOT NULL DEFAULT 0.0 COMMENT "余额"
    )ENGINE=INNODB, CHARSET=utf8, COMMENT="用户表";
    
    -- 添加用户
    INSERT INTO user VALUES
    	(NULL, 'user1', 'password1', 1000),
    	(NULL, 'user2', 'password2', 2000),
    	(NULL, 'user3', 'password3', 3000);
    
    
    使用自定义的JDBC工具类:
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class JdbcUtilsTest {
        public static void main(String[] args) throws SQLException {
            // 1. 使用自定义JDBC工具类获取数据库连接
            Connection connection = JdbcUtils.getConnection();
            // 2. 定义SQL
            String sql = "SELECT id, username, balance FROM user WHERE id = ?";
            // 3. 获取SQL语句执行对象
            PreparedStatement ps = connection.prepareStatement(sql);
            // 4. 给?赋值
            ps.setObject(1, 1);
            // 5. 执行SQL语句,获取结果集对象ResultSet
            ResultSet resultSet = ps.executeQuery();
            // 6. 处理结果集
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                double balance = resultSet.getDouble("balance");
                System.out.println(id + "\t" + username + "\t" + balance);
            }
            // 7. 使用自定义JDBC工具类释放资源:结果集对象、SQL语句执行对象、连接对象
            JdbcUtils.close(connection, ps, resultSet);
            
            /*
            结果:
            1	user1	1000.0
             */
        }
    }
    

3. 数据库连接池

  1. 数据库连接池就是一个存放数据库连接的容器(链表,增删快)

  2. 数据连接池的工作机制:

    1. 程序启动时会在连接池中创建一定数量的连接,需要时从连接池中申请一个连接,使用完将连接归还给连接池
    2. 若当前连接池中没有空闲连接,则判断已使用的连接数是否达到最大连接数
    3. 若没达到最大连接数就创建一定数量的连接供使用
    4. 若达到最大连接数就让当前请求等待,直到有连接归还
    5. 若等待超时则报错或强制使用时间最久的那个连接归还
    6. 连接池会控制空闲连接数小于最大空闲连接数,多余的空闲连接会被销毁
  3. 所有连接池的标准接口:javax.sql.DataSource

    1. 获取连接:getConnection()
    2. 归还连接:Connection.close()
    • 如果连接对象Connection是从连接池中获取的,那么调用Connection.close(),则不会再关闭连接了,而是归还连接(动态代理)

3.1 c3p0

官网:https://www.mchange.com/projects/c3p0/

c3p0数据库连接池使用步骤:

  1. 导入jar包如:c3p0-0.9.5.2.jar(依赖mchange-commons-java-0.2.12.jar)或c3p0-0.9.1.2.jar

  2. 定义配置文件:

    1. 名称:必须为c3p0-config.xml
    2. 路径:必须放在src或resources下
    c3p0-config.xml配置文件样例:
    <?xml version="1.0" encoding="UTF-8" ?>
    <c3p0-config>
        <!-- 使用默认的配置读取连接池对象 -->
        <default-config>
            <!--  连接参数 -->
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/db01?useSSL=false</property>
            <property name="user">root</property>
            <property name="password">123456</property>
    
            <!-- 连接池参数 -->
            <!-- 初始化申请的连接数量 -->
            <property name="initialPoolSize">5</property>
            <!-- 最大的连接数量 -->
            <property name="maxPoolSize">10</property>
            <!-- 超时时间 -->
            <property name="checkoutTimeout">3000</property>
        </default-config>
    
        <!-- 使用指定名称的配置读取连接池对象 -->
        <named-config name="otherc3p0">
            <!--  连接参数 -->
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/db02</property>
            <property name="user">root</property>
            <property name="password">123456</property>
    
            <!-- 连接池参数 -->
            <property name="initialPoolSize">5</property>
            <property name="maxPoolSize">8</property>
            <property name="checkoutTimeout">1000</property>
        </named-config>
    </c3p0-config>
    
  3. 创建数据库连接池对象ComboPooledDataSource

    // 使用默认配置
    DataSource ds = new ComboPooledDataSource();
    // 使用指定名称配置
    DataSource ds2 = new ComboPooledDataSource("otherc3p0");
    
  4. 通过连接池对象获取连接:getConnection()

3.2 druid

官网:https://druid.apache.org/

druid(阿里提供)下载地址:https://github.com/alibaba/druid

druid数据库连接池使用步骤:

  1. 导入jar包如:druid-1.1.6.jar

  2. 定义配置文件:

    1. Properties形式
    2. 可以是任意名称,可以放在任意目录下
    druid.properties配置文件样例:
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/db01?useSSL=false
    username=root
    password=123456
    # 初始化连接数量
    initialSize=5
    # 最大连接数量
    maxActive=10
    # 最大等待时间
    maxWait=3000
    
  3. 加载配置文件Properties

  4. 通过工厂类DruidDataSourceFactory获取DataSource对象:createDataSource(Properties properties)

  5. 根据DataSource对象获取连接:getConnection()

自定义Druid连接池的工具类:

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DruidUtils {
    // 定义成员变量DataSource
    private static DataSource ds;
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    // 使用静态代码块:只会执行一次
    static {
        InputStream is = null;
        try {
            // 1. 加载配置文件
            is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties properties = new Properties();
            properties.load(is);
            // 2. 获取DataSource
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 获取连接池
    public static DataSource getDataSource() {
        return ds;
    }

    // 获取连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        return connection;
    }

    // 从ThreadLocal中获取连接
    public static Connection getConnectionFromThreadLocal() {
        Connection connection = threadLocal.get();
        if (connection == null) {
            connection = getConnection();
            threadLocal.set(connection);
        }
        return connection;
    }

    // 释放资源
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

4. DbUtils

  1. Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
  2. 使用Commons DbUtils需导入jar包,如:commons-dbutils-1.6.jar
  3. 使用步骤:
    1. 创建QueryRunner对象
    2. 定义SQL语句:SQL的参数使用?作为占位符
    3. 创建一个JavaBean类来准备接收结果集中的记录
    4. 调用QueryRunner对象执行SQL语句的方法获取结果集:update或query
    5. 处理结果集

4.1 QueryRunner类

QueryRunner类是执行SQL语句的核心类,自动维护连接

  1. 提供数据源:

    构造:
    // 创建核心类,并提供数据源,内部自己维护Connection
    QueryRunner(DataSource ds)
    
    方法:
    // 执行DML语句
    int update(String sql, Object... params)
    
    // 执行DQL语句并将查询结果封装到对象中
    <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
    
  2. 提供连接:使用事务时需要提供连接

    构造:
    // 创建核心类,没有提供数据源,在进行具体操作时需要手动提供Connection
    QueryRunner()
    方法:
    // 使用提供的Connection,完成DML语句
    int	update(Connection conn, String sql, Object... params)
    
    // 使用提供的Connection,执行DQL语句,并将查询结果封装到对象中
    <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
    

4.2 ResultSetHandler接口

ResultSetHandler接口的实现类将ResultSet转换为其他对象

4.2.1 JavaBean

JavaBean就是一个POJO(Plain Ordinary Java Object),常用于封装数据,其特征有:

  1. 是一个public类
  2. 提供无参构造
  3. 私有化字段并提供get/set方法
  4. 建议实现Serializable接口
  5. 建议重写toString方法,便于打印
  6. 属性如果是基本类型,建议声明成对应的包装类型
  • JavaBean示例:

    import lombok.Data;
    
    import java.math.BigDecimal;
    
    // 为了方便,使用lombok的Data注解在编译时自动生成get/set等方法
    @Data
    public class User {
        private int id;
        private String username;
        private String password;
        private BigDecimal balance;
    }
    
4.2.2 结果集的转换
  1. BeanHandler类:将结果集中的第一条记录封装到指定的JavaBean中

    构造:
    BeanHandler(Class<T> type)
    
  2. BeanListHandler类:将结果集中每一条记录封装到指定的JavaBean中,然后再将这些JavaBean封装到一个List集合中

    构造:
    BeanListHandler(Class<T> type)
    
  3. MapHandler类:将结果集中第一条记录封装到一个Map集合中,字段名为key,字段值为value

  4. MapListHandler类:将结果集中第一条记录封装到一个Map集合中,然后再将这些Map封装到一个List集合中

  5. ColumnListHandler类:将结果集中的指定列封装到一个List集合中

    构造:
    ColumnListHandler(String columnName)
    
  6. ScalarHandler类:将结果集中第一条记录的指定列封装到一个对象;常用来处理单值查询结果,如执行select语句后,结果集只有1个的情况

    将一个ResultSet列转换为一个对象的实现。这个类是线程安全的。
    构造:
    // 获取第一条记录的第一列
    ScalarHandler()
    
    // 获取第一条记录的指定列
    ScalarHandler(String columnName)
    
  • 处理结果集示例

    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    
    import java.sql.SQLException;
    
    public class DbUtilsTest {
        public static void main(String[] args) throws SQLException {
            // 1. 创建QueryRunner对象,使用自定义的Druid工具类获取数据库连接池对象
            QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
            // 2. 定义SQL语句:SQL的参数使用?作为占位符
            String sql = "SELECT count(id) FROM user WHERE id > ?";
            Object[] params = {0};
            // 3. 调用QueryRunner对象执行SQL语句的方法获取结果集:update或query
            Long id = qr.query(sql, new ScalarHandler<Long>(1), params);
            // 4. 处理结果集
            System.out.println(id);
            /*
            结果:
            3
             */
        }
    }
    

4.3 DbUtils工具类

  • 定义了关闭资源与事务处理的方法

    // 关闭资源
    static void	closeQuietly(Connection conn, Statement stmt, ResultSet rs)
    
    // 提交事务并关闭连接,如果为null避免关闭
    static void	commitAndCloseQuietly(Connection conn)
    
    // 回滚事务并关闭连接,如果为null避免关闭
    static void	rollbackAndCloseQuietly(Connection conn)
    

若有错误或补充,欢迎私信

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值