JDBC简介及原理和使用介绍

JDBC简介及原理和使用介绍

JDBC简介

jdbc概述

​ Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

​ 使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问

例如,我们在Java代码中如果要访问MySQL,那么必须编写代码操作JDBC接口。JDBC接口是Java标准库自带的,所以可以直接编译。而具体的JDBC驱动是由数据库厂商提供的。因此,访问某个具体的数据库,我们只需要引入该厂商提供的JDBC驱动,就可以通过JDBC接口来访问,这样保证了Java程序编写的是一套数据库访问代码,却可以访问各种不同的数据库
Java 应用程序 -> JDBC Interface -> JDBC Driver -> Database

JDBC 的常用接口和类

  • Driver 接口:加载驱动程序。
  • DriverManager 类:装人所需的 JDBC 驱动程序,编程时调用它的方法来创建连接。
  • Connection 接口:编程时使用该类对象创建 Statement 对象。
  • Statement 接口:编程时使用该类对象得到 ResultSet 对象。
  • ResultSet 类:负责保存 Statement 执行后所产生的查询结果。

JDBC接口(API)

JDBC接口(API)包括两个层次

  • 面向应用的API
    • Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)
  • 面向数据库的API
    • Java Driver API,供开发商开发数据库驱动程序用

Driver接口

  • java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
  • 在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用
    • Oracle的驱动:oracle.jdbc.driver.OracleDriver
    • mySql的驱动: com.mysql.jdbc.Driver

建立连接的五大步骤

  1. 加载(注册驱动)数据库
  2. 建立链接(Connection)
  3. 创建执行SQL的语句(Statement)
  4. 执行语句
  5. 处理结果集(ResultSet)
  6. 关闭数据库释放资源

加载与注册JDBC驱动

  • 方式1:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名

    • Class.forName(com.mysql.jdbc.Driver);
      
  • 方式2:DriverManager 类是驱动程序管理器类,负责管理驱动程序

    • DriverManager.registerDriver(com.mysql.jdbc.Driver);
      

建立连接(Connection)

  • 可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
  • user,password可以用“属性名=属性值”方式告诉数据库
  • JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
  • JDBC URL的标准由三部分组成,各部分间用冒号分隔。
    • jdbc:子协议:子名称
    • 协议:JDBC URL中的协议总是jdbc
    • 子协议:子协议用于标识一个数据库驱动程序
    • 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名

示例:

jdbc:mysql://localhost:3306/databasename

jdbc : 协议
mysql : 子协议
localhost:3306/databasename : 子名称

常见的数据库URL

jdbc:oracle:thin:@localhost:1521:testdb
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=testdb
jdbc:mysql://localhost:3306/testdb

PreparedStatement接口

PreparedStatement,是java.sql包中的接口,继承了Statement,并与之在两方面有所不同,包含已编译的 SQL 语句,用以执行包含动态参数的SQL查询和更新。

  • 可以通过调用 Connection 对象的 preparedStatement() 方法获取 PreparedStatement 对象
  • PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
  • PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数
    • 设置的 SQL 语句中的参数的索引(从 1 开始)
    • 设置的 SQL 语句中的参数的值

数据类型转换

java类型 SQL类型
boolean BIT
byte TINYINT
short SMALLINT
int INTEGER
long BIGINT
String CHAR,VARCHAR,LONGVARCHAR
byte array BINARY , VAR BINARY
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP

数据库释放资源

  • 释放ResultSet, Statement,Connection

数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放

ResultSet接口

ResultSet,数据库结果集的数据表,通常通过执行查询数据库的语句生成

  • 通过调用 PreparedStatement 对象的 excuteQuery() 方法创建该对象
  • ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现
  • ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行

关于ResultSet的说明

  1. 查询需要调用Prepared Statement 的 executeQuery() 方法,查询结果是一个 ResultSet 对象
  2. 关于 ResultSet:代表结果集
    1. ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果
    2. 调用 PreparedStatement 对象的 executeQuery() 可以得到结果集
    3. ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一条记录的前面
  3. 可以调用 next() 方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于Iterator 对象的 hasNext() 和 next() 方法的结合体
  4. 当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值
  5. ResultSet 也需要进行close关闭

JDBC代码使用

环境准备

准备MySQL测试库

一般常用的为MySQL5.7或者MariaDB均可

创建一个database:wow

CREATE DATABASE wow;

创建一张表:wow_info

