JDBC_SE

JDBC

JDBC概述

JDBC:Java Database Connectivity,它是代表一组独立于任何数据库管理系统(DBMS)的API,声明在java.sql与javax.sql包中,是SUN(现在Oracle)提供的一组接口规范。由各个数据库厂商来提供实现类,这些实现类的集合构成了数据库驱动jar。

1561212287381

即JDBC技术包含两个部分:

(1)java.sql包和javax.sql包中的API

因为为了项目代码的可移植性,可维护性,SUN公司从最初就制定了Java程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种DBMS软件,Java代码可以保持一致性。

(2)各个数据库厂商提供的jar

因为各个数据库厂商的DBMS软件各有不同,那么内部如何通过sql实现增、删、改、查等管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。

Java程序连接MySQL数据库

/*
1、模块添加了依赖的mysql驱动相关库

2、在内存中加载驱动类(可选)
        更早版本mysql驱动类:org.gjt.mm.mysql.Driver
        最近版本:com.mysql.jdbc.Driver
        MySQL8.0版本:com.mysql.cj.jdbc.Driver
        
  Class.forName("com.mysql.cj.jdbc.Driver"); 

3、连接数据库:通过DriverManager工具类获取数据库连接Connection的对象。
    String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
    Connection conn = DriverManager.getConnection(url, "root", "123456");
    
    MySQL8使用时,url需要加参数:serverTimezone=UTC,否则会报错:
    Exception in thread "main" java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.


4、断开连接:使用close方法。
 */
import java.sql.Connection;
import java.sql.DriverManager;

public class TestJDBC {
    public static void main(String[] args)throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn =  DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC","root","123456");
        System.out.println("conn = " + conn);
        conn.close();
    }
}

java实现增删改查

/*步骤:
1、模块添加了依赖的mysql驱动相关库

2、在内存中加载驱动类(可选)
 Class.forName("com.mysql.cj.jdbc.Driver"); 

3、连接数据库
通过DriverManager工具类获取数据库连接Connection的对象。
    String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
    Connection conn = DriverManager.getConnection(url, "root", "123456");

 4、操作数据库
(1)通过Connection对象获取Statement或PreparedStatement对象
(2)通过Statement或PreparedStatement对象执行sql
执行增、删、改:int executeUpate()
执行查询:ResultSet executeQuery()
(3)如果服务器有查询结果返回,需要用ResultSet接收
遍历结果集的方法:
boolean next():判断是否还有下一行
getString(字段名或序号),getInt(字段名或序号),getObject(字段名或序号)

5、释放资源(close)
 */

添加数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class TestInsert {
    public static void main(String[] args)throws Exception {
        //把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //B:获取数据库连接对象
        String url = "jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC";
        Connection connection = DriverManager.getConnection(url,"root","123456");
        //Connection   ==> 网络编程的Socket

        String sql = "insert into t_department values(null,'测试数据部门','测试数据部门简介')";//发给服务器的sql
        PreparedStatement pst = connection.prepareStatement(sql);
        //PreparedStatement ==> IO流  网络编程的socket.getOutputStream()发生数据用的
        int len = pst.executeUpdate();
        //返回sql影响的记录数
        System.out.println(len>0 ? "添加成功" : "添加失败");

        pst.close();
        connection.close();
  }
}

/*
mysql> select * from t_department;
+-----+--------------+------------------+
| did | dname        | description      |
+-----+--------------+------------------+
|   1 | 研发部       | 负责研发工作     |
|   2 | 人事部       | 负责人事管理工作 |
|   3 | 市场部       | 负责市场推广工作 |
|   4 | 财务部       | 负责财务管理工作 |
|   5 | 后勤部       | 负责后勤保障工作 |
|   6 | 测试部       | 负责测试工作     |
|   7 | 测试数据部门 | 测试数据部门简介 |
+-----+--------------+------------------+
7 rows in set (0.00 sec)
*/

