Java -- JDBC和连接池

JDBC和连接池

 

概述

  1. JDBC 为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节的问题
  2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,完成对数据库的操作

原理图

入门

JDBC前置准备步骤:

  1. 项目里新建目录,命名自定义
  2. 将.jar包拷贝到该目录,并将.jar文件加入到项目
    1. 右键文件 - Add as Library - OK

JDBC编写步骤:

  1. 注册驱动 - 加载Driver类
  2. 获取链接 - 得到Connection
  3. 执行增删改查  - 发送sql给MySQL执行
    1. DML - 返回影响行数
    2. DQL - 返回ResultSet对象
  4. 释放资源 - 关闭相关连接

例子:

import com.mysql.jdbc.Driver
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class jdbc01 {

    public static void main(String[] args) throws Exception {

        //1.注册驱动
        Driver driver = new Driver();

        //2.得到connection
        // (1)jdbc:mysel:// 固定写法,通过jdbc 的方式连接MySQL
        // (2)localhost 主机,可以是ip地址
        // (3)3306 表示MySQL监听的端口
        // (4)zzz_db01 连接到MySQL dbms的数据库
        // (5)MySQL的连接本质就是Socket连接
        String url = "jdbc:mysel://localhost:3306/zzz_db01";
        //将用户名,密码放入Properties对象
        Properties properties = new Properties();
        //user 和 password 传参是规定好的
        properties.setProperty("user","root"); //用户名
        properties.setProperty("password","zzz"); //密码

        Connection connect = driver.connect(url, properties);

        //3.执行SQL
        String sql = "insert into actor values (null, '刘得话', '男', '1970-1-1', '110')";

        //statement 用于执行静态SQL语句,并返回生成的结果和对象
        Statement statement = connect.createStatement();
        //DML返回受影响的行数,大于0则成功
        int i = statement.executeUpdate(sql);

        //4.关闭资源
        statement.close();
        connect.close();

    }
}

连接数据库的五种方式

1.获取Driver实现类对象

                如上方(入门:例子)代码

2.反射机制 ,动态加载,更加灵活,减少依赖性

import com.mysql.jdbc.Driver
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class jdbc01 {
    public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class cls = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) cls.getDeclaredConstructor().newInstance();
    }
}

 3.DriverManager替换Driver统一管理

import com.mysql.jdbc.Driver
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class jdbc01 {
    public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class cls = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) cls.getDeclaredConstructor().newInstance();

        String url = "";
        String user = "";
        String password = "";

        DriverManager .registerDriver(driver);//注册驱动

        Connection connection = DriverManager.getConnection(url,user,password);

    }
}

4.Class.forName Driver静态代码自动完成注册驱动,简化代码(开发中经常使用)

import com.mysql.jdbc.Driver
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class jdbc01 {
    public static void main(String[] args) throws Exception {

        //1.使用反射加载Driver类
        // 在加载Driver类时,Driver的静态代码也加载了
        // 静态代码完成了注册工作,则完成注册
        Class.forName("com.mysql.jdbc.Driver");

        String url = "";
        String user = "";
        String password = "";

        Connection connection = DriverManager.getConnection(url,user,password);

    }
}

 5.在方式4的基础上使用配置文件,连接数据库更加灵活

配置文件内容和目录级别

package JDBC_study;

import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;

public class jdbc01 {

    public static void main(String[] args) throws Exception {
        //通过properties获取相关配置文件信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));

        //获取相关值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        //传入参数
        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);


    }
}

API

DriverManager

驱动管理类,getConnection(url,user,pwd) 获取到连接

Statement(存在SQL注入)

查询语句如下,需要输入账号密码

        select * from admin where user= '  '  and pwd = '  ';

SQL注入:当输入的内容为

        user:1' or 

        pwd: or '1'='1

则where条件筛选永远成立,注入成功,能获取到其他的数据

 select * from admin where user= ' 1' or   '  and pwd = '  or '1'='1 ';

PerparedStatement

预处理,防止SQL注入

使用预处理的方法connection.getPreparedStatement(sql),可以防止直接传参导致SQL注入

package JDBC_study;

import com.mysql.jdbc.Driver

import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

