2022暑假软件创新实验室集训 JDBC

使用JDBC前期准备

  • 对应数据库版本的数据库连接驱动jar包,下载链接,例如我的数据库版本8.0.25,所以我应该下载8.0.25连接数据包

image-20220706143711145

  • MySQL数据库,这边建议安装数据库MySQL 8,因为MySQL 5在我大二用的时候好像有一些问题

  • 根据自己喜好创建一个数据库,上课的时候可以自己手动操作一下,像本人新建的数据库是这样的

    image-20220709182429111

JDBC简单介绍

JDBC(Java DataBase Connectivity)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询、更新(增删改)数据库中数据的方法。JDBC,是Sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。打个不恰当的比方,就是本来你要使用不同的方法来使用,可以参考下图,下图出自博客

20201227150402505

如何使用JDBC?

知道了JDBC是什么,为什么会有JDBC后,我们就需要学会使用JDBC。

1. 下载驱动包

在该博文一开始,就让大家下载数据库相对应的驱动包,因为我的数据库是MySQL 8.0.25,所以我下载下来的数据包是MySQL 8.0.25的驱动包,如图所示

image-20220709151844089

2. 创建lib目录

准备好数据库的驱动包之后,我们就要创建一个普通的Java项目,我这里使用的是IDEA企业版,这里就不放创建完成之后的截图了。然后我们就需要在项目文件夹下创建一个lib目录,这里的lib目录就是专门放Java一系列包的地方,像这次我们上的是JDBC,所以我们这里面lib目录存放的就是MySQL驱动包。

image-20220709152713823

3. 解压驱动包,导入驱动jar

在创建完成lib目录之后,我们需要解压驱动包,在解压之后我们可以看到下面内容(只针对MySQL 8.0.25驱动包)

image-20220709154223550

我们唯一需要用到的就是里面mysql-connector-java-8.0.25.jar,把这个包复制粘贴至刚刚创建的lib目录,在目录下右键jar包——>添加为库,如图所示

image-20220709154913265

然后直接确定

image-20220709155034901

