java_Jdbc ,Apache-DBUtils,BasicDAO知识整理

java_Jdbc

Jdbc原理说明

jdbc 本质上是一系列的接口,对于mysql 数据库厂商而言,它需要设计一个
类来实现接口的所有方法,以完成这个方法的功能(即数据库驱动)。

因为不同的数据库实现相同的功能,可能需要不同的操作去实现,如果把这些问题
全都留给程序员来解决无疑是很大的负担(使用也不方便,不利于维护),极大影响了
开发效率和维护成本。所以便有了 Jdbc,它规定了一系列接口和接口实现的功能(抽象),
具体功能的实现就交由对应的厂商来负责。

这样对于不同的数据库,都可以运用一套API来操作,极大的遍历的程序员使用数据库。

Jdbc使用入门

1.注册驱动 - 加载Driver类

2.获取连接 - 加载Connection类

3.执行增删改查 - 发送SQL语句给mysql执行(如果有返回结果,则接收返回结果)

4.释放资源 - 关闭相关连接

简单举例

package Jdbc;
import com.mysql.cj.jdbc.Driver;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
  * @author WJ
  * @date 2022/7/26 17:59
  * @version 1.0
  * 入门使用 jdbc 连接数据库(知识梳理)
  */
public class connection1 {

    public static void main(String[] args) throws SQLException {
        //1 初始化驱动
        Driver driver = new Driver();
        //2 建立连接
        // jdbc:mysql:// 表示使用的是什么数据库
        // 127.0.0.1:3306 连接的数据库所在的主机IP以及Port
        // test 连接的数据库的名字
        String url = "jdbc:mysql://127.0.0.1:3306/test";

        //配置用户名和密码
        Properties properties = new Properties();
        // 用户名的键值必须为 user
        properties.setProperty("user","root");
        // 密码的键值为 password
        properties.setProperty("password","123456");

        //拿到连接
        Connection connect = driver.connect(url, properties);

        //3 执行sql语句
        String sql = "select * from websites";
        //创建sql语句发送以及拿到返回结果的类
        Statement statement = connect.createStatement();
        //拿到返回结果
        ResultSet resultSet = statement.executeQuery(sql);
        System.out.println("id\tname\turl\tage\tcountry");
        while (resultSet.next()){
            System.out.println(resultSet.getString("id")+"\t" +
                    resultSet.getString("name")+"\t" +
                    resultSet.getString("url")+"\t" +
                    resultSet.getString("age")+"\t"+resultSet.getString("country"));
        }

        //4释放资源
        resultSet.close();
        statement.close();
        connect.close();
    }
}

/*
结果展示:
id	name	    url	                        age	    country
1	Google	    https://www.google.cm/	    1	    USA
2	淘宝	    https://www.taobao.com/	    13	    CN
3	菜鸟教程	    http://www.runoob.com	    5892	CN
4	微博	    http://weibo.com/	        20	    CN
5	Facebook	https://www.facebook.com/	3	    USA
*/

连接数据库的5种方式

前言

mysql 驱动5.1.6可以无需 Class.forName(“com.mysql.cj.jdbc.Driver”);
从jdk1.5以后使用jdbc4,不需要再显式的调用Class.forName()注册驱动而是自动调用驱动jar包下
META-INF\services\java.sql.Driver 文本中的类名去注册。

方式1

     //就是上述使用的方式:
     //1 初始化驱动
        Driver driver = new Driver();
        //2 建立连接
        // jdbc:mysql:// 表示使用的是什么数据库
        // 127.0.0.1:3306 连接的数据库所在的主机IP以及Port
        // test 连接的数据库的名字
        String url = "jdbc:mysql://127.0.0.1:3306/test";

        //配置用户名和密码
        Properties properties = new Properties();
        // 用户名的键值必须为 user
        properties.setProperty("user","root");
        // 密码的键值为 password
        properties.setProperty("password","123456");

        //拿到连接
        Connection connect = driver.connect(url, properties);
        

        connect.close();
    

方式2

     //方式2连接数据库
    public void connect2() throws Exception
    {
            //通过反射动态加载Driver 类
            Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
            Driver dirver = (Driver) cls.newInstance();
            // 余下步骤与方式1一样
    }

方式3