修改数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class TestUpdate {
    public static void main(String[] args)throws Exception {
        //把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //B:获取数据库连接对象
        String url = "jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC";
        Connection connection = DriverManager.getConnection(url, "root", "123456");
        //Connection   ==> 网络编程的Socket

        String sql = "update t_department set description = 'xx' where did = 7";//发给服务器的sql
        PreparedStatement pst = connection.prepareStatement(sql);
        //PreparedStatement ==> IO流  网络编程的socket.getOutputStream()发生数据用的
        int len = pst.executeUpdate();
        //返回sql影响的记录数
        System.out.println(len > 0 ? "修改成功" : "修改失败");

        pst.close();
        connection.close();
    }
}
/*
mysql> select * from t_department;
+-----+--------------+------------------+
| did | dname        | description      |
+-----+--------------+------------------+
|   1 | 研发部       | 负责研发工作     |
|   2 | 人事部       | 负责人事管理工作 |
|   3 | 市场部       | 负责市场推广工作 |
|   4 | 财务部       | 负责财务管理工作 |
|   5 | 后勤部       | 负责后勤保障工作 |
|   6 | 测试部       | 负责测试工作     |
|   7 | 测试数据部门 | xx               |
+-----+--------------+------------------+
7 rows in set (0.00 sec)
 */

删除数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class TestDelete {
    public static void main(String[] args)throws Exception {
        //把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //B:获取数据库连接对象
        String url = "jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC";
        Connection connection = DriverManager.getConnection(url, "root", "123456");
        //Connection   ==> 网络编程的Socket

        String sql = "delete from t_department where did = 7";//发给服务器的sql
        PreparedStatement pst = connection.prepareStatement(sql);
        //PreparedStatement ==> IO流  网络编程的socket.getOutputStream()发生数据用的
        int len = pst.executeUpdate();
        //返回sql影响的记录数
        System.out.println(len > 0 ? "删除成功" : "删除失败");

        pst.close();
        connection.close();
    }
}
/*
mysql> select * from t_department;
+-----+--------+------------------+
| did | dname  | description      |
+-----+--------+------------------+
|   1 | 研发部 | 负责研发工作     |
|   2 | 人事部 | 负责人事管理工作 |
|   3 | 市场部 | 负责市场推广工作 |
|   4 | 财务部 | 负责财务管理工作 |
|   5 | 后勤部 | 负责后勤保障工作 |
|   6 | 测试部 | 负责测试工作     |
+-----+--------+------------------+
6 rows in set (0.00 sec)
 */