4. 使用Java代码对MySQL数据库进行操作

  • 加载MySQL驱动

    Class.forName("com.mysql.cj.jdbc.Driver");
    
  • 获取数据库连接

    // 获取mysql连接地址
    String strURL = "jdbc:mysql://localhost:3306/数据库名称?&useSSL=false&serverTimezone=UTC";
    // 用户名称
    String username = "root";
    // 用户密码
    String password = "123456";
    Connection con = DriverManager.getConnection(strURL, username, password);
    
  • 创建statement对象,在之后为了防止SQL语句注入,我们就会用到PreparedStatement来进行预处理

    // 创建statement类对象,用来执行SQL语句!
    Statement stm = con.createStatement();
    

    当我们在拿到stm对象之后就可以使用该对象来操作mysql语句,增删改查数据库

  • 了解Statement对象执行SQL语句的方法

    // 通过SQL查询,把查询到的结果返回至Result的结果集
    ResultSet rs = st.executeQuery(sql);
    // 通过SQL更新数据库内容,把受影响的行数返回
    int i = st.executeUpdate(sql);
    
  • 在数据库操作完毕之后,需要关闭ResultSetStatementConnection

    我们在操作完数据库之后需要知道ResultSetStatementConnection是在程序中占用内存的,所以我们需要关闭,而且需要注意关闭的顺序,是从左到右的。

    首先是最最简单的关闭方法,直接在完成操作之后一次关闭

    rs.close();
    st.close();
    con.close();
    

    但是我们也可以变化的思路,把数据库关闭操作直接封装成一个方法就像下面这样。

    // 关闭数据库连接
    public static void release(Connection con, Statement st, ResultSet rs) {
        boolean flag = true;
    
        if (rs != null) {
            try {
                rs.close();
                rs = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }
    
        if (st != null) {
            try {
                st.close();
                st = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }
    
        if (con != null) {
            try {
                con.close();
                con = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }
    }
    

    在之后,我们会自己制作一个数据库操作类,来封装一些数据库的简单操作,比如说数据库连接、数据库关闭或者数据库其他操作(数据库查询和更新)

简单实战一下

查询操作

// 定义变量
Connection con = null;
Statement st = null;
ResultSet rs = null;

try {
    // 加载数据库驱动com.mysql.cj.jdbc.Driver
    String strDriver = "com.mysql.cj.jdbc.Driver";
    // 获取mysql连接地址
    String strURL = "jdbc:mysql://localhost:3306/market?&useSSL=false&serverTimezone=UTC";
    // 数据库用户名称
    String username = "root";
    // 数据库用户密码
    String password = "123456";
    // 加载数据库驱动程序
    Class.forName(strDriver);
    System.out.println("加载数据库驱动");
    // 获取数据库连接
    con = DriverManager.getConnection(strURL, username, password);
    System.out.println("数据库连接成功");
    // 获取Statement对象,来对数据库语句进行操作
    st = con.createStatement();

    String sql = "SELECT * FROM book";
    // 通过SQL查询,把查询到的结果放入Result的结果集中
    rs = st.executeQuery(sql);
	// 使用next来遍历数据结果集,并且输出
    while (rs.next()) {
        int id = rs.getInt(1);
        String name = rs.getString(2);
        double price = rs.getDouble(3);
        System.out.println(id + " " + name + " " + price);
    }
    
} catch (SQLException | ClassNotFoundException e) {
    e.printStackTrace();
} finally {
    // 关闭结果集ResultSet
    if (rs != null) {
        try {
            rs.close();
            System.out.println("ResultSet成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // 关闭操作对象Statement
    if (st != null) {
        try {
            st.close();
            System.out.println("Statement成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // 关闭数据库连接对象Connection
    if (con != null) {
        try {
            con.close();
            System.out.println("Connection成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

上面的示例只是针对数据库的查询操作,在很多时候我们不仅仅需要的是查询,而是数据库的更新操作,比如说增删改操作。

1. 增操作

这里我想插入一条新的数据库数据,编号6,书本名嫌疑人X的献身,价格43,在数据库中的SQL语句如下:

INSERT INTO book VALUES(6, "嫌疑人X的献身", 43);

所以,我们只需要简单地修改示例中的第24~33行代码就可以了

String sql = "INSERT INTO book VALUES(6, '嫌疑人X的献身', 43)";
int i = st.executeUpdate(sql);
// 判断受影响的行数是不是为0,若是0,则刚才的更新操作没有成功,若不是0,则SQL语句操作成功
if (i != 0) {
    System.out.println("插入成功!");
} else {
    System.out.println("插入失败!");
}

结果成功插入这条数据

image-20220709191056464

查看数据库

image-20220709191129846

2. 删操作

这里我们假设需要删除刚刚那条数据,数据库SQL语句就是

DELETE FROM book WHERE bid = 6;

因为删操作同属于数据库更新操作,所以只需要改变其中的String变量就行

String sql = "DELETE FROM book WHERE bid = 6";

最后的结果成功删除,如图所示

image-20220709192906038

3. 改操作

这里假如我希望修改第5条记录价格,数据库SQL的语句就是

UPDATE book SET bprice = 20 WHERE bid = 5;

这里我们也只需要修改String变量就行

String sql = "UPDATE book SET bprice = 20 WHERE bid = 5";

最后的结果修改成功,如图所示

image-20220709193202748

PreparedStatement的使用

其实在PreparedStatement的使用,跟Statement的使用几乎差不多,只是PreparedStatement多出了一点点的步骤,下面给出代码,可以仔细比较一下与Statement的使用差别

// 定义变量
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;

try {
    // 加载数据库驱动com.mysql.cj.jdbc.Driver
    String strDriver = "com.mysql.cj.jdbc.Driver";
    // 获取mysql连接地址
    String strURL = "jdbc:mysql://localhost:3306/market?&useSSL=false&serverTimezone=UTC";
    // 数据库用户名称
    String username = "root";
    // 数据库用户密码
    String password = "123456";
    // 加载数据库驱动程序
    Class.forName(strDriver);
    System.out.println("加载数据库驱动");
    // 获取数据库连接
    con = DriverManager.getConnection(strURL, username, password);
    System.out.println("数据库连接成功");
    
    
	// 数据库操作语句,这里的问号是我们需要自行设置的参数
    String sql = "SELECT * FROM book WHERE bid = ?";
    // SQL语句预处理,获取PreparedStatement对象
    pst = con.prepareStatement(sql);
    // 为SQL语句设置参数,这里setObject()方法中的第一个参数代表的是SQL语句中的第几个问号,而第二参数代表的是设置变量,比如这里我们是需要查询的编号为1的书本信息
    pst.setObject(1, 1);
    rs = pst.executeQuery();

    
    while (rs.next()) {
        int id = rs.getInt(1);
        String name = rs.getString(2);
        double price = rs.getDouble(3);
        System.out.println(id + " " + name + " " + price);
    }

} catch (SQLException | ClassNotFoundException e) {
    e.printStackTrace();
} finally {
    // 关闭结果集ResultSet
    if (rs != null) {
        try {
            rs.close();
            System.out.println("ResultSet成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // 关闭操作对象Statement
    if (pst != null) {
        try {
            pst.close();
            System.out.println("Statement成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // 关闭数据库连接对象Connection
    if (con != null) {
        try {
            con.close();
            System.out.println("Connection成功关闭");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

这边需要注意的是这里我设置参数的时候用了偷懒的方式,即使用pst.setObject(1, 1)的方式,但其实这边应该使用pst.setInt(1, 1)的方式更加准确,因为我们需要根据具体的数据库字段来看。在以后我们会用到数据库操作通用类,来进行数据的连接、关闭、更新操作,所以其实最后还是会使用pst.setObject()方法来设置参数

那么根据上面的代码,是否可以以此类推,如何使用Preparedment进行数据库操作?下面给出删除数据记录的操作示例,其余增加和修改操作,还请同学们自己操作一下

// 数据库操作语句,这里的问号是我们需要自行设置的参数
String sql = "DELETE FROM book WHERE bid = ? AND bprice = ?";
// SQL语句预处理,获取PreparedStatement对象
pst = con.prepareStatement(sql);
// 为SQL语句设置参数,这里setObject()方法中的第一个参数代表的是SQL语句中的第几个问号,而第二参数代表的是设置变量,比如这里我们是需要查询的编号为1的书本信息
pst.setInt(1, 1);
// 因为我创建的数据库书本价格字段是Double
pst.setDouble(2, 12);
int i = pst.executeUpdate();
if (i != 0) {
    System.out.println("成功");
} else {
    System.out.println("失败");
}

学到这里不知道大家有没有一点点的疑惑,为什么有了Statement还需要PreparedStatement

StatementPreparedStatement区别

那么我们就需要知道两者的区别了,参考博客

  1. 性能方面

    Statement语句

    // 获取数据库连接
    con = DriverManager.getConnection(strURL, username, password);
    // 获取Statement对象,来对数据库语句进行操作
    st = con.createStatement();
    String sql = "SELECT * FROM book";
    // 通过SQL查询,把查询到的结果放入Result的结果集中
    rs = st.executeQuery(sql);
    

    PreparedStatement语句

    // 获取数据库连接
    con = DriverManager.getConnection(strURL, username, password);
    // 数据库操作语句,这里的问号是我们需要自行设置的参数
    String sql = "SELECT * FROM book WHERE bid = ?";
    // SQL语句预处理,获取PreparedStatement对象
    pst = con.prepareStatement(sql);
    // 为SQL语句设置参数,这里setObject()方法中的第一个参数代表的是SQL语句中的第几个问号,而第二参数代表的是设置变量,比如这里我们是需要查询的编号为1的书本信息
    pst.setObject(1, 1);
    rs = pst.executeQuery();
    

    从上面的对比,我们可以看到PreparedStatement有预编译的过程,而且绑定了sql语句,无论执行多少遍,都不会再次进行编译。而Statement则不同,sql执行多少遍,就需要编译多少遍,所以PreparedStatement效率要比Statement

  2. 方便后期改动,提高代码阅读和可维护性

    我们可以注意到Statement的数据库操作语句是定死的,当程序运行的时候是无法改变的,但是PreparedStatement在SQL语句中需要自定义参数都是带上?的,我们是需要通过setInt()setObject()setString()等方法进行参数的设置就行,便于代码的阅读和维护

  3. 防止SQL注入

    如果你使用的是Statement语句的话,想要查询某一个ID书,程序预留的语句就是"SELECT * FROM book WHERE bid = " + bid, 但是假如这个时候有人想要知道所有的数据,那么他会把bid = 2 OR 1 = 1,最后形成的SQL语句就是SELECT * FROM book WHERE bid = 2 OR 1 = 1,我们知道1 = 1是绝对成立的,所以最后呈现的就是所有的数据库数据。这个时候PreparedStatement就成了关键,在PreparedStatementSQL语句是采用预编译的,SQL语句的结构也是一开始设计好的,采用设置?来进行参数的填写,所以不能假如一些奇奇怪怪的字符来实现SQL注入。

数据库工具类

我们知道不断地重复编写数据库连接、关闭这些代码是一件非常无聊的事情,我们不可能在一个项目中多次编写这一段相同的代码,这会大大降低编写代码的效率,所以我们想出了一个办法就是把这些操作封装成一个Java类。

public class JDBCUtil {
    private static final String driver = "com.mysql.cj.jdbc.Driver";
    private static final String url = "jdbc:mysql://localhost:3306/market?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8";
    private static final String userName = "root";
    private static final String password = "123456";

    // 获取数据库连接
    public static Connection getConnection() {
        Connection con = null;
        try {
            Class.forName(driver);
            con = DriverManager.getConnection(url, userName, password);
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return con;
    }

    // 数据库查询,返回结果集
    public static ResultSet query(Connection con, PreparedStatement st, ResultSet rs, String sql
            , Object[] params) throws SQLException {
        st = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE,
                ResultSet.CONCUR_READ_ONLY);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                st.setObject(i + 1, params[i]);
            }
        }
        rs = st.executeQuery();
        return rs;
    }

    // 数据库增删改
    public static int update(Connection con, String sql
            , Object[] params, ResultSet rs, PreparedStatement st) throws SQLException {
        st = con.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            st.setObject(i + 1, params[i]);
        }
        return st.executeUpdate();
    }

    // 关闭数据库连接
    public static void release(Connection con, Statement st, ResultSet rs) {
        boolean flag = true;

        if (rs != null) {
            try {
                rs.close();
                rs = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (st != null) {
            try {
                st.close();
                st = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (con != null) {
            try {
                con.close();
                con = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }
    }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值