//方式3连接数据库
    public void connect3() throws Exception {
        //通过DriverManager来统一管理连接
        //通过反射动态加载Driver 类
        Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver dirver = (Driver) cls.newInstance();
        String url = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String password = "123456";
        //注册驱动
        DriverManager.registerDriver(dirver);
        //通过DriverManager来获取连接
        Connection connection = DriverManager.getConnection(url, user, password);

        /*
        与方式一一样
         */

        //注意断开连接释放资源
        connection.close();
    }
    

方式4

     //方式4
    public void connect4() throws Exception{
        //在源码里面,存在静态方法自动注册一个Driver
        /*
        源码:
            static {
        try {
            //自动注册一个Driver(默认注册)
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
        }
         */
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String password = "123456";
        //通过DriverManager来获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        /*
        与方式一一样
         */
        //注意断开连接释放资源
        connection.close();
    }

方式5

    //方式5
    //通过配置文件来管理
    
    
    //配置文件详情如下:
    url=jdbc:mysql://127.0.0.1:3306/test
    user=root
    password=123456
    driver=com.mysql.cj.jdbc.Driver
    
    
    public void connect5() throws Exception{
       Properties properties = new Properties();
       properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
       String url = properties.get("url").toString();
       String user = properties.get("user").toString();
       String password = properties.get("password").toString();
       String driver = properties.get("driver").toString();
       Class.forName(driver);
       Connection connection = DriverManager.getConnection(url, user, password);
       connection.close();
    }

ResultSet

基本介绍

  1. 表示数据库结果集的数据表,通常由数据库查询语句生成
  2. ResultSet对象保持一个光标指向当前的数据行,最初光标指向第一行的前一行。(类似于迭代器)
  3. next 方法将光标移动到下一行,如果不存在下一行,则返回false。因此可以使用此方法配合while遍历结果集。

底层介绍

ResultSet 类里面有一个 rowData 对象,rowdata里面的rows对象就存放着结果。
在这里插入图片描述

PreparedStatament(预处理)

预处理好处分析

  • 不再使用 + 拼接sql语句,减少语法错误。
  • 有效的解决了Sql注入问题。
  • 大大减少了编译次数,提高效率。

代码演示

 public void preparedstatement() throws Exception{
 
    Properties properties = new Properties();
    properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
    String url = properties.get("url").toString();
    String user = properties.get("user").toString();
    String password = properties.get("password").toString();
    String driver = properties.get("driver").toString();
    Class.forName(driver);
    Connection connection = DriverManager.getConnection(url, user, password);

    String sql = "select * from person where id = ? && password=?";
    //拿到预处理接口
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    //为 ? 赋值
    // 第一个参数为?的位置,1:代表第一个?,以此类推.
    //执行此方法时会解决Sql注入问题
    preparedStatement.setString(1,"1");
    preparedStatement.setString(2,"1");

    ResultSet resultSet = preparedStatement.executeQuery();
    
    if(resultSet.next()){
        System.out.println("登录成功");
    }else{
        System.out.println("登陆失败");
    }

    resultSet.close();
    preparedStatement.close();
    connection.close();
}

批处理

引言

批处理就是一批一批的处理,而不是一个一个的处理。
当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这么做效率上很差!处理的方案是使用批处理,即一次向服务器发送多条SQL语句,然后由服务器一次性处理。
批处理只针对更新(增、删、改)语句,不可以用于查询。
我们首先要打开Mysql的批处理,添加参数:rewriteBatchedStatements=true,也就是把我们的URL变成以下这种形式:jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true

statement批处理

可以多次调用Statement类的addBatch(String sql)方法,把需要执行的所有SQL语句添加到一个“批”中,然后调用Statement类的executeBatch()方法来执行当前“批”中的语句。

  • void addBatch(String sql):添加一条语句到“批”中;
  • int[] executeBatch():执行“批”中所有语句。返回值表示每条语句所影响的行数据;
  • void clearBatch():清空“批”中的所有语句;
    for(int i = 0; i < 10; i++) {
	String number = "S_10" + i;
	String name = "stu" + i;
	int age = 20 + i;
	String gender = i % 2 == 0 ? "male" : "female";
	String sql = "insert into stu values('" + number + "', '" + name + "', " + age + ", '" +         
	gender + "')";
	stmt.addBatch(sql);
}
    stmt.executeBatch();

