【Java学习总结】MySQL与JDBC

一、MySQL概述

1.什么是MySQL

概念 : 是现在流行的开源的,免费的关系型数据库
历史 : 由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品
特点 :

  • 免费 , 开源数据库
  • 小巧 , 功能齐全
  • 使用便捷
  • 可运行于Windows或Linux操作系统
  • 可适用于中小型甚至大型网站应用

2.MySQL的安装

  1. 在官网下载MySQL5.7的压缩包。官网:https://www.mysql.com/
  2. 将安装包解压到安装目录。
  3. 配置环境变量。
    配置环境变量

新建完系统变量后,在path中添加%MYSQL_HOME%\bin。
4. 编辑 my.ini 文件 ,注意替换路径位置。

[mysqld]
#端口号
port = 3306
#数据库的安装路径
basedir=D:\Program Files\mysql-5.7.26
#数据库的安装路径+\data(数据库存储路径)
datadir=D:\Program Files\mysql-5.7.26\data 
#最大连接数
max_connections=200
#编码
character-set-server=utf8
default-storage-engine=INNODB
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
#使MySQL跳过密码验证。
skip-grant-tables
  1. 管理员启动CMD,并将路径切换至mysql下的bin目录,然后输入mysqld –install来安装mysql。
mysqld –instal
  1. 再输入 mysqld --initialize-insecure --user=mysql 初始化数据文件。
mysqld --initialize-insecure --user=mysql
  1. 然后再次启动mysql 然后用命令 mysql –u root –p 进入mysql管理界面(密码为空)。
  2. 通过以下SQL语句更改root密码。
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
  1. 刷新权限
flush privileges; 
  1. 修改 my.ini文件删除最后一句skip-grant-tables 。恢复登录密码验证。
  2. 重启mysql即可正常使用
net stop mysql
net start mysql
  1. 使用root用户名和设置的密码登录MySQL。
mysql -uroot -p123456

3.MySQL的数据类型

(1) 数值类型

类型说明取值范围字节数
tinyint非常小的整数有符号值:- 2 7 2^{7} 27~ 2 7 2^{7} 27-1
无符号值:0~ 2 8 2^{8} 28-1
1
smallint较小的整数有符号值:- 2 15 2^{15} 215~ 2 15 2^{15} 215-1
无符号值:0~ 2 16 2^{16} 216-1
2
mediumint中等大小的整数有符号值:- 2 23 2^{23} 223~ 2 23 2^{23} 223-1
无符号值:0~ 2 24 2^{24} 224-1
3
int标准整数有符号值:- 2 31 2^{31} 231~ 2 31 2^{31} 231-1
无符号值:0~ 2 32 2^{32} 232-1
4
bigint较大的整数有符号值:- 2 63 2^{63} 263~ 2 63 2^{63} 263-1
无符号值:0~ 2 64 2^{64} 264-1
8
float单精度浮点数有符号值:-3.402823466E+38~-1.175494351E-38
无符号值:0和1.175494351E-38~3.402823466E+38
4
double双精度浮点数有符号值: -1.7976931348623157E+308~2.2250738585072014E-308
无符号值:0和2.2250738585072014E-308~1.7976931348623157E+308
8
decimal字符串形式的浮点数decimal(m,d)m

(2) 字符串类型

类型说明最大长度
char(M)固定长度字符串,检索快但费空间
0<=M<=255
M字符
varchar(M)可变字符串
0<=M<=65535
可变长度
tinytext微型文本串0~ 2 8 2^{8} 28-1字节
text文本串0~ 2 16 2^{16} 216-1字节

(3) 日期类型

类型说明取值范围
DATEYYYY-MM-DD,日期格式1000-01-01至9999-12-31
TIMEhh:mm:ss,时间格式-838:59:59至838:59:59
DATETIMEYYYY-MM-DD hh:mm:ss1000-01-01 00:00:00至9999-12-31 23:59:59
TIMESTAMPYYYYMMDDhhmmss格式表示的时间戳197010101000000至2037年的某个时刻
YEAR1901至2155

(4)NULL值

  • 理解为“没有值”或“未知值”
  • 不要用NULL进行算术运算,结果仍为NULL

