数据库之JDBC

JDBC

主要参考资料:尚硅谷-宋红康-JDBC

一、什么是JDBC?

  1. JDBC是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口。可以理解为它就是位于Java程序与不同数据库中间的一组规范,向上提供一套Java API,使得Java程序在使用不同数据库时有统一的方式,向下提供面向不同的Java Driver API,使得不同的开发商在开发数据库管理系统的时候都去实现这些接口,统一操作的调用方法的目的。JDBC和Java程序以及数据库管理系统之间的关系类似于JVM与class文件以及操作系统之间的关系。这样的好处在于可以屏蔽掉在使用不同的数据库管理系统时的不同细节,同时也可以对操作数据库的功能进一步封装,使得对数据库的操作更加便利。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-873SbkBh-1646228837180)(C:\Users\Flyin\AppData\Roaming\Typora\typora-user-images\image-20220223101725743.png)]

二、获取数据库连接

  1. 首先,要加载和注册JDBC驱动。通过调用Class类的静态方式forName()加载需要的JDBC驱动,然后再使用DriverManager类的registerDriver()方法进行注册驱动,但通常情况下这一步不需要显示的调用,因为Driver接口的静态代码块中就会调用这个方法。

  2. 然后,通过JDBC URL选择正确的驱动程序,从而建立到数据库的连接。JDBC URL的标准由协议:子协议:子名称三部分组成,各部分用冒号分隔,再JDBC中协议总是jdbc,而子协议用于标识一个数据库驱动程序,子名称是用来标识数据库的,或者说是为了定位数据库提供足够的信息。

  3. 最后,通过用户名和密码连接登录数据库。

  4. 以下是一种连接方式,其中使用了配置文件。

    public void JDBCConnection() throws Exception {
        // 加载配置文件
        InputStream in = Connection.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(in);
        // 读取配置信息
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass =  pros.getProperty("driverClass");
        // 加载驱动(加载驱动类的时候会触发静态代码块中的代码,进一步完成注册的动作)
        Class.forName(diverClass);
        // 获取连接(通过url、用户名、密码连接指定数据库)
        Connection con = DriverManager.getConnection(url, user, password);
    }
    
    user=root
    password=123456
    url=jdbc:mysql://localhost:3306/test
    #jdbc是协议,mysql是子协议,后面的都是子名称,localhost就是主机名(对应着ip地址,3306是MySQL的端口号,test是数据库的名字)
    driverClass=com.mysql.jdbc.Driver
    

三、实现CRUD操作

  1. 使用Statement类操作数据库:用于执行静态SQL语句,通过Connection对象的createStatement()方法创建该类对象,通过这个对象的int excuteUpdate(String sql)和ResultSet executeQuery(String sql)两个方法分别完成curd操。但是这种方法存在着繁琐和SQL注入问题。

  2. 使用PreparedStatement操作数据库:可以通过Connection对象的preparedStatement(String sql)方法获取该类对象,并将一条SQL语句预编译,可多次执行这条SQL语句,在这条语句中可以用问号(?)来表示SQL语句中的参数,使用中可以调用preparedStatement类的setXxx()方法对某个参数赋值,注意此时参数的索引是从1开始的。

  3. 对于上述内容中提到的查询方法时会返回一个ResultSet对象,这个对象就是以逻辑表格的形式封装了执行数据库操作的结果集,实际上就是一张数据表,同时维护了一个指向数据行的游标,初始的时候,游标在第一行之前,可以通过next()方法进行检测下一行是否有效,同时游标还会向下移。调用ResultSet对象的getMetaData()方法,会返回一个ResultSetMetaData对象,这个对象可以用来获取关于ResultSet对象中列的类型和属性信息。

  4. 在对数据库操作完毕之后,也要做到资源的释放,如ResultSet,Statement和Connection,一般直接调用close方法即可,也可以使用JDBCUtils.closeResource()方法。

  5. // 使用PrepareStatement实现查询操作
    public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 1.获取数据库连接
            conn = JDBCUtils.getConnection();
            // 2.预编译sql语句
            ps = conn.prepareStatement(sql);
            // 3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, arg[i]);
            }
            // 4.执行executeQuery(),得到结果集:ResultSet
            rs = ps.executeQuery();
           	// 5.得到结果集的元数据:ResultSetMetaData
            ResultSetMetaData rsmd = rs.getMeteData();
            // 6.通过ResultSetMetaData得到相关数据
            int columnCount = rsmd.getColumnCount();
            if (rs.next()) {
                T t = clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    // 获取列值
                    Object columnVal = rs.getObject(i + 1);
                    // 获取列的别名,使用类的属性名充当
                    String columnLabel = remd.getColumnLabel(i + 1);
                    // 使用反射,给对象的相应属性赋值
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t, columnVal);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 7.关闭资源
            JDBCUtils.closeResource(conn, ps, rs);
        }
    }
    
  6. 批量执行SQL语句,这是采用Java的批量更新机制,这一机制,允许多条语句一次性提交给数据库批量处理,提高效率。主要是使用如下的三个方法完成:

    • addBatcch(String):添加需要批量处理的SQL语句或者是参数
    • executeBath():执行批量处理语句
    • clearBatch():清空缓存的数据
    # 创建一个表,用于测试	
    CREATE TABLE goods(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20)   
    );
    
    // 批量插入,MySQL服务器默认是关闭批处理的,我们需要通过一个参数,让MySQL开启批处理的支持。?rewriteBatchedStatements=true写在配置文件的url后面
    @Test
    public void testInsert() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        // 一、设置为不自动提交数据
        conn.setAutoCommit(false);
        String sql = "insert into goods(name)values(?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 1; i <= 1000000; i++) {
            ps.setString(1, "name_" + i);
            //1. “攒”sql语句
            ps.addBatch();
            if (i % 500 == 0) {
                // 2.执行sql语句
                ps.executeBatch();
                // 3.清空sql语句的缓存
                ps.clearBatch();
            }
        }
        // 二、提交数据
        conn.commit();
     }
    