查询数据

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/*
步骤:
1、一个项目引入一次数据库驱动jar就可以
2、建立数据库连接
(1)加载驱动类:通过Class类的forName方法注册驱动
(2)获取数据库连接
通过DriverManager类的静态方法获取数据库连接对象
3、通过数据库连接对象获取Statement或PreparedStatement对象,用来执行sql
4、通过Statement或PreparedStatement对象调用
(1)int executeUpdate():执行insert,update,delete等更新数据库数据的sql
(2)ResultSet executeQuery():执行select查询的sql,返回一个结果集
(3)boolean execute():可以用来执行DDL语句

遍历结果集ResultSet的方法:
boolean next():判断是否还有下一行
getString(字段名或序号),getInt(字段名或序号),getObject(字段名或序号)
*/
public class TestSelect {
    public static void main(String[] args)throws Exception {
        //把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //B:获取数据库连接对象
        String url = "jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC";
        Connection connection = DriverManager.getConnection(url, "root", "123456");
        //Connection   ==> 网络编程的Socket

        String sql = "select * from t_department";//发给服务器的sql
        PreparedStatement pst = connection.prepareStatement(sql);
        //PreparedStatement ==> IO流  网络编程的socket.getOutputStream()发生数据用的

        ResultSet resultSet = pst.executeQuery();//==>IO流  输入流,又像是集合和迭代器的集成
        while(resultSet.next()){ //while循环一次,迭代一行,遍历一行
            int did = resultSet.getInt("did");//get一次得到一个单元格的数据
            String dname = resultSet.getString("dname");
            String decription = resultSet.getString("description");
            System.out.println(did +"\t" + dname +"\t" + decription);
        }

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

轻松处理各种问题

避免sql拼接和注入问题可以用 “?”

/*
 String sql = "select * from t_employee where eid = ? ";//不用拼接了
 PreparedStatement pst = connection.prepareStatement(sql);
//给?指定值   ? 从1开始
 pst.setObject(1, id);
*/
 @Test
    public void test02()throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.print("请输入你要查询的员工的编号:");
        String id = input.nextLine();
        // 第一种正常输入:1
        // 第二种恶意输入:1 or 1=1  第一个1表示员工编号, 后面 or 1= 1表示条件,而1=1是永远成立,其他条件全部失效

        //把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //B:获取数据库连接对象
        String url = "jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC";
        Connection connection = DriverManager.getConnection(url, "root", "123456");
        //Connection   ==> 网络编程的Socket

        String sql = "select * from t_employee where eid = ? ";//不用拼接了
        PreparedStatement pst = connection.prepareStatement(sql);

        //给?指定值
        pst.setObject(1, id);
        //因为这里把 "2 or 1=1"当成一个整体赋值给eid
        //select * from t_employee where eid = '2 or 1=1';
        /*
        eid字段是int类型,mysql在解析时,发现给eid赋值了字符串,会把字符串尽量转为int类型的值。
        SELECT '2 or 1=1'+0;  得到结果是2。
        select * from t_employee where eid = '2 or 1=1';
        等价于
        select * from t_employee where eid = 2;
         */

        //执行查询
        ResultSet rs = pst.executeQuery();
        /*
        ResultSet接口提供了
        (1)boolean next():判断是否有下一条记录
        (2)获取某个单元格的数据
        String getString(字段名)
        int getInt(字段名)
        double getDouble(字段名)
        ...

        有点麻烦,需要一一去对应字段名
        Object getObject(字段名)
        Object getObject(字段的序号)  从1开始。
         */
        while (rs.next()) {//while循环一次,代表一行
            //t_employee有14个字段
            for (int i = 1; i <= 14; i++) {//for循环一次,代表一行中的一个单元格
                System.out.print(rs.getObject(i) + "\t");
            }
            System.out.println();
        }

        rs.close();
        pst.close();
        connection.close();
        input.close();
}

获取自增长键值

/*
获取自增长键值:
mysql中很多表都是有自增长字段,特别是id。
当我们添加了一个员工、部门,添加成功后,需要立刻返回该员工、部门的自增长的id值。

(1)在用Connection数据库连接对象获取PreparedStatement对象时,要加一个参数
PreparedStatement pst = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
这里Statement.RETURN_GENERATED_KEYS表示,执行sql后,返回自增长键值

(2)执行完成之后,需要从PreparedStatement对象中获取自增长键值
 ResultSet rs = pst.getGeneratedKeys(); //方法别调错
if(rs.next()){ //因为只有一条记录,所以只有一个自增长键值,用if即可
    System.out.println("新员工编号是:" + rs.getObject(1));//因为自增长键值只有一个,所以这里直接getObject(1)即可
}
 */
 
//todo 获取自增的主键
@Test
public void test01() throws Exception{

        //1.注册驱动
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "12345");
        //3.准备sql
        String sql  ="insert into t_account values(null,?,?)";
        //4.创建命令发送器
        PreparedStatement pst = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        //5.填充数据
        pst.setObject(1,"李白");
        pst.setObject(2,6666);
        //6.执行命令获取结果
        pst.executeUpdate();
        //7.获取自增的主键值
        ResultSet rs = pst.getGeneratedKeys();
        //8.展示主键值
        while (rs.next()){
            Object pid = rs.getObject(1);
            System.out.println("pid = " + pid);
        }

        //9.关闭资源
        rs.close();
        pst.close();
        connection.close();
}

批处理

/*
批处理:
    批量执行一组sql。大多数情况下都是批量执行insert语句。

在url后面再加一个参数 rewriteBatchedStatements=true

url的格式:
    jdbc:mysql://localhost:3306/atguigu
    如果要加参数,需要用一个?,表示后面是参数

    jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC

    如果有多个参数,参数之间使用&连接,
    每一个参数都是key=value的格式。

 jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC&rewriteBatchedStatements=true

 如何实现批处理?
 (1)url中加rewriteBatchedStatements=true
jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC&rewriteBatchedStatements=true

(2)PreparedStatement对象调用
A:addBatch()   //添加到批量处理
B:executeBatch() //执行批量处理

(3)不要把values写错value(学生问题)
*/
 @Test
public void test02() throws Exception{
       //todo 批处理
        long start = System.currentTimeMillis();
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu?rewriteBatchedStatements=true", "root", "12345");
        //3.准备sql
        String sql = "insert into t_account values(null,?,?)";
        //4.创建命令发送器
        PreparedStatement pst = connection.prepareStatement(sql);
        //5.填充数据
        for (int i = 0; i < 10000; i++) {
            pst.setObject(1,"李商隐"+i);
            pst.setObject(2,i);
            //6.添加到批处理
            pst.addBatch();
        }
        //执行批处理
        pst.executeBatch();
        //7.关闭资源#
        pst.close();
        connection.close();
        long end = System.currentTimeMillis();
        System.out.println(end-start);// 776
}

事务处理