4.MySQL的数据字段属性

  1. UnSigned:无符号的,声明该数据列不能为负数。
  2. ZEROFILL:0填充的,不足的位数用0来填充。
  3. Auto_InCrement:自增,每添加一条数据,自动在上一个记录数上加1。
  4. NULL和NOT NULL:默认为NULL,即没有插入该列的数值;如果设置为NOT NULL,则该列必需有值。
  5. DEFAULT:默认的,用于设置默认值。

二、数据库与数据表的操作

1.数据库的基本操作

  • 创建数据库
create database [if not exists] 数据库名;
  • 删除数据库
drop database [if exists] 数据库名;
  • 查看数据库
show databases;
  • 使用数据库
use 数据库名;

2.数据表的基本操作

(1)创建表

属于DDL的一种,语法:

create table [if not exists] `表名`(
  '字段名1' 列类型 [属性][索引][注释],
  '字段名2' 列类型 [属性][索引][注释],
 #...
  '字段名n' 列类型 [属性][索引][注释]
)[表类型][表字符集][注释];

创建一个学生表示例:

 create table student(
 id int not null comment auto_increment '学号',
 name varchar(20) default null comment '学生姓名',
 gender default null comment '性别',
 birthday date,
 PRIMARY KEY (`id`)
 )ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='学生表'
(2)修改表
  • 修改表名
ALTER TABLE 旧表名 RENAME AS 新表名
  • 添加字段
ALTER TABLE 表名 ADD字段名 列属性[属性]
  • 修改字段
ALTER TABLE 表名 MODIFY 字段名 列类型[属性]
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性]
  • 删除字段
ALTER TABLE 表名 DROP 字段名
(3)删除表
DROP TABLE [IF EXISTS] 表名

三、数据管理

1.外键

  1. 概念:
    如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
    在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。外键可以使两张表形成关联。
  2. 创建外键
    (1)创建外键的方式一 : 创建子表同时创建外键