CREATE TABLE `wow_info` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`role` varchar(255) DEFAULT NULL,
	`role_cn` varchar(255) DEFAULT NULL,
	`role_pinyin` varchar(255) DEFAULT NULL,
	`zhuangbei` varchar(255) DEFAULT NULL,
	PRIMARY KEY (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARSET = utf8

插入一些实验样例数据:

INSERT INTO `wow_info`
VALUES (1, 'fs', '法师', 'fashi', '布甲'),
	(2, 'ms', '牧师', 'mushi', '布甲'),
	(3, 'ss', '术士', 'shushi', '布甲'),
	(4, 'dz', '盗贼', 'daozei', '皮甲'),
	(5, 'ws', '武僧', 'wuseng', '皮甲'),
	(6, 'xd', '德鲁伊', 'xiaode', '皮甲'),
	(7, 'sq', '圣骑士', 'shengqi', '板甲'),
	(8, 'zs', '战士', 'zhanshi', '板甲'),
	(9, 'dk', '死亡骑士', 'siwangqishi', '板甲'),
	(10, 'dh', '恶魔猎手', 'emolieshou', '皮甲');

查看环境信息:

[root@wangting ~]# mysql -uroot -p
Enter password: 
MariaDB [(none)]> use wow;
Database changed
MariaDB [wow]> show tables;
+---------------+
| Tables_in_wow |
+---------------+
| wow_info      |
+---------------+
1 row in set (0.000 sec)
MariaDB [wow]> select * from wow_info;
+----+------+--------------+-------------+-----------+
| id | role | role_cn      | role_pinyin | zhuangbei |
+----+------+--------------+-------------+-----------+
|  1 | fs   | 法师         | fashi       | 布甲      |
|  2 | ms   | 牧师         | mushi       | 布甲      |
|  3 | ss   | 术士         | shushi      | 布甲      |
|  4 | dz   | 盗贼         | daozei      | 皮甲      |
|  5 | ws   | 武僧         | wuseng      | 皮甲      |
|  6 | xd   | 德鲁伊       | xiaode      | 皮甲      |
|  7 | sq   | 圣骑士       | shengqi     | 板甲      |
|  8 | zs   | 战士         | zhanshi     | 板甲      |
|  9 | dk   | 死亡骑士     | siwangqishi | 板甲      |
| 10 | dh   | 恶魔猎手     | emolieshou  | 皮甲      |
+----+------+--------------+-------------+-----------+
10 rows in set (0.000 sec)

idea新建maven项目

项目信息可自定义设置,不影响代码调试测试

pom文件中增加Test、MySQLjdbc依赖

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
    </dependencies>

因为核心逻辑使用到jdbc的jar包,所以需要在pom中加依赖

注意:

version版本取决于使用的数据库是什么版本,例如MySQL5.7则需要去找到对应的MySQL版本依赖

所有的依赖信息清单见:

https://mvnrepository.com/artifact/mysql/mysql-connector-java

例如5.7添加了5.1.38,小版本影响不大

mysql
mysql-connector-java
5.1.38

新建package

com.wangting.bigdata

在包下会陆续创建各个实现类,例如 JDBCDemo

pom文件中每加一个新依赖,对应一个新xxx

接口开发使用介绍

项目整体结构:

简单连接数据库demo实现

包下创建一个TestJdbc类

代码示例:

package com.wangting.bigdata;

import java.sql.*;

public class TestJdbc {
    
    
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://47.88.88.88:3306/wow", "root", "123456");
        Statement statement = conn.createStatement();
        String sql = "select * from wow_info";
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
    
    
            System.out.println(
                    resultSet.getInt("id") + "|"
                            + resultSet.getString("role") + "|"
                            + resultSet.getString("role_cn") + "|"
                            + resultSet.getString("role_pinyin") + "|"
                            + resultSet.getString("zhuangbei"));

        }
        statement.close();
        conn.close();
    }
}

执行效果如下:

1|fs|法师|fashi|布甲
2|ms|牧师|mushi|布甲
3|ss|术士|shushi|布甲
4|dz|盗贼|daozei|皮甲
5|ws|武僧|wuseng|皮甲
6|xd|德鲁伊|xiaode|皮甲
7|sq|圣骑士|shengqi|板甲
8|zs|战士|zhanshi|板甲
9|dk|死亡骑士|siwangqishi|板甲
10|dh|恶魔猎手|emolieshou|皮甲