preparedstatement批处理

PreparedStatement的批处理有所不同,因为每个PreparedStatement对象都绑定一条SQL模板。所以向PreparedStatement中添加的不是SQL语句,而是给“?”赋值。

    con = JdbcUtils.getConnection();
    String sql = "insert into stu values(?,?,?,?)";
    pstmt = con.prepareStatement(sql);
    for(int i = 0; i < 10; i++) {
	    pstmt.setString(1, "S_10" + i);
	    pstmt.setString(2, "stu" + i);
	    pstmt.setInt(3, 20 + i);
	    pstmt.setString(4, i % 2 == 0 ? "male" : "female");
	    pstmt.addBatch();
    }
    pstmt.executeBatch();

批处理源码分析

引言

  • 在调用addBatch();方法后,Statement或PreparedStatement对象,会调用自身的addBatch()方法,
    该方法会检查sql语句的语法。
  • 然后调用(ClintPreapredQuery)query对象的addBatch()方法,将sql语句放入query的batchedArgs
    (ArrayList)数组。
  • 调用executeBatch();会统一处理batchedArgs里面的sql语句。
  • 所以调用后需调用clearBatch(),清空query.batchedArgs数组。

源码


PrepareedStatement.addBatch:

public void addBatch() throws SQLException {
    try {
        synchronized(this.checkClosed().getConnectionMutex()) {
        
        QueryBindings queryBindings = ((PreparedQuery)this.query).getQueryBindings();
        queryBindings.checkAllParametersSet();
        this.query.addBatch(queryBindings.clone());
        
        }
    } catch (CJException var6) {
        throw SQLExceptionsMapping.translateException(var6, this.getExceptionInterceptor());
    }
}

this.query.addBatch:

public void addBatch(Object batch) {
        if (this.batchedArgs == null) {
            this.batchedArgs = new ArrayList();
        }

        this.batchedArgs.add(batch);
    }

Jdbc Utils(工具类,传统连接方法)开发

引言

问题引出:对于这样的三个类A,B,C。他们都需要获取mysql服务,但是对于JDBC而言,获取mysql服务
都有同样的三个步骤:

  1. 注册驱动(由于mysql 5.1.6版本后会自动注册,所以此步骤可去除,即严格意义上说只有两个步骤)
  2. 创建连接
  3. 释放资源

显而易见的是这会导致代码的冗余,增大了后期维护的成本。此种情况下,就使得Jdbc工具类的开发显得尤为重要。

实例代码

package Jdbc;

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

/**
  * @author WJ
  * @date 2022/7/27 14:56
  * @version 1.0
  * 用于简化Jdbc操作的工具类
  */
public class JDBCUtils {
    /*
    定义四个相关变量,由于是工具类,所以相关结构设置成静态
        */
    private static String user;
    private static String password;
    private static String url;
    private static String driver;