-- 年级表 (id\年级名称)
CREATE TABLE `grade` (
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级ID',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
-- 学生信息表 (学号,姓名,性别,年级,手机,地址,出生日期,邮箱,身份证号)
CREATE TABLE `student` (
`studentno` INT(4) NOT NULL COMMENT '学号',
`studentname` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`gradeid` INT(10) DEFAULT NULL COMMENT '年级',
PRIMARY KEY (`studentno`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`
(`gradeid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

(2)创建外键方式二 : 创建子表完毕后,修改子表添加外键

ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`
(`gradeid`);
  1. 删除外键
ALTER TABLE student DROP FOREIGN KEY FK_gradeid;
-- 发现执行完上面的,索引还在,所以还要删除索引
-- 注:这个索引是建立外键的时候默认生成的
ALTER TABLE student DROP INDEX FK_gradeid;

2.添加数据

语法:

INSERT INTO 表名[(字段1,字段2,字段3,...)] VALUES('值1','值2','值3') 

3.修改数据

语法:

UPDATE 表名 SET column_name=value [,column_name2=value2,...] [WHERE condition];
  • column_name 为要更改的数据列
  • value 为修改后的数据 , 可以为变量 , 具体指 , 表达式或者嵌套的SELECT结果
  • condition 为筛选条件 , 如不指定则修改该表的所有列数据

where条件子句:有条件地从表中筛选数据

运算符含义举例结果
=等于5=6false
<>或!=不等于5!=6true
>大于5>6false
<小于5<6true
>=大于等于5>=6false
<=小于等于5<=6true
BETWEEN在某个范围之间BETWEEN 5 AND 10
AND并且5>1 AND 1>2false
OR5>1 OR 1>2true

4.删除数据

语法:

DELETE FROM 表名 [WHERE condition]; 
TRUNCATE [TABLE] table_name;
-- 清空年级表
TRUNCATE grade

TRUNCATE与DELETE的不同之处:
(1)使用TRUNCATE TABLE 重新设置AUTO_INCREMENT计数器。
(2)使用TRUNCATE TABLE不会对事务有影响

5.查询数据

(1)SELECT语法
SELECT [ALL | DISTINCT]
{* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
FROM table_name [as table_alias]
 [left | right | inner join table_name2]  -- 联合查询
 [WHERE ...]  -- 指定结果需满足的条件
 [GROUP BY ...]  -- 指定结果按照哪几个字段来分组
 [HAVING]  -- 过滤分组的记录必须满足的次要条件
 [ORDER BY ...]  -- 指定查询记录按一个或多个条件排序
 [LIMIT {[offset,]row_count | row_countOFFSET offset}];
  -- 指定查询的记录从哪条至哪条
(2)AS子句

作用:

可给数据列取一个新别名
可给表取一个新别名
可把经计算或总结的结果用另一个新名称来代替

-- 使用as可以为列和表取别名
SELECT studentno AS 学号,studentname AS 姓名 FROM student AS s;
(3)where条件语句

作用:为SELECT语句提供检索条件。
检索条件一般由一个或多个逻辑表达式组成,结果为真或假。

如:

SELECT * FROM student where id = 1;
(4)模糊查询:
操作符名称语法描述
IS NULLa IS NULL若操作符为NULL,则结果为真
IS NOT NULLa IS NOT NULL若操作符不为NULL,则结果为真
BETWEENa BETWEEN b AND c若 a 范围在 b 与 c 之间,则结果为真
LIKEa LIKE b SQL模式匹配,若a匹配b,则结果为真
INa IN (a1,a2,a3,…)若 a 等于 a1,a2… 中的某一个,则结果为真
-- 查询姓刘的同学的学号及姓名
-- like结合使用的通配符 : % (代表0到任意个字符) _ (一个字符)
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘%';

-- 查询学号为1000,1001,1002的学生姓名
SELECT studentno,studentname FROM student
WHERE studentno IN (1000,1001,1002)

-- 查询出生日期没有填写的同学
-- 不能直接写=NULL , 这是代表错误的 , 用 is null
SELECT studentname FROM student
WHERE BornDate IS NULL;

-- 查询出生日期填写的同学
SELECT studentname FROM student
WHERE BornDate IS NOT NULL;
(5)连接查询
操作符名称描述
INNER JOIN如果表中有至少一个匹配,则返回行
LEFT JOIN即使右表中没有匹配,也从左表中返回所有的行
RIGHT JOIN即使左表中没有匹配,也从右表中返回所有的行

在这里插入图片描述

  1. 内连接:
    是一种最常用的连接类型。内连接查询实际上是一种任意条件的查询。使用内连接时,如果两个表的相关字段满足连接条件,就从这两个表中提取数据并组合成新的记录,也就是在内连接查询中,只有满足条件的元组才能出现在结果关系中。
  2. 外连接:
    内连接的查询结果都是满足连接条件的元组。但有时我们也希望输出那些不满足连接条件的元组信息。比如,我们想知道每个学生的选课情况,包括已经选课的学生(这部分学生的学号在学生表中有,在选课表中也有,是满足连接条件的),也包括没有选课的学生(这部分学生的学号在学生表中有,但在选课表中没有,不满足连接条件),这时就需要使用外连接。外连接是只限制一张表中的数据必须满足连接条件,而另一张表中的数据可以不满足连接条件的连接方式。
    (1)左外连接:以左表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充。
    (2)右外连接:以右表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充。
(6)子查询

什么是子查询?

  • 在查询语句中的WHERE条件子句中,又嵌套了另一个查询语句
  • 嵌套查询可由多个子查询组成,求解的方式是由里及外;
  • 子查询返回的结果一般都是集合,故而建议使用IN关键字;

6.事务

(1)什么是事务:
事务就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行。
(2)事务的ACID特性:

  • 原子性(Atomic)–所有操作要么全部正确反应,要么全部不反应。
  • 一致性(Consist)–事务的执行不会让数据库出现不一致。
  • 隔离性(Isolated)–事务之间是隔离的,每个事务都感觉不到系统中其他事务在并发的进行。
  • 持久性(Durable)–事务完成后对数据库的改变是永久的。

(3)基本语法

-- 使用set语句来改变自动提交模式
SET autocommit = 0;  /*关闭*/
SET autocommit = 1;  /*开启*/
-- 注意:
--- 1.MySQL中默认是自动提交
--- 2.使用事务时应先关闭自动提交
-- 开始一个事务,标记事务的起始点
START TRANSACTION 

-- 提交一个事务给数据库
COMMIT
-- 将事务回滚,数据回到本次事务的初始状态
ROLLBACK
-- 还原MySQL数据库的自动提交
SET autocommit =1;
-- 保存点
SAVEPOINT 保存点名称 -- 设置一个事务保存点
ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名称 -- 删除保存点

7.MySQL函数

-- 数值函数
abs(x)       -- 绝对值 abs(-10.9) = 10
format(x, d)   -- 格式化千分位数值 format(1234567.456, 2) = 1,234,567.46
ceil(x)       -- 向上取整 ceil(10.1) = 11
floor(x)     -- 向下取整 floor (10.1) = 10
round(x)     -- 四舍五入去整
mod(m, n)     -- m%n m mod n 求余 10%3=1
pi()       -- 获得圆周率
pow(m, n)     -- m^n
sqrt(x)       -- 算术平方根
rand()       -- 随机数
truncate(x, d)   -- 截取d位小数

-- 时间日期函数
now(), current_timestamp();   -- 当前日期时间
current_date();           -- 当前日期
current_time();           -- 当前时间
date('yyyy-mm-dd hh:ii:ss');   -- 获取日期部分
time('yyyy-mm-dd hh:ii:ss');   -- 获取时间部分
date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j');   -- 格式化时间
unix_timestamp();         -- 获得unix时间戳
from_unixtime();         -- 从时间戳获得时间

-- 字符串函数
length(string)       -- string长度,字节
char_length(string)     -- string的字符个数
substring(str, position [,length])     -- 从str的position开始,取length个字符
replace(str ,search_str ,replace_str)   -- 在str中用replace_str替换search_str
instr(string ,substring)   -- 返回substring首次在string中出现的位置
concat(string [,...])   -- 连接字串
charset(str)       -- 返回字串字符集
lcase(string)       -- 转换成小写
left(string, length)   -- 从string2中的左边起取length个字符
load_file(file_name)   -- 从文件读取内容
locate(substring, string [,start_position])   -- 同instr,但可指定开始位置
lpad(string, length, pad)   -- 重复用pad加在string开头,直到字串长度为length
ltrim(string)       -- 去除前端空格
repeat(string, count)   -- 重复count次
rpad(string, length, pad)   --在str后用pad补充,直到长度为length
rtrim(string)       -- 去除后端空格
strcmp(string1 ,string2)   -- 逐字符比较两字串大小

-- 聚合函数
count()
sum();
max();
min();
avg();
group_concat()

-- 其他常用函数
md5();
default()

四、JDBC

1.JDBC介绍

SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范(接口),称之为JDBC(Java Data Base Connectivity)。这套接口由数据库厂商去实现,这样,开发人员只需要学习jdbc接口,并通过jdbc加载具体的驱动,就可以操作数据库。
在这里插入图片描述

2.JDBC程序:

一个基本的JDBC程序如下所示:

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver"); //固定写法
        //2.用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";
        //3.连接成功,数据库对象  Connection代表数据库
        Connection connection = DriverManager.getConnection(url,username,password);
        //4.执行SQL的对象    statement代表执行sql的对象
        Statement statement = connection.createStatement();
        //5.执行SQL的对象去执行SQL,可能存在结果,查看返回结果
        String sql = "SELECT * FROM users";
        ResultSet resultSet = statement.executeQuery(sql);  //返回的结果集,查询到的所有结果都存在这里
        while (resultSet.next()){
            System.out.println("id="+resultSet.getObject("id"));
            System.out.println("name="+resultSet.getObject("NAME"));
            System.out.println("password="+resultSet.getObject("PASSWORD"));
            System.out.println("email="+resultSet.getObject("email"));
            System.out.println("birthday="+resultSet.getObject("birthday"));
            System.out.println("===================================");
        }
        //6.释放链接
        resultSet.close();
        statement.close();
        connection.close();
    }

3.使用jdbc对数据库增删改查

在JDBC操作中,我们可以将数据库的驱动等信息存入一个配置文件中,并创建一个util工具类,存入一些方法,以后每次对数据库操作调入该方法即可。这样可以使我们的代码更加简洁。具体步骤如下:
(1)创建db.properties文件。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

(2)新建一个Util工具类。

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static {
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
            //1.驱动只加载一次
            Class.forName(driver);
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }
    //获取链接对象
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }
    //释放资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)对数据进行增删改查操作。

    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();//获取数据库链接
            st = conn.createStatement();//获得SQL的执行对象
            String sql = "UPDATE users SET `NAME`='zhangsansan',`email`='1231414@163.com' WHERE id=1";
            int i = st.executeUpdate(sql);
            if(i > 0){
                System.out.println("更新成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }

4.SQL注入问题

我们编写如下代码,模拟一个登录操作。

    public static void main(String[] args) {
        //login("lisi","123456");//正常登录
        login("'or '1=1","'or '1=1");//技巧
    }
    public static void login(String username,String password){
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            
            String sql = "select * from users where `NAME` ='"+username+"'AND `PASSWORD`='"+password+"'";
            rs = st.executeQuery(sql);//查询完毕会返回一个结果集
            while(rs.next()){
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("PASSWORD"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }

上述代码中,正常输入用户名和密码:lisi 123456即可访问数据库中数据。但是如果用户在输入用户名和密码的位置输入代码中login()的参数:'or '1=1","'or '1=1,也可以实现访问数据库,这就导致了我们的程序不够安全。
login方法传入的这个参数和sql语句拼接后的结果为:
select * from users where NAME = ’ ’ or ‘1=1’ AND PASSWORD= ’ 'or '1=1’。这个语句是可以查询成功的。
即通过巧妙的技巧来拼接字符串,造成SQL短路,从而获取数据库数据。这就是SQL注入问题。
解决办法:

PreperedStatement是Statement的子类,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言:PreperedStatement可以避免SQL注入的问题。
Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。

使用 PreperedStatemen对象解决注入问题实例如下:

    public static void main(String[] args) {
        login("lisi","111111");//正常登录
        //login("'or '1=1","'or '1=1");//技巧无法成功
    }

    public static void login(String username,String password){
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //PreparedStatement防止sql注入的本质,把传递进来的参数当做字符
            //加上其中存在转译字符,比如说'会被直接定义
            String sql = "select * from users where `NAME` = ? AND `PASSWORD`= ?";
            st = conn.prepareStatement(sql);

            st.setString(1,username);
            st.setString(2,password);

            rs = st.executeQuery();//查询完毕会返回一个结果集
            while(rs.next()){
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("PASSWORD"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }

5.事务

在JDBC中实现对事务的测试,从而加深对事务的理解。
ABC三个人的账户各有1000块,现在模拟A给B转账100块钱。首先需要先从A的账户扣除100元,随后再在B的账户中增加100元。
若全部执行成功,事务提交,两条SQL语句生效。如果中间某个环节出现错误,那么事务回滚,所有的SQL语句都不生效。
(1)新建一个account表:

idNAMEmoney
1A800
2B1200
3C1000

(2)执行JDBC代码。

    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs= null;
        try {
            conn= JdbcUtils.getConnection();
            //关闭数据库的自动提交,自动会开启事务
            conn.setAutoCommit(false);//开启事务
            String sql1 = "update account set money = money-100 where name = 'A'";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();
            //int x = 1/0;//报错
            String sql2 = "update account set money = money+100 where name = 'B'";
            st = conn.prepareStatement(sql2);
            st.executeUpdate();
            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功!");
        } catch (SQLException e) {
            try {
                conn.rollback();//如果失败,回滚
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }

代码执行成功,A减少100元,B增加100元。
在这里插入图片描述
(3)现在在代码中模拟植入一个错误,在A的账户扣除100元后执行一个会出错的代码,看看A是否会真的扣除100元。

package com.kuang.lesson04;

import com.kuang.lesson02.utils.JdbcUtils;

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

public class TestTransaction1 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs= null;
        try {
            conn= JdbcUtils.getConnection();
            //关闭数据库的自动提交,自动会开启事务
            conn.setAutoCommit(false);//开启事务
            String sql1 = "update account set money = money-100 where name = 'A'";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();
            int x = 1/0;//报错
            String sql2 = "update account set money = money+100 where name = 'B'";
            st = conn.prepareStatement(sql2);
            st.executeUpdate();
            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功!");
        } catch (SQLException e) {
            try {
                conn.rollback();//如果失败,回滚
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

执行结果如下,程序执行失败。
在这里插入图片描述
在这里插入图片描述
数据库中A和B的账户都没有发生变化,说明事务回滚成功。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helloWorldZMY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值