/*
如果多条sql要组成一个事务,要么一起成功,要么一起失败。
例如:订单
    (1)修改商品表的商品库存和销量
    (2)订单表新建订单数据
    (3)订单明细表新建订单明细记录(多条)
    ....
    这些sql要么一起成功,要么都还原到最初。
JDBC如何管理事务?
(1)mysql默认是自动提交事务,每执行一条语句成功后,自动提交。
需要开启手动提交模式。

Connection连接对象.setAutoCommit(false);//取消自动提交模式,开始手动提交模式

(2)sql执行成功,别忘了提交事务
Connection连接对象.commit();

(3)sql执行失败,回滚事务
Connection连接对象.rollback();
 */
 //1.注册驱动
        Connection connection=null;
        PreparedStatement pst = null;
        try {

            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接

            connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "12345");
            //todo 开启事务
            connection.setAutoCommit(false);
            //3.准备sql
            String sql = "update t_account set balance = balance + ? where pid = ?";

            //4.创建命令发送器

            pst = connection.prepareStatement(sql);
            //5.妲己-3000
            pst.setObject(1, -3000);
            pst.setObject(2, 1);
            pst.executeUpdate();

           // int n = 10/0;

            //6.吕布+3000
            pst.setObject(1, 3000);
            pst.setObject(2, 2);
            pst.executeUpdate();
            //7.关闭资源
            System.out.println("转账成功");
            //todo 提交
            connection.commit();
        } catch (Exception e) {

            System.out.println("有内鬼 终止交易");
            try {
                //todo 回滚
                connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
        }finally {
            if(pst!=null){
                try {
                    pst.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if(connection!=null){
                try {
                    connection.close();
             } catch (SQLException e) {
                    throw new RuntimeException(e);
           }
     }

}

数据库连接池

DataSource接口

JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口(通常被称为数据源),所有的Java数据库连接池都需要实现该接口。该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现

常见的数据库连接池

  • DBCP 是Apache提供的数据库连接池,速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持
  • C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以
  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  • HikariCP 俗称光连接池,是目前速度最快的连接池
  • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池

Druid连接池的使用

(1)加入jar包

例如:druid-1.1.10.jar

(2)代码步骤

第一步:创建druid连接池的配置文件druid.properties文件,放置到类路径下

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root 用户名
password=123456 密码
initialSize=5 初始的连接数量
maxActive=10 最大连接数
maxWait=1000 等待时间

第二步:使用工厂模式创建DruidDataSource对象

//1. 创建一个Properties对象,让其去读取druid.properties文件
Properties properties = new Properties();
//1.1 将druid.properties配置文件转成字节输入流
//FileInputStream is = new FileInputStream("D:\\讲课资料\\尚硅谷\\210323JavaEE(深圳)\\atguigu0323\\day04_JDBC_01\\resources\\druid.properties");

//使用相对路径来将配置文件转成字节输入流,我们可以使用类加载器来读取类路径下文件
//TestDataSource.class.getClassLoader() 表示获取ClassLoader对象
InputStream is = TestDataSource.class.getClassLoader().getResourceAsStream("druid.properties");

//1.2 使用properties对象加载流
properties.load(is);

//2. 使用DruidDataSourceFactory创建Druid连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

第三步:使用连接池对象获取连接

Connection connection = dataSource.getConnection();

Druid连接池的配置参数列表

配置 缺省 说明
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(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为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,是组合关系,并非替换关系

封装JDBCUtil

配置文件:src/jdbc.properties 或者resources/jdbc.properties

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_test
username=root
password=123456
initialSize=5
maxActive=10
maxWait=1000

JDBCUtil工具类:

import com.alibaba.druid.pool.DruidDataSourceFactory;

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


public class JDBCUtil {
    private static DataSource dataSource;
    static {
        try {
            //读取配置文件,创建连接池
            InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties properties = new Properties();
            properties.load(inputStream);

            //使用DruidDataSourceFactory创建连接池
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 获取连接
     * @return
     */
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 归还连接的方法
     * @param connection
     */
    public static void releaseConnection(Connection connection){
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
}

Apache的DBUtils

DBUtils的概述

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

其中QueryRunner类封装了SQL的执行,是线程安全的。

(1)可以实现增、删、改、查、

(2)考虑了事务处理需要共用Connection。

(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

DBUtils执行增删改的SQL语句

API介绍

  1. QueryRunner() ,创建QueryRunner对象,用于执行SQL语句
  2. QueryRunner的update(Connection conn, String sql, Object... params)方法,用于执行增删改的SQL语句
@Test
public void testAddUser() throws SQLException {
    //目标:往user表中添加一行数据
    //1. 创建QueryRunner
    QueryRunner queryRunner = new QueryRunner();
    //2. 执行SQL语句
    String sql = "insert into user values (null,?,?,?)";
    Connection conn = JDBCUtil.getConnection();

    int i = queryRunner.update(conn, sql, "aolafu", "123456", "狂战士");

    //关闭连接
    JDBCUtil.releaseConnection(conn);
}

@Test
public void testAddUserAnother() throws SQLException {
    //1. 创建QueryRunner,并且传入连接池对象
    QueryRunner queryRunner = new QueryRunner(JDBCUtil.getDataSource());

    //2. 执行SQL语句
    String sql = "insert into user values (null,?,?,?)";
    queryRunner.update(sql,"neisesi","123456","狗头");

    //这种方式的缺点是用不了事务
}

@Test
public void testDeleteUser() throws SQLException {
    //1. 创建QueryRunner,并且传入连接池对象
    QueryRunner queryRunner = new QueryRunner(JDBCUtil.getDataSource());
    //2. 执行SQL语句
    String sql = "delete from user where id=?";
    queryRunner.update(sql,1);
}

使用QueryRunner类实现查询

API介绍

  1. query(String sql, ResultSetHandler rsh, Object... params) ,执行查询 select
  2. ResultSetHandler结果集处理类
Handler类型 说明
ArrayHandler 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。
BeanHandler 将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
ColumnListHandler 将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler 将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。
MapHandler 将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值
MapListHandler 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。
ScalarHandler 它是用于单个数据。例如select count(*) from 表。
@Test
public void testFindById() throws SQLException {
    //使用DBUtils执行查询的SQL语句,将查询到的一条数据封装到User对象中
    //1. 创建QueryRunner,并且传入连接池对象
    QueryRunner queryRunner = new QueryRunner(JDBCUtil.getDataSource());
    //2. 执行SQL语句
    String sql = "select * from user where id=?";
    User user = queryRunner.query(sql, new BeanHandler<>(User.class), 2);
    System.out.println(user);
}

@Test
public void testFindAll() throws SQLException {
    //查询多条数据,封装到List<JavaBean>
    //1. 创建QueryRunner,并且传入连接池对象
    QueryRunner queryRunner = new QueryRunner(JDBCUtil.getDataSource());

    //2. 执行SQL语句
    String sql = "select * from user";
    List<User> userList = queryRunner.query(sql, new BeanListHandler<>(User.class));
    System.out.println(userList);
}

@Test
public void testFindCount() throws SQLException {
    //查询数据条数
    //1. 创建QueryRunner,并且传入连接池对象
    QueryRunner queryRunner = new QueryRunner(JDBCUtil.getDataSource());

    //2. 执行SQL语句
    String sql = "select count(id) from user";
    long count = queryRunner.query(sql, new ScalarHandler<>());

    System.out.println(count);
}

DBUtils批处理

1.创建QueryRunner对象

2.调用batch方法

3.要开启批处理

@Test
public void test01() throws Exception {
        //1.创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();
        //2.准备sql
        String sql = "insert into users values(null,?,?,?)";
        //3.数据填充
        //3.110000条数据 3个位置添加数据
        Object[][]objs = new Object[10000][3];
        for (int i = 0; i < objs.length; i++) {
            objs[i][0]="杜甫"+i;
            objs[i][1]="123"+i;
            objs[i][2]="dufu@atguigu.com";
        }
        queryRunner.batch(JDBCutils.getConnection(),sql,objs);
}

注意:类的属性名和数据库字段名相同,需要提供get/set方法

MySQL和JDBC阶段必须掌握的内容

  1. mysql服务器和sqlyog的安装和使用
  2. 会使用sqlyog对数据库、表进行创建、修改、删除
  3. 对于单表的数据的增删改查语句要非常熟练
  4. 理解多表关系,理解外键,会使用sqlyog创建和删除外键,设置外键的等级
  5. 难点:熟练使用内连接查询、外连接查询、子查询
  6. 会使用连接池+DBUtils,执行sql语句:增删改的sql语句,查询的sql语句
  7. 会使用JDBC执行添加的SQL语句,获取自增长的主键
posted @ 2024-08-18 14:10  CH_song  阅读(15)  评论(0编辑  收藏  举报