public class jdbc01  {
    public static void main(String[] args) throws Exception {

        //1.使用反射加载Driver类
        // 在加载Driver类时,Driver的静态代码也加载了
        // 静态代码完成了注册工作,则完成注册
        Class.forName("com.mysql.jdbc.Driver");

        String url = "root";
        String user = "zzz";
        String password = "jdbc:mysel://localhost:3306/zzz_db01";

        Connection connection = DriverManager.getConnection(url,user,password);

        Scanner scanner = new Scanner(System.in);

        //获取输入的值
        String userId = scanner.nextLine();
        String pwd = scanner.nextLine();


        //组织sql语句,语句的 ? 相当于占位符
        String sql = "select name, id, age where user = ? and pwd = ?";
        //PreparedStatement对象实现了PreparedStatement接口的实现类的对象
        PreparedStatement preparedStatement =  connection.getPreparedStatement(sql);

        //给 ? 占位符赋值
        preparedStatement.setString(1,userId);
        preparedStatement.setString(2,pwd);

        //因为上方已经预处理了sql语句,且赋值了,所以此处执行sql的方法就不需要传参了
        preparedStatement.executeQuery();

    }
}
DML预处理
package JDBC_study;

import com.mysql.jdbc.Driver

import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

public class jdbc01  {
    public static void main(String[] args) throws Exception {

        //1.使用反射加载Driver类
        // 在加载Driver类时,Driver的静态代码也加载了
        // 静态代码完成了注册工作,则完成注册
        Class.forName("com.mysql.jdbc.Driver");

        String url = "root";
        String user = "zzz";
        String password = "jdbc:mysel://localhost:3306/zzz_db01";

        Connection connection = DriverManager.getConnection(url,user,password);

        Scanner scanner = new Scanner(System.in);

        //获取输入的值
        String userId = scanner.nextLine();
        String pwd = scanner.nextLine();


        //组织sql语句,语句的 ? 相当于占位符
        String sql1 = "insert into actor values(?, ?)";
        String sql2 = "update actor set name = ? where age = ?";
        String sql3 = "delete from actor where name = ? or age = ?";

        //PreparedStatement对象实现了PreparedStatement接口的实现类的对象
        PreparedStatement preparedStatement =  connection.getPreparedStatement(sql);

        //给 ? 占位符赋值
        preparedStatement.setString(1,userId);
        preparedStatement.setString(2,pwd);

        //因为上方已经预处理了sql语句,且赋值了,所以此处执行sql的方法就不需要传参了
        preparedStatement.executeUpdate();

    }
}

ResultSet

ResultSet对象保持一个光标指向其当前的数据行;

最初,光标位于第一行之前(标题行)。

  • next()方法:将光标移动到下一行
    • 当ResultSet对象中没有更多行时,返回false,因此可以使用while 循环遍历结果集
  • previous()方法:光标向上移动一行
  • getXxx(列的索引或者列名)方法:获取该行的某一列
        ...
        ResultSet rs = statement.executeQuery(sql);
        while(rs.next()){
            int id = rs.getInt(1);//获取该行的第 1 列的数据
            String name = rs.getString(2);//获取该行的第 2 列的数据
            int age = rs.getInt(3);//获取该行的第 3 列的数据
            Date date = rs.getDate(4);//获取该行的第 4 列的数据
            System.out.println(id +"\t" + name +"\t" + age +"\t" + date);
        }

Utils

在实际开发过程中,获取连接和释放资源是经常用到的,可以将其封装JDBC连接的工具类JDBCUtils

事务

批处理

当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理;

JDBC 批量处理语句

        addBatch() :添加需要批量处理的SQL语句或者参数

        executeBatch() :执行批量处理语句

        clearBatch() :清空批处理包的语句

  

JDBC批处理在链接MySQL时,需要添加参数

如果要使用批量处理功能,请在URL中添加参数  : ?rewriteBatchedStatements=true

批处理搭配Preparedstatement使用

减少编译次数,减少运行次数,提高效率

传统:sql语句一条一条发送给MySQL执行


        ...
        //组织sql语句,语句的 ? 相当于占位符
        String sql1 = "insert into actor values(?, ?)";
        //PreparedStatement对象实现了PreparedStatement接口的实现类的对象
        PreparedStatement preparedStatement =  connection.getPreparedStatement(sql);

        for (int i = 0; i < 5000; i++) {
            preparedStatement.setString(1,"jack"+i);
            preparedStatement.setString(2,"666");
            preparedStatement.executeUpdate();
        }