三、数据库连接池

  1. 一个传统JDBC连接数据库,会出现数据库的连接资源没有得到有效的利用、对于每一次数据库连接使用后都需要断开、不能控制连接数量等问题。为了解决这些问题,可以采用数据库连接池技术。数据库连接池技术主要的思想就是先建立一个连接缓冲池,里面放入一定数量的连接,对于这些连接,使用时取出,使用完毕后放回。全程由数据库连接池负责分配、管理和释放数据库连接,同时也可以提前为连接池进行一些相关数据的配置。采用数据库连接池技术可以有效利用资源,避免了频繁的创建和释放,同时提高了系统的反应速度以及可以对数据库的连接进行一些必要的限制,初次之外,还可以在连接池内部的实现进行一些功能的封装。
  2. JDBC的数据库连接池使用javax.sql.DataSource来表示,这是一个接口,该接口通常由服务器和一些开源组织提供实现,这些数据库有DBCP、C3P0、Proxool、BoneCP、Druid。
// 演示获取C3P0数据库连接池的连接,该连接池速度较慢,但稳定性还可以
private static DataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection() throws SQLException {
    Connection conn = cpds.getConnection();
    return conn;
}
<!--以下是C3P0的配置文件-->
<?xml version="1.0" encoding="UTE-8"?>
<c3p0-config>
    <named-config name="helloc3p0">
        <!-- 获取连接的4个基本信息 -->
        <property name="user">root</property>
        <property name="password">abc123</property>
        <property name="jdbcUrl">jdbc:mysql:///test</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!-- 涉及到数据库连接池的管理的相关属性的设置 -->
        <!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">5</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">5</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">10</property>
        <!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
        <property name="maxStatements">20</property>
        <!-- 每个连接同时可以使用的 Statement 对象的个数 -->
        <property name="maxStatementsPerConnection">5</property>
    </named-config>
</c3p0-config>
// 演示Druid(德鲁伊)数据库连接池,可以说是目前最好的连接池之一
public class TestDruid {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();
        pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        Connection conn = ds.getConnection();
    }
}
# 以下是druid连接池需要的配置文件
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
  • 详细配置参数:
配置缺省说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter
driverClassName根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

四、Apache-DBUtils实现CRUD操作

  1. commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。主要的API需要学习的是:org.apache.commons.dbutils.QueryRunner、org.apache.commons.dbutils.ResultSetHandler和org.apache.commons.dbutils.DbUtils
  2. commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。主要的API需要学习的是:org.apache.commons.dbutils.QueryRunner、org.apache.commons.dbutils.ResultSetHandler和org.apache.commons.dbutils.DbUtils
  3. DbUtils是提供关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。QueryRunner类简化了SQL查询,它与ResultSetHandler组合一起使用可以完成大部分的数据库操作,能够大大减少编码量。ResultSetHandler接口以及实现类主要用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值