但是这种简单实现,一般业务上很少使用,仅用于调试或者功能测试时使用,弊端非常明显,MySQL连接信息写死在代码中,查询功能高度耦合,非常的不灵活,基于这些原因,需要使用面向对象的思路去拆分功能来实现。

JDBCUtils工具类

创建JDBCUtils类,用于实现连接逻辑,交互配置文件等等

因为实际使用场景中,几乎数据库连接信息都不会写死在代码中,采用了配置文件的形式,使得灵活配置

对于配置文件的实现:

  1. 创建Properties对象
  2. 创建流
  3. 加载流
  4. 通过Properties对象读取文件中的内容
  5. 关闭资源

JDBCUtils工具类详解:

  1. 在JdbcUtils的静态代码static代码块中读取db.properties中的相关配置,并加载驱动

  2. 每次使用JDBC之前都要获取getConnection连接,而获取连接的代码都是固定的,因此可以提取成一个公共方法getConnection;用户代码可以通过调用JdbcUtils.getConnection()来获取连接。

  3. 使用JDBCUtils工具类的优点,在我们有大量使用mysql的数据库的情况下,可以通过更改jdbc.properties配置文件就可以灵活修改数据库的配置,而不是寻找代码然后在一次次更改代码中的数据

  4. 查询操作中可以通过调用JdbcUtils.close(rs, stmt, conn)来关闭释放资源,而更新操作中可以通过调用JdbcUtils.close(stmt, conn)来释放资源

JDBCUtils.java代码示例:

package com.wangting.bigdata;

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

public class JDBCUtils {
    
    
    private static String className;
    private static String url;
    private static String user;
    private static String password;