批处理:sql语句打包一起发送给MySQL执行

 public void batch(){
        ...
        //组织sql语句,语句的 ? 相当于占位符
        String sql1 = "insert into actor values(?, ?)";
        //PreparedStatement对象实现了PreparedStatement接口的实现类的对象
        PreparedStatement preparedStatement =  connection.getPreparedStatement(sql);

        for (int i = 0; i < 5000; i++) {
            preparedStatement.setString(1,"jack"+i);
            preparedStatement.setString(2,"666");
            //将sql语句加入到批处理包中
            preparedStatement.addBatch();
            //当有1000条记录时,再批量执行
            if ((i+1)%1000 == 0){
                preparedStatement.executeBatch();
                //清空一把
                preparedStatement.clearBatch();
            }


        }

连接池

传统连接带来的问题

传统连接步骤:

  1.         得到连接
  2.         发送sql到mysql
  3.         关闭连接
  • 传统的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址,用户名和密码(耗时0.05-1s之间)
  • 频繁的进行数据库连接操作将占用很多的系统资源,容易造成数据库崩溃
  • 每次连接使用完都要断开,如果程序异常未能关闭,将导致数据库内存泄漏,最终导致重启数据库
  • 传统连接的方式不能控制连接数量,如果连接过多,也可能导致内存泄漏,MySQL崩溃

数据库连接池介绍

  • 使用连接:预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从缓冲池中取到一个,使用完毕后放回去(引用断开,连接对象还在)
  • 重复使用:数据库连接池负责分配,管理和释放数据连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新创建一个
  • 请求超出当前连接:当应用程序向连接池请求的连接数量大于连接数量时,这些请求将被加入到等待队列中

连接池的种类

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现(提供相应的.jar包)

DataSource
DBCP

速度相对C3P0较快,但不稳定

C3P0(旧系统会用到)

老牌的数据库连接池技术,速度相对较慢,稳定性好

连接方式1 : 


import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.SQLException;

public class C3P001 {


    public void testC3P0_01() throws SQLException {

        //创建数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();

        //通过配置文件获取相关信息:musql.properties
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相关值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        //给数据源comboPooledDataSource 设置相关的参数
        // 连接的管理是由comboPooledDataSource来管理
        comboPooledDataSource.setDriverClass(driver);
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);

        //初始化数据源的连接数
        comboPooledDataSource.setInitialPoolSize(10);
        //设置最大连接数
        comboPooledDataSource.setMaxPoolSize(50);
        //获得连接
        Connection connection = comboPooledDataSource.getConnection();//此方法是从DataSource实现
        //关闭连接
        connection.close();
    }

    //使用配置文件模板来完成
    //将C3P0提供的c3p0-config.xml拷贝到src目录下
    //该文件指定了连接数据库和连接池的相关参数
    public void testC3P0_02() throws SQLException {


    }

}
 连接方式2:使用c3p0-config.xml文件连接

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <named-config name="zzz_dbs">
        <!-- 配置数据库用户名 -->
        <property name="user">root</property>
        <!-- 配置数据库密码 -->
        <property name="password"></property>
        <!-- 配置数据库链接地址 -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/zzz_db01?useUnicode=true&amp;characterEncoding=UTF-8</property>
        <!-- 配置数据库驱动 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!-- 数据库连接池一次性向数据库要多少个连接对象 -->
        <property name="acquireIncrement">20</property>
        <!-- 初始化连接数 -->
        <property name="initialPoolSize">10</property>
        <!-- 最小连接数 -->
        <property name="minPoolSize">5</property>
        <!--连接池中保留的最大连接数。Default: 15 -->
        <property name="maxPoolSize">30</property>
        <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
        <property name="maxStatements">0</property>
        <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
        <property name="maxStatementsPerConnection">0</property>
        <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能 通过多线程实现多个操作同时被执行。Default:3 -->
        <property name="numHelperThreads">3</property>
        <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
        <property name="propertyCycle">3</property>
        <!-- 获取连接超时设置 默认是一直等待单位毫秒 -->
        <property name="checkoutTimeout">1000</property>
        <!--每多少秒检查所有连接池中的空闲连接。Default: 0 -->
        <property name="idleConnectionTestPeriod">3</property>
        <!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
        <property name="maxIdleTime">10</property>
        <!--配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。 -->
        <property name="maxIdleTimeExcessConnections">5</property>
        <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
        <property name="acquireRetryDelay">1000</property>
        <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null -->
        <property name="automaticTestTable">Test</property>
        <!-- 获取connnection时测试是否有效 -->
        <property name="testConnectionOnCheckin">true</property>
    </named-config>
</c3p0-config>

 代码

    public void testC3P0_02() throws SQLException {

        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("zzz_dbs");

        Connection connection = comboPooledDataSource.getConnection();

        connection.close();
    }
Proxool

有监控连接池状态的功能,稳定性相对C3P0差一点

BoneCP

速度快

Druid(较新技术,新开发使用最多)

阿里提供的数据库连接池,集DBCP,C3P0,Proxool于一身的数据库连接池