    //由静态代码块进行初始化操作
    static {
        try {
            Properties properties = new Properties();
            //加载配置文件
            properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
            //读取相关属性
            url = properties.get("url").toString();
            user = properties.get("user").toString();
            password = properties.get("password").toString();
            driver = properties.get("driver").toString();
        } catch (FileNotFoundException e) {
            //实际开发处理时
            //选择将编译异常编程运行异常
            //这时调用者可以捕获该异常,也可以选择默认处理,比较方便
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //获取连接
    public static Connection getConnection(){
        try {
            return DriverManager.getConnection(url,user,password);
        } catch (SQLException e) {
            //选择将编译异常编程运行异常
            throw new RuntimeException(e);
        }
    }

    //资源关闭
    public static void close(ResultSet set, Statement statement,Connection connection){
        try {
            if(set != null){
                set.close();
            }
            if(statement != null){
                statement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            //选择将编译异常编程运行异常
            throw new RuntimeException(e);
        }
    }
}


简单应用


    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;

    try {
        connection = JDBCUtils.getConnection();
        // 执行sql语句
        String sql = "select * from websites";
        //创建sql语句发送以及拿到返回结果的类
        statement = connection.createStatement();
        //拿到返回结果
        resultSet = statement.executeQuery(sql);
        System.out.println("id\tname\turl\tage\tcountry");
        while (resultSet.next()) {
            System.out.println(resultSet.getString("id") + "\t" +
                    resultSet.getString("name") + "\t" +
                    resultSet.getString("url") + "\t" +
                    resultSet.getString("age") + "\t" + resultSet.getString("country"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally{
        //释放资源
        JDBCUtils.close(resultSet,statement,connection);
    }
    

Jdbc事务

基本介绍

  • JDBC程序中当一个Connection对象创建时,默认是自动提交事务:每执行一个SQL语句,
    如果执行成功,就会向数据库自动提交,而不能回滚。
  • JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务
  • 调用Connection的setAutoCommit(flse);可以取消自动提交事务。
  • 在所有的SQL语句执行成功后调用Commit();方法提交事务。
  • 在某个操作失败或异常时调用rollback();方法可以回滚事务。

应用实例

问题引出:即存在两个人A,B。如果A要给B转账100块,那么在数据库里面的操作就是:
将A的钱减少100,B的钱增加100。显然这是两个操作,而且是两个必须都成功或都失败的操作,
如果两个操作只成功一个就会出现错误:即凭空多出或少了100块。显然这是不被允许的。

 //事务演示
public void affairs(){
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;

    try {
        connection = JDBCUtils.getConnection();
        connection.setAutoCommit(false);//开启事务
        // 执行sql语句
        String sql1 = "update affairs set count = count - ? where id = ?";
        //创建sql语句发送以及拿到返回结果的类
        statement = connection.prepareStatement(sql1);
        statement.setString(1,"100");
        statement.setInt(2,1);
        statement.executeUpdate();

        int i = 1/0;//产生异常

        String sql2 = "update affairs set count = count + ? where id = ?";
        statement.setString(1,"100");
        statement.setInt(2,2);
        statement.executeUpdate();

        connection.commit();//提交事务
    } catch (Exception e) {

        System.out.println("发生了异常,事务回滚!!!");

        //发生了异常,撤销执行的Sql语句
        try {
            //无参数则默认回滚到事务开启的地方
            connection.rollback();
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }

        e.printStackTrace();
    } finally{
        //释放资源
        JDBCUtils.close(resultSet,statement,connection);
    }
}

Jdbc传统连接弊端分析

  1. 传统的Jdbc连接使用DriverManager来获取,每次向数据库建立连接的时候,
    都要将Connection加载到内存中,再验证ip地址,用户名和密码(0.05s ~ 1s)。
    需要数据库连接的时候,就像数据库请求一个连接,频繁的进行数据库连接操作会
    占用大量的系统资源,容易造成服务器崩溃。
  2. 每一次使用完数据库都得断开,如果程序出现异常未断开连接,将导致数据库内存
    泄露,最终导致重启数据库。
  3. 传统连接的方式,不能控制连接的数量,如果连接过多,也可能造成数据库内存泄露
    并崩溃。

由此便引出了连接池技术

Jdbc连接池

基本介绍

  • 预先在缓冲池里面放入一定数量的连接,当需要进行数据库连接时,"取"出一个连接,
    使用完后"放"回去即可。
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的
    连接,而不是重新创建一个。
  • 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被放入等待队列。

数据库连接池种类

  1. JDBC连接池使用javax.sql.DataSource来表示,DataSource知识一个接口,该接口通常
    由第三方提供实现。
  2. C3P0 数据库连接池。速度相对较慢,稳定性不错(hibernate,spring)。
  3. DBCP 数据库连接池,速度相对C3P0较快,但不稳定。
  4. Proxool 数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点。
  5. BoneCP 数据库连接池,速度快。
  6. Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池。

C3P0连接池技术

经典,需掌握

  • 毫无疑问,首先需要拿到对应的jar包(到官网下载),配置到项目依赖。
  • 代码演示
使用方式1

*方式1(手动设置相关属性)

    
    package Jdbc;

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

/**
  * @author WJ
  * @date 2022/7/28 12:04
  * @version 1.0
  * 演示数据库连接池技术的使用
  */
@SuppressWarnings("all")
public class ConnectPool_c3p0 {

    //c3p0连接池技术演示
    public static void main(String[] args) throws Exception{
        //1 初始化相关信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
        String url = properties.get("url").toString();
        String user = properties.get("user").toString();
        String password = properties.get("password").toString();
        String driver = properties.get("driver").toString();

        //2 生成连接池对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //设置相关属性
        comboPooledDataSource.setDriverClass(driver);
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);

        //设置初始化连接池里连接个数
        comboPooledDataSource.setInitialPoolSize(10);
        //设置最大连接个数
        comboPooledDataSource.setMaxPoolSize(20);

        //测试连接五十万次的时间
        long start = System.currentTimeMillis();
        for(int i=0;i<500000;i++) {
            //从连接池里拿到一个连接
            Connection connection = comboPooledDataSource.getConnection();
            //释放资源
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("c3p0连接池耗时: "+(end-start));//c3p0连接池耗时: 3384
    }
}


使用方式2
  • 方式2(配置文件方式)
    • 首先要创建一个xml(在src目录下),其名字一定是c3p0-config.xml(代码中自动寻找)
    • 格式说明
        <c3p0-config>
    <!-- 配置名称 下面的代码会根据给定的名称寻找相关配置 -->
    <named-config name="wj">
        <!--  连接参数 -->
        <!--  驱动类-->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!-- 连接url -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <!-- 用户名 -->
        <property name="user">root</property>
        <!-- 密码 -->
        <property name="password">123456</property>
 
        <!-- 连接池参数 -->
        <!--初始化申请的连接数量-->
        <property name="initialPoolSize">5</property>
        <!-- 每次增长的连接数量 -->
        <property name="acquireIncrement">5</property>
        <!--最大的连接数量-->
        <property name="maxPoolSize">10</property>
        <!--超时时间(毫秒)-->
        <property name="checkoutTimeout">3000</property>
            
        <!-- 可连接的最多的命令对象数 -->
        <property name="maxStatements">5</property>        

        <!-- 每个连接对象可连接的最多的命令对象数 -->
        <property name="maxStatementsPerConnection">2</property>
        
    </named-config>
</c3p0-config>
  • 实例代码
        //即配置文件里的<named-config>标签
        ComboPooledDataSource wj = new ComboPooledDataSource("wj");

        Connection connection = wj.getConnection();

        System.out.println("ok");

        connection.close();

Druid(德鲁伊连接池使用)

使用最广,性能最优,需掌握

  • 导入jar包
  • 创建配置文件.properties后缀,内容(格式)如下:
driverClassName=com.mysql.cj.jdbc.Driver
#URL连接数据库的URL,其中travel(以下面例子来说)为连接的数据库,后面的参数可不改但不删
url=jdbc:mysql://localhost:3306/test
characterEncoding=utf-8
#安装mysql时候设置的用户与密码
username=root
password=123456
#初始化物理连接的个数
initialSize=10
#最小连接数
minIdle=5
#最大连接池数量
maxActive=30
#获取连接时最大等待时间(毫秒)
maxWait=3000
#用来检测连接是否有效的sql
validationQuery=SELECT 1
#保证安全性!
testWhileIdle=true

  • 示例代码
package Jdbc;

import com.alibaba.druid.pool.DruidDataSourceFactory;

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

public class ConnectPool_Druid{

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

        Properties properties = new Properties();
        //读取配置文件
        properties.load(new FileInputStream("src\\druid.properties"));
        //通过配置文件创造一个指定的连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //效率测试
        long start = System.currentTimeMillis();
        for(int i=0;i<500000;i++){
            Connection connection = dataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("德鲁伊连接池耗时: "+(end-start));//德鲁伊连接池耗时: 517
    }
}

Jdbc Utils(德鲁伊连接池方式实现)

示例代码

package Jdbc;

import com.alibaba.druid.pool.DruidDataSourceFactory;

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

@SuppressWarnings("all")
public class JDBCUtilsByDruid {
    private static DataSource dataSource;
    
    static {
        try {
            Properties properties = new Properties();
            //读取配置文件
            properties.load(new FileInputStream("src\\druid.properties"));
            //通过配置文件创造一个指定的连接池
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    //获取连接
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    
    //释放资源
    public static void close(ResultSet set, Statement statement, Connection connection){
        try {
            if(set != null){
                set.close();
            }
            if(statement != null){
                statement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            //选择将编译异常编程运行异常
            throw new RuntimeException(e);
        }
    }
}

Apache-DBUtils

问题引出

  1. ResultSet结果集与Connetion绑定了,即连接断开以后,结果集就不能用了;
  2. ResultSet不利于数据管理,体现在结果集只能遍历一次;
  3. ResultSet获取结果也不方便,体现在通过getString(“字段名”)来获取,不利于阅读和使用。

基本介绍

在这里插入图片描述

实例代码

数据库字段

  • id
  • name
  • url
  • age
  • country

javaBean类

package Jdbc;

public class website {

    private String id;
    private String name;
    private String url;
    private String age;
    private String country;

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(id);
        builder.append("\t");
        builder.append(name);
        builder.append("\t");
        builder.append(url);
        builder.append("\t");
        builder.append(age);
        builder.append("\t");
        builder.append(country);
        return builder.toString();
    }

    public website() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

演示查询代码

返回多行多列
package Jdbc;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

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

public class DBUtils {
    public static void main(String[] args) throws SQLException {
        Connection connection = JDBCUtilsByDruid.getConnection();

        String sql = "select * from websites where id > ?";

        //生成一个执行增删改查的对象
        /**
         * @return ArrayList 集合
         * connection: 数据库连接
         * sql: 执行的sql语句
         * BeanListHandler :泛型类:用于创建保存数据的集合(反射机制)
         * 后面是可变参数,用于给sql语句里面的 ? 赋值
         */
        QueryRunner queryRunner = new QueryRunner();
        //(1) query 方法就是执行sql语句,得到的resultset -> website 对象 -> ArrayList 对象

        List<website> websites =
                queryRunner.query(connection, sql, new BeanListHandler<>(website.class), 1);
        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);
        for(website w:websites){
            System.out.println(w);
        }

    }
}

返回一行多列
 // 演示返回一行结果
    @Test
    public void onerow() throws Exception{
        Connection connection = JDBCUtilsByDruid.getConnection();
        String sql = "select * from websites where id = ?";
        QueryRunner queryRunner = new QueryRunner();
        website w = queryRunner.query(connection, sql, new BeanHandler<>(website.class), 1);
        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);
        System.out.println(w);
    }
返回一行一列
// 演示返回一行一列结果
    @Test
    public void onerowandcol() throws Exception{
        Connection connection = JDBCUtilsByDruid.getConnection();
        String sql = "select name from websites where id = ?";
        QueryRunner queryRunner = new QueryRunner();
        Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 1);
        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);
        System.out.println(obj);
    }

演示DML(增删改查)代码

//测试增删改查
    @Test
    public void testDML() throws SQLException {
        Connection connection = JDBCUtilsByDruid.getConnection();
        String sql = "update websites set url = ? where name = ?";
        QueryRunner queryRunner = new QueryRunner();

        //返回受影响的行数,update函数执行增删改操作
        int update = queryRunner.update(connection, sql,"无法访问","Google");

        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);

        if(update > 0){
            System.out.println("ok");
        }else{
            System.out.println("error");
        }
    }

源码解析

  • wrap()
    protected ResultSet wrap(ResultSet rs) {
        return rs;
    }
  • query()


public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
        return this.query(conn, false, sql, rsh, (Object[])null);
    }

private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        if (conn == null) {
            throw new SQLException("Null connection");
        } else if (sql == null) {
            if (closeConn) {
                this.close(conn);
            }

            throw new SQLException("Null SQL statement");
        } else if (rsh == null) {
            if (closeConn) {
                this.close(conn);
            }

            throw new SQLException("Null ResultSetHandler");
        } else {
        
            PreparedStatement stmt = null;
            ResultSet rs = null;
            T result = null;

            try {
                stmt = this.prepareStatement(conn, sql);//获取指令发送类
                this.fillStatement(stmt, params);//填充参数
                rs = this.wrap(stmt.executeQuery());//如上实例,没有任何处理(待完善)
                result = rsh.handle(rs);//通过反射机制,将每一行的值赋给一个实例的website对象,最后拼接成数组返回
                
            } catch (SQLException var33) {
                this.rethrow(var33, sql, params);
            } finally {
                try {
                    this.close(rs);//这里可以看到,底层已经关闭了结果集
                } finally {
                    this.close(stmt);//也关闭了指令发送器
                    if (closeConn) {
                        this.close(conn);//在debug里面可以看到 closeConn默认是 false,即不关闭连接。
                                        //且这是私有方法,即我们无法干涉,所以仍需手动关闭。
                    }

                }
            }

            return result;
        }
    }

反射机制简单说明

下面的代码就是通过反射机制给一个实例化的对象赋值


package Jdbc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Refelect {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        user u = new user();
        Class c = user.class;
        Method[] declaredMethods = c.getDeclaredMethods();//获取方法数组
        
        for (Method m :declaredMethods){
            //根据方法名做出相应动作:赋值
            if(m.getName().equals("setId")){
                m.invoke(u,"root");
            }
            if(m.getName().equals("setPassword")){
                m.invoke(u,"123456");
            }
            
        }
        System.out.println("id:"+u.id);
        System.out.println("password:"+u.password);
    }
}

class user{
    String id;
    String password;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

BasicDAO

引言

类似前面的Jdbc-Utils,对于数据库操作,存在大量的重复语句,除了获取连接和断开
连接外,基本的增删改查在函数里的不同只是sql语句的不同,以及返回类型的不同。
由此便引发的BasicDAO的开发,即将基本的操作封装在这个类里面,而对于不同的表则在
这个类的基础上开发其特有的子类,这样:一个子类就对应一张表的查询。

实例代码

BasicDAO

package dao_;

import Jdbc.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

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

public class BasicDAO <T>{
    private QueryRunner queryRunner = new QueryRunner();

    /**
     *
     * @param sql 执行的sql语句
     * @param params 填充的参数
     * @return 受影响的行数
     */
    public int updateData(String sql,Object... params){
        Connection connection = null;

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

    /**
     *
     * @param sql 执行的sql语句
     * @param cls 返回的表对应的JavaBean 类
     * @param params 填充的参数
     * @return 查询到的数据(多行)
     */
    public List<T> querryRows(String sql,Class<T>cls,Object... params){
        Connection connection = null;

        try {
            connection = JDBCUtilsByDruid.getConnection();
            return queryRunner.query(connection,sql,new BeanListHandler<T>(cls),params);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }

    /**
     * @param sql 执行的sql语句
     * @param params 填充的参数
     * @return 返回查询到的数据(单行单列)
     */
    public Object querryScalar(String sql,Object... params){
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            return queryRunner.query(connection,sql,new ScalarHandler(),params);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }

    /**
     *
     * @param sql 执行的sql语句
     * @param cls 返回的表对应的JavaBean 类
     * @param params 填充的参数
     * @return 查询到的数据(单行)
     */
    public T querrySingle(String sql,Class<T>cls,Object... params){
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            return queryRunner.query(connection,sql,new BeanHandler<T>(cls),params);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }
}

websiteDao

package dao_;

import Jdbc.website;

public class websiteDao extends BasicDAO<website>{
    //根据业务,编写特有的方法
}

website(JavaBean对象(Domain))

package Jdbc;

public class website {
    private String id;
    private String name;
    private String url;
    private String age;
    private String country;

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(id);
        builder.append("\t");
        builder.append(name);
        builder.append("\t");
        builder.append(url);
        builder.append("\t");
        builder.append(age);
        builder.append("\t");
        builder.append(country);
        return builder.toString();
    }

    public website() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

测试代码

package dao_;

import Jdbc.website;

import java.util.List;

public class TestDao {
    public static void main(String[] args) {
        websiteDao websiteDao = new websiteDao();
        String sql;
        //多行查询
        sql = "select * from websites";
        List<website> websites = websiteDao.querryRows(sql, website.class);
        System.out.println("=====多行查询=====");
        for (website w:websites){
            System.out.println(w);
        }
        System.out.println("=====单行单列查询=====");
        //单行单列查询
        sql = "select name from websites where id = ?";
        Object o = websiteDao.querryScalar(sql, 3);
        System.out.println(o);
        System.out.println("=====单行查询=====");
        //单行查询
        sql = "select * from websites where id = ?";
        website w = websiteDao.querrySingle(sql, website.class, 2);
        System.out.println(w);


    }
}

测试结果

=====多行查询=====
1 Google 无法访问 1 USA
2 淘宝 https://www.taobao.com/ 13 CN
3 菜鸟教程 http://www.runoob.com 5892 CN
4 微博 http://weibo.com/ 20 CN
5 Facebook https://www.facebook.com/ 3 USA
=====单行单列查询=====
菜鸟教程
=====单行查询=====
2 淘宝 https://www.taobao.com/ 13 CN
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值