    static {
    
    
        FileInputStream fis = null;
        try {
    
    
            // 1.创建Properties对象
            Properties p = new Properties();
            // 2.创建流
            fis = new FileInputStream("jdbc.properties");
            // 3.加载流到Properties中
            p.load(fis);
            // 4.通过Properties读取文件中的内容
            user = p.getProperty("user");
            password = p.getProperty("password");
            url = p.getProperty("url");
            className = p.getProperty("className");
        } catch (Exception e) {
    
    
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
    
    
            // 5.关闭资源
            if (fis != null) {
    
    
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }

    /*
        获取Connection对象
     */
    public static Connection getConnection() {
    
    
        try {
    
    
            //1.让Driver类中的静态代码块执行
            Class.forName(className);
            //3.获取Connection对象
            Connection connection = DriverManager.getConnection(url, user, password);
            return connection;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            //终止程序运行
            throw new RuntimeException(e.getMessage());
        }
    }

    /*
        关闭资源
     */
    public static void close(Connection connection, PreparedStatement ps) {
    
    
        if (connection != null) {
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }

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

    public static void close(Connection connection, PreparedStatement ps, ResultSet rs) {
    
    
        close(connection, ps);
        if (rs != null) {
    
    
            try {
    
    
                rs.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

代码行:fis = new FileInputStream(“jdbc.properties”);

这里使用的是以项目的根目录为相对路径,直接文件名意味着在项目顶级路径下查找jdbc.properties配置文件

在项目根目录下创建jdbc.properties配置文件:

user=root
password=123456
url=jdbc:mysql://47.88.88.88:3306/wow
className=com.mysql.cj.jdbc.Driver

使用key=value的键值对形式来进行配置

表对象

表对象Wow.java

这里Wow表仅仅做参考,不同的表对应不同的对象,把MySQL中被操作的表理解成一个对象

后面会用到,也可以在new Wow对象,调用JDBCUtils工具类去操作Wow对象时,再创建。这里提前创建好备着

流程基本固定为:

private定义属性对应表中的字段

无参构造器

全参构造器

属性的get和set方法

重写toString方法

Wow.java代码示例:

package com.wangting.bigdata;

public class Wow {
    
    
    private int id;
    private String role;
    private String role_cn;
    private String role_pinyin;
    private String zhuangbei;

    public Wow() {
    
    
    }

    public Wow(int id, String role, String role_cn, String role_pinyin, String zhuangbei) {
    
    
        this.id = id;
        this.role = role;
        this.role_cn = role_cn;
        this.role_pinyin = role_pinyin;
        this.zhuangbei = zhuangbei;
    }

    public int getId() {
    
    
        return id;
    }

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

    public String getRole() {
    
    
        return role;
    }

    public void setRole(String role) {
    
    
        this.role = role;
    }

    public String getRole_cn() {
    
    
        return role_cn;
    }

    public void setRole_cn(String role_cn) {
    
    
        this.role_cn = role_cn;
    }

    public String getRole_pinyin() {
    
    
        return role_pinyin;
    }

    public void setRole_pinyin(String role_pinyin) {
    
    
        this.role_pinyin = role_pinyin;
    }

    public String getZhuangbei() {
    
    
        return zhuangbei;
    }

    public void setZhuangbei(String zhuangbei) {
    
    
        this.zhuangbei = zhuangbei;
    }

    @Override
    public String toString() {
    
    
        return id + "|" + role + "|" + role_cn + "|" + role_pinyin + "|" + zhuangbei;
    }
}

增删改查操作示例

insert新增数据

原表:

MariaDB [wow]> select * from wow_info;
+----+------+--------------+-------------+-----------+
| id | role | role_cn      | role_pinyin | zhuangbei |
+----+------+--------------+-------------+-----------+
|  1 | fs   | 法师         | fashi       | 布甲      |
|  2 | ms   | 牧师         | mushi       | 布甲      |
|  3 | ss   | 术士         | shushi      | 布甲      |
|  4 | dz   | 盗贼         | daozei      | 皮甲      |
|  5 | ws   | 武僧         | wuseng      | 皮甲      |
|  6 | xd   | 德鲁伊       | xiaode      | 皮甲      |
|  7 | sq   | 圣骑士       | shengqi     | 板甲      |
|  8 | zs   | 战士         | zhanshi     | 板甲      |
|  9 | dk   | 死亡骑士     | siwangqishi | 板甲      |
| 10 | dh   | 恶魔猎手     | emolieshou  | 皮甲      |
+----+------+--------------+-------------+-----------+
10 rows in set (0.000 sec)

创建DemoInsert类插入数据

代码示例:

package com.wangting.bigdata;

import org.junit.Test;

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

public class DemoInsert {
    
    

    @Test
    public void test() throws SQLException {
    
    
        // 1.获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        // 2.sql语句(?符号为占位符)
        String sql = "insert into wow_info(id,role,role_cn,role_pinyin,zhuangbei) values(?,?,?,?,?)";

        // 3.对sql预编译
        PreparedStatement ps = connection.prepareStatement(sql);
        // 3-1.给占位符赋值
        ps.setInt(1, 11);
        ps.setString(2, "lr");
        ps.setString(3, "龙人");
        ps.setString(4, "longren");
        ps.setString(5, "锁甲");

        // 3-2.执行sql语句
        int result = ps.executeUpdate();
        System.out.println("[info-]" + result + "行数据受到影响");

        // 4.关闭资源
        JDBCUtils.close(connection, ps);
    }
}

控制台输出内容:

[info-]1行数据受到影响

执行代码后验证:

MariaDB [wow]> select * from wow_info;
+----+------+--------------+-------------+-----------+
| id | role | role_cn      | role_pinyin | zhuangbei |
+----+------+--------------+-------------+-----------+
|  1 | fs   | 法师         | fashi       | 布甲      |
|  2 | ms   | 牧师         | mushi       | 布甲      |
|  3 | ss   | 术士         | shushi      | 布甲      |
|  4 | dz   | 盗贼         | daozei      | 皮甲      |
|  5 | ws   | 武僧         | wuseng      | 皮甲      |
|  6 | xd   | 德鲁伊       | xiaode      | 皮甲      |
|  7 | sq   | 圣骑士       | shengqi     | 板甲      |
|  8 | zs   | 战士         | zhanshi     | 板甲      |
|  9 | dk   | 死亡骑士     | siwangqishi | 板甲      |
| 10 | dh   | 恶魔猎手     | emolieshou  | 皮甲      |
| 11 | lr   | 龙人         | longren     | 锁甲      |
+----+------+--------------+-------------+-----------+
11 rows in set (0.000 sec)

最后一行:11 | lr | 龙人 | longren | 锁甲 则为新增数据,已经可以查询到

Update修改操作

创建DemoUpdate类:

代码示例:

package com.wangting.bigdata;

import org.junit.Test;

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

public class DemoUpdate {
    
    

    @Test
    public void test2() throws SQLException {
    
    
        // 1.获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        // 2.sql语句
        String sql = "update wow_info set role_pinyin=? where id=?";
        // 3.预编译
        PreparedStatement ps = connection.prepareStatement(sql);
        // 3-1给占位符赋值
        ps.setString(1, "小龙人");
        ps.setInt(2, 11);
        // 3-2执行sql语句
        ps.executeUpdate();
        // 4.关闭资源
        JDBCUtils.close(connection, ps);
    }
}

验证:

MariaDB [wow]> select * from wow_info where id='11';
+----+------+---------+-------------+-----------+
| id | role | role_cn | role_pinyin | zhuangbei |
+----+------+---------+-------------+-----------+
| 11 | lr   | 龙人    | 小龙人      | 锁甲      |
+----+------+---------+-------------+-----------+
1 row in set (0.000 sec)

role_pinyin列的值已经从“longren”被改为“小龙人”。

Delete删除操作

创建DemoDelete类:

代码示例:

package com.wangting.bigdata;

import org.junit.Test;

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

public class DemoDelete {
    
    
    @Test
    public void test3() throws SQLException {
    
    
        // 1.获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        // 2.sql语句
        String sql = "delete from wow_info where id='11'";
        // 3.预编译
        PreparedStatement ps = connection.prepareStatement(sql);
        // 3-1.执行sql语句
        ps.executeUpdate();
        // 4.关闭资源
        JDBCUtils.close(connection, ps);
    }
}

验证:

MariaDB [wow]> select * from wow_info where id='11';
Empty set (0.000 sec)

Select查询操作

DemoSelect01:尝试查询某条数据

进行条件查询,找出id=xxx的某条数据

代码示例:

package com.wangting.bigdata;

import org.junit.Test;

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

public class DemoSelect01 {
    
    
    @Test
    public void test() throws SQLException {
    
    
        // 1.获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        // 2.sql语句
        String sql = "select id,role_cn,zhuangbei from wow_info where id=?";
        // 3.预编译
        PreparedStatement ps = connection.prepareStatement(sql);
        // 4.给占位符赋值
        ps.setInt(1, 3);
        // 5.执行sql语句
        // ResultSet :用来遍历查询的结果
        ResultSet rs = ps.executeQuery();
        // 6.通过ResultSet遍历数据
        while (rs.next()) {
    
    
            // 7.获取对应的字段中的数据
            int id = rs.getInt("id");
            String role_cn = rs.getString("role_cn");
            String zhuangbei = rs.getString("zhuangbei");
            System.out.println(id + " | " + role_cn + " | " + zhuangbei);
        }
        // 7.关闭资源
        JDBCUtils.close(connection, ps, rs);
    }
}

输出验证:

3 | 术士 | 布甲

DemoSelect02:满足条件的多条数据

进行条件查询,查询满足条件的多条数据

代码示例:

package com.wangting.bigdata;

import org.junit.Test;

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

public class DemoSelect02 {
    
    
    @Test
    public void test2() throws SQLException {
    
    
        // 1.获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        // 2.sql语句
        String sql = "select id,role_cn,zhuangbei from wow_info limit 5";
        // 3.预编译
        PreparedStatement ps = connection.prepareStatement(sql);
        // 4.执行sql语句
        // ResultSet :用来遍历查询的结果
        ResultSet rs = ps.executeQuery();
        // 6.通过ResultSet遍历数据
        while (rs.next()) {
    
    //next() :如果有数据结果为true
            // 7.获取对应的字段中的数据
            int id = rs.getInt("id");
            String role_cn = rs.getString("role_cn");
            String zhuangbei = rs.getString("zhuangbei");
            System.out.println(id + " | " + role_cn + " | " + zhuangbei);
        }
        // 7.关闭资源
        JDBCUtils.close(connection, ps, rs);
    }
}

输出验证:

1 | 法师 | 布甲
2 | 牧师 | 布甲
3 | 术士 | 布甲
4 | 盗贼 | 皮甲
5 | 武僧 | 皮甲

DemoSelect03:获取表中所有的数据

通过调用方法获取表中所有的数据

package com.wangting.bigdata;

import org.junit.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DemoSelect03 {
    
    
    public DemoSelect03() {
    
    

    }

    @Test
    public void test3() throws SQLException {
    
    
        List<Wow> Wows = getWows();
        for (Wow Wow : Wows) {
    
    
            System.out.println(Wow);
        }
    }

    public List<Wow> getWows() throws SQLException {
    
    
        // 创建一个集合用来存放对象
        List<Wow> list = new ArrayList<Wow>();

        Connection connection = JDBCUtils.getConnection();
        String sql = "select * from wow_info";
        PreparedStatement ps = connection.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
    
    
            int id = rs.getInt("id");
            String role = rs.getString("role");
            String role_cn = rs.getString("role_cn");
            String role_pinyin = rs.getString("role_pinyin");
            String zhuangbei = rs.getString("zhuangbei");
            // 封装
            Wow s = new Wow(id, role, role_cn, role_pinyin, zhuangbei);

            // 将对象放入到集合中
            list.add(s);
        }
        JDBCUtils.close(connection, ps, rs);
        // 返回集合
        return list;
    }
}

输出验证:

1|fs|法师|fashi|布甲
2|ms|牧师|mushi|布甲
3|ss|术士|shushi|布甲
4|dz|盗贼|daozei|皮甲
5|ws|武僧|wuseng|皮甲
6|xd|德鲁伊|xiaode|皮甲
7|sq|圣骑士|shengqi|板甲
8|zs|战士|zhanshi|板甲
9|dk|死亡骑士|siwangqishi|板甲
10|dh|恶魔猎手|emolieshou|皮甲

JDBC事务

事务基本介绍

数据库事务:

​ 事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

JDBC 事务:

  • 当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚
  • 为了让多个 SQL 语句作为一个事务执行
    • 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
    • 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
    • 在出现异常时,调用 rollback(); 方法回滚事务

实现流程:

  1. 获取数据库连接

    • conn = JDBCUtils.getConnection();
  2. 开启事务

    • conn.setAutoCommit(false);
  3. 进行数据库操作

    xxx

  4. 若没有异常,则提交事务

    • conn.commit();
  5. 若有异常,则回滚事务

准备实验环境

在wow库中新建一张表 game_gold,插入样例数据

CREATE TABLE game_gold(
NAME VARCHAR(20) comment "游戏角色",
gold INT comment "游戏金币"
);

insert into game_gold (NAME,gold) values ("player01",3000);
insert into game_gold (NAME,gold) values ("player02",1000);


MariaDB [wow]> select * from game_gold;
+----------+------+
| NAME     | gold |
+----------+------+
| player01 | 3000 |
| player02 | 1000 |
+----------+------+
2 rows in set (0.000 sec)

需求:玩家1对玩家2交易金币800,玩家1扣除金币和玩家2获取到金币必须都完成,数据最终才准确

创建GameGold类,进行事务功能测试

代码如下:

package com.wangting.bigdata;


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

public class GameGold {
    
    
    public static void main(String[] args) throws SQLException {
    
    
        // 获取Connection对象
        Connection connection = JDBCUtils.getConnection();
        PreparedStatement ps = null;
        try {
    
    
            // 开启事务(禁止自动提交)
            connection.setAutoCommit(false);

            // sql语句
            String sql = "update game_gold set gold=? where name=?";
            // 预编译
            ps = connection.prepareStatement(sql);
            // 给占位符赋值 player01减少800金币
            ps.setInt(1, 2200);
            ps.setString(2, "player01");
            ps.executeUpdate();

            // 刻意添加报错代码,看是否会出现play01正确的语句成功执行到MySQL
            System.out.println(1 / 0);

            // player02获取增加800金币
            ps.setInt(1, 1800);
            ps.setString(2, "player02");
            ps.executeUpdate();

            // 事务提交
            connection.commit();
        } catch (Exception e) {
    
    
            // 事务回滚
            connection.rollback();
            // 打印异常信息
            e.printStackTrace();
        } finally {
    
    
            // 允许自动提交
            connection.setAutoCommit(true);
            // 关闭资源
            JDBCUtils.close(connection, ps);
        }
    }
}

因为有System.out.println(1 / 0);代码的存在,结果预期内的抛异常

java.lang.ArithmeticException: / by zero
at com.wangting.bigdata.GameGold.main(GameGold.java:27)

回到MySQL查询数据:

MariaDB [wow]> select * from game_gold;
+----------+------+
| NAME     | gold |
+----------+------+
| player01 | 3000 |
| player02 | 1000 |
+----------+------+

数据并没有发生一半执行一半没执行的情况,说明触发了事务回滚

现在将System.out.println(1 / 0);代码注释掉,再次执行GameGold类main方法

验证最终结果:

MariaDB [wow]> select * from game_gold;
+----------+------+
| NAME     | gold |
+----------+------+
| player01 | 2200 |
| player02 | 1800 |
+----------+------+

总结:

使用 ROLLBACK 语句可使数据变化失效

  • 数据改变被取消
  • 修改前的数据状态可以被恢复

猜你喜欢

转载自blog.csdn.net/wt334502157/article/details/128655098