准备工作:

  1. 项目里新建一个只保存第三方包的目录,命名自定义
  2. 将druid.jar包拷贝到该目录,并将.jar文件加入到项目
    1. 右键文件 - Add as Library - OK
  3. 加入druid.properties配置文件,拷贝到项目的src目录
配置文件:
driverClassName=com.mysql.jdbc.Driver //驱动加载
url=jdbc:mysql://127.0.0.1:3306/zzz_db01?characterEncoding=utf-8 //注册驱动
username=root //连接数据库的用户名
password=root //连接数据库的密码。
filters=stat  //属性类型的字符串,通过别名的方式配置扩展插件, 监控统计用的stat 日志用log4j 防御sql注入:wall
initialSize=2 //初始化时池中建立的物理连接个数。
maxActive=50 //最大的可活跃的连接池数量
maxWait=5000 //获取连接时最大等待时间,单位毫秒,超过连接就会失效。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
timeBetweenEvictionRunsMillis=60000 // 连接回收器的运行周期时间,时间到了清理池中空闲的连接,testWhileIdle根据这个判断
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1 //用来检测连接是否有效的sql,要求是一个查询语句。
testWhileIdle=true //建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效。
testOnBorrow=false //申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。设置为false
testOnReturn=false //归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,设置为flase
poolPreparedStatements=false //是否缓存preparedStatement,也就是PSCache。
maxPoolPreparedStatementPerConnectionSize=200 // 池中能够缓冲的preparedStatements语句数量
 连接数据库连接池
    public void testDruid() throws Exception {
        //创建Properties对象,读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));

        //创建一个指定参数的数据库连接池,druid连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        Connection connection = dataSource.getConnection();
        System.out.println("连接成功");
        connection.close();
    }
基于德鲁伊数据库连接池的工具类
package JDBC_study;

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

public class JDBCUtilsByDruid {

