JavaWeb学习之旅-JDBC

189 阅读4分钟

JavaWeb-JDBC

一、什么是JDBC

JDBC:Java Database Connect, Java数据库连接。像驱动一样,使Java程序可以连接多个不同品牌的数据库。

image-20210526150330070.png

二、如何使用JDBC

1.准备工作

创建一个名为JDBC的数据库,创建一个用户表,表里的字段分别为id,name,password,email, birthday

# 创建数据库
create database jdbc;
# 创建表
create table users
(
    `id`       int primary key,
    `name`     varchar(40) comment '姓名',
    `password` varchar(40) comment '密码',
    `email`    varchar(40) comment '邮箱',
    `birthday` varchar(40) comment '出生日期'
);
# 添加内容
insert into users
values (1, '张三', '123456', 'zs@163.com', '2001-01-01'),
       (2, '李四', '123456', 'ls@163.com', '2021-05-26'),
       (3, '王五', '123456', 'ww@163.com', '2015-05-05');
# 查询表
select * from users;

image-20210526210525110.png

2.使用JDBC

1.导入jar包

需要导入 mysql-connector-java,连接数据库的驱动。

<dependencies>
    <dependency>
        <!-- mysql驱动 -->
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
</dependencies>
2.IDEA中连接数据库

image-20210526211518454.png

注意:如果右侧没有database选项卡,可在顶部标题栏 View->Tool Windows->Database 开启。

3.配置信息
// 解决中文乱码 
//jdbc:mysql://localhost:3306/数据库名?参数1&参数2
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "123456";
4.连接数据库
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接数据库
Connection connection = DriverManager.getConnection(url, username, password);
5.执行语句获取结果

方法1:

// 通过statement执行sql语句
Statement statement = connection.createStatement();
// 查询语句
String query = "select * from users;";
// 执行查询语句,返回结果集
ResultSet resultSet = statement.executeQuery(query);
// 增删改都是调用executeUpdate()方法
// int row = statement.executeUpdate(sql)
// 获取内容
while (resultSet.next()) {
    System.out.println("=======分割线========");
    System.out.printf(Locale.getDefault(), "id=%s%n", resultSet.getObject("id"));
    System.out.printf(Locale.getDefault(), "name=%s%n", resultSet.getObject("name"));
    System.out.printf(Locale.getDefault(), "password=%s%n", resultSet.getObject("password"));
    System.out.printf(Locale.getDefault(), "email=%s%n", resultSet.getObject("email"));
    System.out.printf(Locale.getDefault(), "birthday=%s%n", resultSet.getObject("birthday"));
}

效果:

image-20210526170555329.png

方法2:预编译

// 插入语句
String insert = "insert into users (id, name, password, email, birthday) values (?, ?, ?, ?, ?);";
// 预先编译插入语句
PreparedStatement preparedStatement = connection.prepareStatement(insert);
// 插入具体值
// parameterIndex是从1开始
preparedStatement.setInt(1, 4);
preparedStatement.setString(2, "梁大侠");
preparedStatement.setString(3, "123456");
preparedStatement.setString(4, "leungmx0206@163.com");
preparedStatement.setDate(5, new Date(System.currentTimeMillis()));
// 执行sql
int row = preparedStatement.executeUpdate();
if (row > 0) {
    System.out.println("添加数据成功!");
} else {
    System.out.println("添加数据失败!");
}

效果:

image-20210526214316794.png

6.关闭数据库,释放资源
// 关闭连接,释放资源(先开后关)
// 方法1 关闭
// resultSet.close();
// statement.close();
//方法2 关闭
preparedStatement.close();
connection.close();

三、事务

1. 什么是事务

将一条sql或一组sql放进一个批次里去执行。

2. 事务原则 ACID原则

ACID原则:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)*、持久性(Durability)

2.1 原子性

要么都成功,要么都失败!

2.2 一致性

事务前后的数据要保持完整性。

2.3 隔离性

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

2.4 持久性

事务一旦提交则不可逆,被持久化到数据库中。

2.5 隔离所导致的一些问题(脏读,幻读...)

脏读:指一个事务读取了另外一个事务未提交的数据。

虚读:是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。(一般是行影响,如下图所示:多了一行)

不可重复性:在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

3. 测试事务

3.1 准备工作

创建数据

create table account
(
    `id`    int primary key auto_increment,
    `name`  varchar(30),
    `money` decimal(9, 2)
);

insert into account(`name`, `money`)
values ('A', 1000),
       ('B', 1000),
       ('C', 1000);

3.2 实验

模拟A用户给B用户转账100

@Test
public void testTransaction() throws ClassNotFoundException {
    String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "123456";
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = null;
    try {
        connection = DriverManager.getConnection(url, username, password);
        // 关闭自动提交
        connection.setAutoCommit(false);
        // 模拟转账
        String transfer = "update account set money = money - 100 where name = 'A';";
        connection.prepareStatement(transfer).executeUpdate();
        // 模拟错误
		int i = 1 / 0;
        // 模拟转账
        String update = "update account set money = money + 100 where name = 'B';";
        connection.prepareStatement(update).executeUpdate();
        // 提交
        connection.commit();
        System.out.println("转账成功!");
    } catch (Exception e) {
        try {
            assert connection != null;
            connection.rollback();
            System.out.println("回滚成功!");
        } catch (SQLException throwables) {
        	throwables.printStackTrace();
        }
        System.out.println("转账失败!");
    }
    try {
        connection.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

模拟错误发生,事务回滚:

image-20210528155705530.png

image-20210528155839605.png

发生错误后,会恢复回原来的数据。如果不写回滚,默认也是会回滚的。

去除错误,正常提交:

image-20210528160018679.png

image-20210528160034385.png

未关闭自动提交,模拟错误运行结果:

...
connection = DriverManager.getConnection(url, username, password);
// 关闭自动提交
// connection.setAutoCommit(false);
// 模拟转账
String transfer = "update account set money = money - 100 where name = 'A';";
connection.prepareStatement(transfer).executeUpdate();
// 模拟错误
int i = 1 / 0;
// 模拟转账
String update = "update account set money = money + 100 where name = 'B';";
connection.prepareStatement(update).executeUpdate();
...

image-20210528160508482.png

image-20210528160521726.png

没有关闭自动提交,也会执行sql语句。

这里参考了以下资料:

【狂神说Java】JavaWeb入门到实战(p28-p29)

【狂神说Java】MySQL最新教程通俗易懂(p28-p29)

事务ACID理解