java基础之JDBC

简介

什么是JDBC?

JDBC指java连接数据库,是一种标准java应用编程接口(JAVA API),用来连接java编程语言和广泛的数据库,其实就是java语言操作数据库。

JDBC本质:

​ 是官方(sun公司)定义的一套操作所有关系型数据库的规则。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC编程),真正执行的代码是驱动jar包中的实现类。

入门

那么我们如何利用JDBC进行操作数据库呢?

一般步骤分为以下几步:

  1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
    • 复制jar包到项目的libs目录下
    • 右键–> Add As Library
  2. 注册驱动
  3. 获取数据库连接对象 Connection
  4. 定义sql语句
  5. 获取执行sql语句对象 Statament或PreparedStatement(防止sql注入问题)
  6. 执行sql语句
  7. 处理执行之后的结果
  8. 释放资源

练习

修改account表中的记录

将account表中所有人的钱数都修改为100

这是执行sql语句之前account表的状态:

在这里插入图片描述

我们执行以下代码:

/*
这个sql语句是将所有人的钱数修改为100
*/
public static void main(String[] args) throws Exception {
        // 1. 导入驱动jar包
        // 2. 注册驱动(不写也行)
        Class.forName("com.mysql.jdbc.Driver");		//这句话的意思是注册的是mysql的驱动
        // 3. 获取数据库连接对象
        //三个参数分别是位置、用户名、密码
        //在第一个参数里表示连接的是mysql数据库ip地址(locdlhost代表本机)3306端口db3数据库,因为一般我们连接的都是本机,所		  //以我们一般将本机的ip地址和端口号省略,省略后的语句在下一行
        //Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
        Connection con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");	
        // 4. 定义sql语句
        String sql = "update account set balance = 100";
        // 5. 获取执行sql的对象statemen
        Statement sta = con.createStatement();		//执行对象的类型为Statement,是用来执行sql语句的
        // 6. 执行sql
        //如果是数据库的添加,修改,删除,DDL用的都是executeUpdate方法,返回的参数代表影响的行数,我们可以用这个参数判断			//sql语句是否执行成功
        int result = sta.executeUpdate(sql);		
        // 7. 处理结果
        System.out.println(result);		
        // 8. 释放资源
        sta.close();								//先释放后开启资源
        con.close();								//再释放先开启的资源

    }
/*
其实这个代码并不健壮,有很多的问题,比如:
数据库连接对象和执行sql对象在释放资源的时候可能会出现空指针异常等等
这些问题我们会在下面进行解决
*/

这是执行sql语句之后account表的状态:

在这里插入图片描述

我们发现表的更新完全正确,这也代表着我们操作的正确性。

向account表中插入一条记录

向account表中插入一条记录

这是执行sql语句之前表的状态:

在这里插入图片描述

我们执行以下代码:

/*
下面的jdbc操作流程我们在第一次执行的流程上进行了优化:
首先,对于异常,我门并没有把它抛出去,而是抓起来进行了处理;
其次,对于Connection对象和Statement对象,我们进行了
避免空指针异常的处理
*/
public static void main(String[] args) {
        Connection con = null;
        Statement sta = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2. 定义sql语句
            String sql = "insert into account values(null, '王五', 3000)";
            //3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            //4. 获取执行sql对象
            sta = con.createStatement();
            //6. 执行sql
            int result = sta.executeUpdate(sql);
            //7. 处理结果
            System.out.println(result);
            if(result > 0){
                System.out.println("执行成功");
            }else{
                System.out.println("执行失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            if(sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这是执行sql语句之后表的状态:

在这里插入图片描述

我们发现,执行之后结果正确,说明我们的代码是正确的。

修改account表中的记录

将所有人的钱数都修改为5000

这是执行sql语句之前表的状态:

在这里插入图片描述

我们执行以下的代码:

package cn.itcast.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

//account表 修改记录
public class JDBCdemo3 {
    public static void main(String[] args) {
        Connection con = null;
        Statement sta = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 定义sql语句
            String sql = "update account set balance = 5000";
            // 3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            // 4. 获取执行sql对象
            sta = con.createStatement();
            // 5. 执行sql语句并返回结果
            int result = sta.executeUpdate(sql);
            // 6. 处理结果
            System.out.println(result);
            if(result > 0){
                System.out.println("执行成功");
            }else{
                System.out.println("执行失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 7. 释放资源
            if(sta != null){            //防止空指针异常
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
/*
这个代码其实是对第一个sql语句大的完善,避免释放资源时的空指针异常
*/

执行sql语句之后,结果如下:

在这里插入图片描述

删除account表中的一条记录

删除account表中id = 4 的记录

执行sql语句之前account表的状态:

在这里插入图片描述

我们执行以下代码:

public static void main(String[] args) {
        Connection con = null;
        Statement sta = null;
        // 1. 注册驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 定义sql语句
            String sql = "delete from account where id = 3";
            // 3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            // 4. 获取执行sql对象
            sta = con.createStatement();
            // 5. 执行sql并返回结果
            int result = sta.executeUpdate(sql);
            // 6. 处理结果
            System.out.println(result);
            if(result > 0) System.out.println("执行成功");
            else{
                System.out.println("执行失败");
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            if(sta != null){        //防止空指针异常
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){        //防止空指针异常
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

执行sql语句之后结果如下:

在这里插入图片描述

从表中记录可以看出我们执行的结果是正确的。

执行account的DDL语句

上面我们执行了数据库的添加、删除、修改,接下来我们说一下DDL(与数据库中定义相关的内容)语句。

account位于db3数据库,现在我们在db3数据库里再建一个student表

这是执行sql语句之前account表的状态,我们可以发现并没有student表

在这里插入图片描述

下面我们执行以下代码:

public static void main(String[] args) {
        Connection con = null;
        Statement sta  = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 定义sql语句
            String sql = "create table student(id int, name varchar(10))";
            // 3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            // 4. 获取执行sql对象
            sta = con.createStatement();
            // 5. 执行sql并返回结果
            int result = sta.executeUpdate(sql);            //返回的其实是影响的行数
            // 6. 处理结果
            System.out.println(result);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 7. 释放资源
            if(sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

执行sql语句之后数据库db3的状态:

在这里插入图片描述

我们可以发现,student被创建了,也说明我们的代码是正确的。

详解各个对象

DriverManager

DriverManager:驱动管理对象

功能

  1. 注册驱动

    看到这里大家可能会有疑问,会说,我们注册驱动的语句不是下面这条语句吗,和DriverManager有什么关系呢?大家请往下看

    Class.forName("com.mysql.jdbc.Driver");
    

    我们知道Class.forName()会将指定的类加载到内存中去,那我们去看看Driver的源码吧,这是Driver类的源码

    public class Driver extends NonRegisteringDriver implements java.sql.Driver {
        public Driver() throws SQLException {
        }
    
        static {
            try {
                DriverManager.registerDriver(new Driver());		//注册驱动
            } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
            }
        }
    }
    
    
    我们可以发现,这里面有一个静态代码块,里面是DriverManager进行注册驱动操作的,看到这里,大家应该明白了,虽然我们写的是
    Class.forName("com.mysql.jdbc.Driver");
    但其实真正注册驱动的其实是DriverManager类,其实mysql5之后的驱动jar包可以省略注册驱动的步骤。
    但是还是建议写上注册驱动,以为可以向下兼容(使用低版本的mysql依然可以使用同样的代码)
    

    下面我们来说DriverManager类的第二个功能

  2. 获取数据库连接

    方法:

    public  static Connection getConnection(String url, String user, String password)		
    

    我们可以看到,这是DriverManager的静态方法,提供三个参数,分别是url、user、password

    参数:

    • url:指定连接的路径
      • 语法: jdbc:mysql://ip地址(域名):端口号/数据库名称
      • 例子:jdbc:mysql://localhost:3306/db3
      • 细节:如果数据库连接的url是本主机,并且端口号是3306,则url可以写成jdbc:mysql:///数据库名称
    • user:用户名
    • password:密码
Connection

Connection对象:数据库连接对象

功能:

  1. 获取执行sql的对象
    • Statement createStatement()
    • PreparedStatement prepareStatement(String sql))
  2. 管理事务
    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
    • 提交事务:commit()
    • 回滚:rollback()
Statement

Statement:执行sql的对象

功能:

  • 执行sql
    1. boolean execute(String sql) : 可以执行任意的sql (了解)
    2. int executeUpdate(String sql):执行DML语句(insert、delete、update)、DQL语句(caeate、drop、alter)
      • 返回值:影响的行数,可以通过这个值判断执行结果是否成功。影响的行数大于0,执行成功,反之,执行失败。
    3. ResultSet executeQuery(String sql):执行DQL语句(select)
ResultSet

ResultSet:结果集对象,封装查询结果

方法

  • boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果没数据返回false,否则返回true

  • getXxx(参数):获取数据

    • Xxx:代表数据类型 如: int getInt(), String getString()

    • 参数:

      1. int:代表列的编号,从1开始,例如:getString(1)
      2. String:代表列的名称,例如:getDouble(“balance”)
    • 使用步骤:

      1. 游标向下移动一行
      2. 判断当前行是否有数据
      3. 获取数据
      //循环判断游标是否是最后一行末尾
      while (re.next()){                      //判断当前行先向下移动一行,然后判断是不是不是最后一行,不是最后一行返回true,是最后一行返回false
                      int id = re.getInt(1);
                      String name = re.getString("name");
                      double money = re.getDouble(3);
                      System.out.println(id + "--" + name + "--" + money);
                  }
      
      

下面我们来练习一下ResultSet的使用:

打印account表中的全部记录

这是执行sql语句之前account表中的记录

在这里插入图片描述

我们执行如下代码:

public static void main(String[] args) {
        Connection con = null;
        Statement sta  = null;
        ResultSet re = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 定义sql语句
            String sql = "select * from account";
            // 3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            // 4. 获取执行sql对象
            sta = con.createStatement();
            // 5. 执行sql并返回结果
            re = sta.executeQuery(sql);            //返回的其实是影响的行数
            // 6. 处理结果
            while (re.next()){                      //判断当前行先向下移动一行,然后判断是不是不是最后一行,不是最后一行返回true,是最后一行返回false
                int id = re.getInt(1);
                String name = re.getString("name");	//获取当前游标指向行的那一列名字为name的那一个数据,类型为String
                double money = re.getDouble(3);		//获取当前游标指向行的第三列的那一个数据,类型为Double
                System.out.println(id + "--" + name + "--" + money);
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 7. 释放资源
            if(re != null){
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

执行之后输出如下:

在这里插入图片描述

我们发现,确实完全的打印出来了。

接下来我们来进行一个小练习

定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,并返回

这是我们定义的Emp类,里面的成员变量分别对应于emp表中的每一个属性:

public class Emp {
    private int id;
    private String ename;
    private int job_id;
    private int mgr;
    private Date joindate;
    private double salary;
    private double bonus;
    private int dept_id;

    public int getId() {
        return id;
    }

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

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public int getJob_id() {
        return job_id;
    }

    public void setJob_id(int job_id) {
        this.job_id = job_id;
    }

    public int getMgr() {
        return mgr;
    }

    public void setMgr(int mgr) {
        this.mgr = mgr;
    }

    public Date getJoindate() {
        return joindate;
    }

    public void setJoindate(Date joindate) {
        this.joindate = joindate;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public int getDept_id() {
        return dept_id;
    }

    public void setDept_id(int dept_id) {
        this.dept_id = dept_id;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", ename='" + ename + '\'' +
                ", job_id=" + job_id +
                ", mgr=" + mgr +
                ", joindate=" + joindate +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", dept_id=" + dept_id +
                '}';
    }
}

这是我们写的方法:

public static void main(String[] args) {
        List<Emp> list = new JDBCdemo8().findAll();
        System.out.println(list.size());
        list.forEach(System.out::println);			//lambda表达式遍历输出
    }

    public List<Emp> findAll(){
        Connection con = null;
        Statement sta = null;
        ResultSet re = null;
        List<Emp> list = new ArrayList<Emp>();
        Emp emp = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 定义sql语句
            String sql = "select * from  emp";
            // 3. 获取数据库连接对象
            con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
            // 4. 获取sql执行对象
            sta = con.createStatement();
            // 5. 执行sql语句并返回结果
            re = sta.executeQuery(sql);
            while(re.next()){
                int id = re.getInt("id");
                String ename = re.getString("ename");
                int job_id = re.getInt("job_id");
                int mgr = re.getInt("mgr");
                Date joindate = re.getDate("joindate");
                double salary = re.getDouble("salary");
                double bonus = re.getDouble("bonus");
                int dept_id = re.getInt("dept_id");
                emp = new Emp();
                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);
                list.add(emp);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            if(re != null){
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

输出结果如下:

在这里插入图片描述

好的,我们发现表中的记录已经全部打印了

PreparedStatement

PreparedStatement:执行sql的对象,与Statement不同的是它已经预编译过了,预编译的意思是它是动态sql,不是拼接而成的。

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

    1. 我们输入用户名和密码,和一个表中存在的用户名和密码比较,检查用户名和密码是否正确。

      这是我们的用户名和密码表

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0m10ro6v-1649203053145)(sql15.png)]

      1. 例如:我们输入 用户名:abc, 密码:a’ and ’ a ’ = ’ a

      2. sql:select * from user where username = ‘abc’ and password = ‘a ’ or ’ a ’ = ’ a’

        我们的代码如下:

        public static void main(String[] args) {
                Scanner in = new Scanner(System.in);
                // 1. 键盘录入,判断用户名和密码
                String user = in.nextLine();
                String password = in.nextLine();
                // 2. 调用方法
                boolean value = new JDBCdemo10().login2(user, password);
                // 3. 判断结果
                if(value){
                    System.out.println("登陆成功");
                }else {
                    System.out.println("登陆失败");
                }
        
            }
            //使用Statement实现
            public boolean login(String username, String password){
                if(username == null || password == null){
                    return false;
                }
                Connection con = null;
                Statement sta = null;
                ResultSet re = null;
                try {
                    // 1. 获取数据库连接对象
                    con = JDBCUtils.getConnection();
                    // 2. 定义sql语句
                    String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
                    System.out.println(sql);
                    // 3. 获取执行sql对象
                    sta = con.createStatement();
                    // 4. 执行sql并返回结果
                    re = sta.executeQuery(sql);
                    // 5. 处理结果
                    return re.next();
                } catch (SQLException e) {
                    e.printStackTrace();
                }  finally {
                    JDBCUtils.close(re, sta, con);
                }
        
                return false;
            }
            public boolean login2(String username, String password){
                if(username == null || password == null){
                    return false;
                }
                Connection con = null;
                Statement sta = null;
                ResultSet re = null;
                try {
                    // 1. 获取数据库连接对象
                    con = JDBCUtils.getConnection();
                    // 2. 定义sql语句
                    String sql = "select * from user where username = '" + username +"' and password = '"+ password+"' ";
                    // 3. 获取执行sql对象
                    sta = con.createStatement();
                    //pre.setString(1, username);
                    //pre.setString(2, password);
                    System.out.println(sql);
                    // 4. 执行sql并返回结果
                    re = sta.executeQuery(sql);
                    // 5. 处理结果
                    return re.next();
                } catch (SQLException e) {
                    e.printStackTrace();
                }  finally {
                    JDBCUtils.close(re, sta, con);
                }
        
                return false;
            }
        /*
        注意:这里用到了JDBCUtils类,我们会在下面讲解这个工具类
        */
        

        结果如下:在这里插入图片描述

      ​ 从上面的例子中我们可以看出,我们利用静态sql,也就是字符串拼接执行sql语句的方法可能会产生安全性问题(我们称为 sql注入)。那么我们如何解决这种安全性问题呢?这就需要用到PreparedStatement类了,下面我们来了解一下它的使用

  2. 解决sql注入:使用PreparedStatement类

    1. 参数使用?作为占位符。例如:

      String sql = "select * from user where username = ? and password = ? ";
      
    2. 步骤:

      1. 导入驱动jar包

      2. 注册驱动

      3. 获取数据库连接对象Connection

      4. 定义sql

        ​ 注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?

      5. 获取执行sql的对象PreparedStatement pre = con.prepareStatement(String sql);

      6. 给?赋值

        1. 方法:setXxx(参数1,参数2)
          • 参数1:?的位置编号,从1开始
          • 参数2:?的值
      7. 执行sql,接受返回结果,不需要传递sql语句

      8. 处理结果

      9. 释放资源

  3. 注意:后期都会使用PreparedStatement对象来完成增删查改的所有操作

    1. 可以防止sql注入
    2. 效率更高

当我们使用PreparedStatement类时,代码如下:

package cn.itcast.jdbc;

import cn.itcast.util.JDBCUtils;

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

/**
 * 通过键盘录入用户名和密码
 * 判断是否登陆成功
 */
public class JDBCdemo10 {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 1. 键盘录入,判断用户名和密码
        String user = in.nextLine();
        String password = in.nextLine();
        // 2. 调用方法
        boolean value = new JDBCdemo10().login2(user, password);
        // 3. 判断结果
        if(value){
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆失败");
        }

    }
    //使用Statement实现
    public boolean login(String username, String password){
        if(username == null || password == null){
            return false;
        }
        Connection con = null;
        Statement sta = null;
        ResultSet re = null;
        try {
            // 1. 获取数据库连接对象
            con = JDBCUtils.getConnection();
            // 2. 定义sql语句
            String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
            System.out.println(sql);
            // 3. 获取执行sql对象
            sta = con.createStatement();
            // 4. 执行sql并返回结果
            re = sta.executeQuery(sql);
            // 5. 处理结果
            return re.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }  finally {
            JDBCUtils.close(re, sta, con);
        }

        return false;
    }
    //使用Preparement实现
    public boolean login2(String username, String password){
        if(username == null || password == null){
            return false;
        }
        Connection con = null;
        PreparedStatement pre = null;
        ResultSet re = null;
        try {
            // 1. 获取数据库连接对象
            con = JDBCUtils.getConnection();
            // 2. 定义sql语句
            String sql = "select * from user where username = ? and password = ? ";
            // 3. 获取执行sql对象
            pre = con.prepareStatement(sql);
            pre.setString(1, username);
            pre.setString(2, password);
            System.out.println(sql);
            // 4. 执行sql并返回结果
            re = pre.executeQuery();
            // 5. 处理结果
            return re.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }  finally {
            JDBCUtils.close(re, pre, con);
        }

        return false;
    }

}

当我们再次输入相同的用户名和密码时,发现如下结果:

在这里插入图片描述

我们发现登录失败,因此很好的解决了sql注入问题。

JDBC工具类:JDBCUtils

什么是JDBCUtils工具类呢?我们发现,每次在使用JDBC时,步骤基本如下:

  1. 注册驱动
  2. 获取数据库连接对象
  3. 定义sql语句
  4. 获取执行sql语句对象
  5. 执行sql并返回结果
  6. 处理结果
  7. 释放资源

在这里插入图片描述

在这里插入图片描述

我们从上面可以看出,我们在获取数据库连接对象时每次都要重新写一下url、user、root非常繁琐,而且每次在释放资源时都要写那么一段长长的代码,很繁琐。而且这两个操作一般都是很固定的,那么我们可不可以把这两个操作封装成工具类,这样我们每次调用的时候只要调用工具类就可以了,很简便。那么我们怎样写这个JDBCUtils工具类呢?

在这里插入图片描述

我们写一个配置文件,名字叫做:jdbc.properties,里面写上如下格式,url = 后面写连接数据库的位置,user=后写数据库的用户名,password=后写数据库的密码,driver厚些com.mysql.jdbc.Driver这其实是我们将Driver类加载进内存,这是注册驱动操作。而上面的url、user、password是在数据库连接时我们需要传递到连接方法的参数。

我们写的JDBCUti工具类如下:

在获取数据库连接对象时,我们只需更改一下配置文件中的参数,调用getConnection() 方法即可。

在释放资源时,我们只需将数据库连接对象,执行sql对象,如果有结果集对象的话,也把结果集对象作为参数传递进close方法,直接调用close方法就可以完成释放资源的操作了,很简便。

public class JDBCUtils {

    private static String url;
    private static String user;
    private static String password;
    private static String driver;

    static {

        try {
            //读取资源文件,获取值
            // 1. 创建Properties集合类
            Properties pro = new Properties();
            ClassLoader cls = JDBCUtils.class.getClassLoader();
            URL res = cls.getResource("jdbc.properties");
            String path = res.getPath();
            System.out.println(path);
            // 2. 加载文件
            //pro.load(new FileReader("D:\\javaJDBC\\src\\jdbc.properties"));
            pro.load(new FileReader(path));
            // 3. 获取数据、赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            Class.forName(driver);
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取连接
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }

    /**
     *
     * 释放连接
     */
    public static void close(Statement sta, Connection con){
        if(sta != null){
            try {
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con != null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放连接
     * @param res
     * @param sta
     * @param con
     */
    public static void close(ResultSet res, Statement sta, Connection con){
        if(res != null){
            try {
                res.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(sta != null){
            try {
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con != null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

下面是一个使用JDBCUtils的代码:

/**
 * 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回
 * JDBCUtils工具类的演示
 */
public class JDBCdemo9 {

    public static void main(String[] args) {
        List<Emp> list = new JDBCdemo9().findAll();
        System.out.println(list.size());
        list.forEach(System.out::println);
    }

    public List<Emp> findAll(){
        Connection con = null;
        Statement sta = null;
        ResultSet re = null;
        List<Emp> list = new ArrayList<Emp>();
        Emp emp = null;
        try {
            // 1. 注册驱动
            // 2. 定义sql语句
            String sql = "select * from emp";
            // 3. 获取数据库连接对象
            con = JDBCUtils.getConnection();
            // 4. 获取sql执行对象
            sta = con.createStatement();
            // 5. 执行sql语句并返回结果
            re = sta.executeQuery(sql);
            while(re.next()){
                int id = re.getInt("id");
                String ename = re.getString("ename");
                int job_id = re.getInt("job_id");
                int mgr = re.getInt("mgr");
                Date joindate = re.getDate("joindate");
                double salary = re.getDouble("salary");
                double bonus = re.getDouble("bonus");
                int dept_id = re.getInt("dept_id");
                emp = new Emp();
                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);
                list.add(emp);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JDBCUtils.close(re, sta, con);
        }
        return list;
    }
}

我们可以发现相较于之前我们写过的代码来说,注册驱动、获取数据库连接对象、释放资源的操作非常简便,大大提高了我们写代码的速度。

JDBC控制事务

  1. 什么是事务呢?

一个包含多个步骤的业务操作,如果交由事务管理,那么这多个步骤要么同时成功,要么同时失败。

  1. 操作
    1. 开启事务
    2. 提交事务
    3. 回滚事务
  2. 使用Connection对象来管理事务
    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
      • 注意:在执行sql之前开启事务
    • 提交事务:commit()
      • 注意:当所有sql都执行完再提交事务
    • 回滚事务:rollback()
      • 在catch中回滚事务

下面是一个包含多个步骤的业务操作:张三借给李四500元钱

代码如下:

public class JDBCdemo11 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement pre1 = null;
        PreparedStatement pre2 = null;
        try{
            // 1. 注册驱动
            con = JDBCUtils.getConnection();
            con.setAutoCommit(false);						//开启事务
            // 2. 定义sql语句
            // 2.1 张三减500
            String sql1 = "update account set balance = balance - ? where id = ?";
            String sql2 = "update account set balance = balance + ? where id = ?";
            // 3. 定义sql执行对象
            pre1 = con.prepareStatement(sql1);
            pre2 = con.prepareStatement(sql2);
            pre1.setDouble(1, 500);
            pre1.setInt(2, 1);
            pre2.setDouble(1, 500);
            pre2.setInt(2, 2);
            pre1.executeUpdate();
            int i = 3/0;
            pre2.executeUpdate();
            con.commit();								//提交事务
        } catch (Exception throwables) {
            if(con != null){
                try {
                    con.rollback();						//回滚
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        }  finally {
            JDBCUtils.close(pre1, con);
            JDBCUtils.close(pre2, con);
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值