    private static DataSource dataSource ;
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src\\druid.properties"));
            dataSource  = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
    }

    //关闭连接,在数据库连接池技术中,close()方法不是duan断掉连接
    // 而是把使用的Connection对象放回连接池
    public static void close(ResultSet resultSet, Statement statement, Connection connection){
        try {
            if (resultSet != null){
                resultSet.close();
            }else if (statement != null){
                statement.close();
            } else if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

Apache - DBUtils

保存数据时候会遇到的问题

分析:

  1. 关闭Connection后,ResuleSet结果集无法使用
  2. ResultSet不利于数据的管理
  3. ResultSet使用返回信息也不方便

解决方法:

JavaBean(PoJO,Domain)

  1. 定义一个类A,类的成员属性名跟表的字段名一致,映射一张表
  2. 返回的一行结果集记录,保存到A实例对象里,再用ArrayList保存A对象,实现保存表数据

引出Apache - DBUtils

基本介绍:

commons-dbutils 是Apache组织提供的一个开源的JDBC工具类库,他是对JDBC的封装,使用dbutils能极大简化JDBC编码的工作量

DbUtils类:

  1. QueryRunner类,该类封装了SQL 的执行,是线程安全,可以实现增删查改,批处理
  2. 使用QueryRunner类实现查询
  3. ResultSetHandler接口,该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式

前置准备

  1. 项目里新建一个只保存第三方包的目录,命名自定义
  2. 将commons-dbutils.jar包拷贝到该目录,并将.jar文件加入到项目
    1. 右键文件 - Add as Library - OK
  3. 加入druid.properties配置文件,拷贝到项目的src目录
    使用Apache-DBUtils 工具类 + druid 完成对表的crud操作
package JDBC_study;

import java.sql.Connection;

public class DBUtils_USE {
    //使用Apache-DBUtils 工具类 + druid 完成对表的crud操作

    public void testQueryMany(){//返回多行数据
        //得到连接
        Connection connection = JDBCUtilsByDruid.getConnection();

        //使用DBUtils类和接口,先引入DBUtils相关的jar,加入到本Project
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        String sql = "select * from actor where id = ? or sex = ? or name = ?";

        /*
            1. query()就是执行一个sql 语句,并返回resultset ,封装到 ArrayList集合中
            2. 返回集合

            传入参数解读:
            (1) connection :连接
            (2) sql : sql语句
            (3) new BeanListHandler<>(Actor.class) : 在将resultset 取出到 Actor对象,封装到 ArrayList 中
                底层使用了反射机制,查看获取Actor类 的属性,然后进行封装
            (4) 1,2,3 :就是sql中的?,是可变参数
         */
        List<Actor> list = queryRunner.query(connection,sql,new BeanListHandler<>(Actor.class),1,2,3);

        //(5) 底层得到的resultset,会在query关闭,还会关闭PreparedStatment
        JDBCUtilsByDruid.close(null,null,connection);
    }


    public void testQuerySingle(){//返回单行数据 BeanHandler
        //得到连接
        Connection connection = JDBCUtilsByDruid.getConnection();

        //使用DBUtils类和接口,先引入DBUtils相关的jar,加入到本Project
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        String sql = "select * from actor where id = ?";

        Actor actor = queryRunner.query(connection,sql,new BeanHandler<>(Actor.class),1);

        JDBCUtilsByDruid.close(null,null,connection);
    }



  public void testQueryScalar(){//返回单行单列数据 ScalarHandler
        //得到连接
        Connection connection = JDBCUtilsByDruid.getConnection();

        //使用DBUtils类和接口,先引入DBUtils相关的jar,加入到本Project
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        String sql = "select name from actor where id = ?";

        Object o = queryRunner.query(connection,sql,new ScalarHandler<>(Actor.class),1);

        JDBCUtilsByDruid.close(null,null,connection);
    }



    public void testDML(){//返回单行单列数据 ScalarHandler
        //得到连接
        Connection connection = JDBCUtilsByDruid.getConnection();

        //使用DBUtils类和接口,先引入DBUtils相关的jar,加入到本Project
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        String sql1 = "update actor set name = ? where id = ?";
        String sql2 = "delete from actor where id = ?";
        String sql3 = "insert into actor values(null,?,?,?)";

        //返回值是受影响的行数
        int affectdRow = queryRunner.update(connection,sql,1);
        System.out.println(affectdRow > 0? "成功":"没有影响到表");

        JDBCUtilsByDruid.close(null,null,connection);
    }

}

DAO增删改查 - BasicDao

Apache - DBUtils会遇到的问题

问题分析:

  1. Apache - DBUtils简化了JDBC的开发,但还有不足
  2. SQL语句是固定的,不能通过参数传入,通用性不好,需要进行改进,更方便地执行curd
  3. 对于select来说,如果有返回值,返回类型不能固定,需要使用泛型
  4. 将来的表很多,业务需求复杂,不可能只靠一个Java完成

引出DAO(Data access object)数据访问对象

  • BasicDAO :通用类,是专门和数据库交互的,即完成对数据库的curd操作
  • 在BasicDAO的基础上,实现一张表对应一个DAO,更好地完成功能
    • Actor表 -- Actor.java类 -- ActorDAO.java
  • 包com.zzz.dao_
    • dao          存放XxxDAO和BasicDAO
      • BasicDAO
    • domain    javabean
    • utils         工具类
      • JDBCUtilsByDruid

    • test          写测试类
BasicDAO
package com.zzz.dao_.dao;

import com.zzz.dao_.utils.JDBCUtilsByDruid;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class BasicDAO<T> {

    private QueryRunner qr = new QueryRunner();

    public int updateDAO(String sql, Object... parameters){

        Connection connection = null;
        try {
           connection =  JDBCUtilsByDruid.getConnection();
           int update = qr.update(connection,sql,parameters);
           return update;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null, null,connection);
        }
    }

    //查询结果多行,针对任意表

    /**
     *
     * @param sql  sql语句
     * @param cls  传入一个类的Class对象,反射会使用到,如Actor.class
     * @param parameters  传入问号具体值
     * @return
     */
    public List<T> queryMulti(String sql, Class<T> cls, Object... parameters){

        Connection connection = null;
        try {
            connection =  JDBCUtilsByDruid.getConnection();
            return  qr.query(connection,sql,new BeanListHandle<>(cls),parameters);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null, null,connection);
        }

    }

    public List<T> querySingle(String sql, Class<T> cls, Object... parameters){

        Connection connection = null;
        try {
            connection =  JDBCUtilsByDruid.getConnection();
            return qr.query(connection,sql,new BeanHandle<>(cls),parameters);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null, null,connection);
        }

    }

    public List<T> queryScalar(String sql, Object... parameters){

        Connection connection = null;
        try {
            connection =  JDBCUtilsByDruid.getConnection();
            return qr.query(connection,sql,new ScalarHandle(),parameters);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null, null,connection);
        }

    }


}
JDBCUtilsByDruid
package com.zzz.dao_.utils;

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

public class JDBCUtilsByDruid {

    private static DataSource dataSource ;
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src\\druid.properties"));
            dataSource  = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
    }

    //关闭连接,在数据库连接池技术中,close()方法不是duan断掉连接
    // 而是把使用的Connection对象放回连接池
    public static void close(ResultSet resultSet, Statement statement, Connection connection){
        try {
            if (resultSet != null){
                resultSet.close();
            }else if (statement != null){
                statement.close();
            } else if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值