[Java learning summary] MySQL and JDBC

1. MySQL overview

1. What is MySQL

Concept: It is a popular open source and free relational database
History: Developed by the Swedish MySQL AB company, it is currently a product of Oracle
Features:

  • free, open source database
  • compact, full-featured
  • Easy to use
  • Can run on Windows or Linux operating system
  • Applicable to small, medium and even large-scale website applications

2. MySQL installation

  1. Download the compressed package of MySQL5.7 from the official website. Official website: https://www.mysql.com/
  2. Unzip the installation package to the installation directory.
  3. Configure environment variables.
    Configure environment variables

After creating the system variables, add %MYSQL_HOME%\bin to the path.
4. Edit the my.ini file, pay attention to replace the path location.

[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. The administrator starts CMD, switches the path to the bin directory under mysql, and then enters mysqld –install to install mysql.
mysqld –instal
  1. Then enter mysqld --initialize-insecure --user=mysql to initialize the data file.
mysqld --initialize-insecure --user=mysql
  1. Then start mysql again and use the command mysql –u root –p to enter the mysql management interface (the password is empty).
  2. Change the root password with the following SQL statement.
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
  1. Refresh permissions
flush privileges; 
  1. Modify the my.ini file to delete the last sentence skip-grant-tables. Restore login password verification.
  2. Restart mysql to use normally
net stop mysql
net start mysql
  1. Log in to MySQL using the root user name and the configured password.
mysql -uroot -p123456

3.MySQL data type

(1) Numerical type

type illustrate Ranges Bytes
tinyint very small integer Signed value: - 2 7 2^{7}27~ 2 7 2^{7} 27 -1
unsigned value: 0~2 8 2^{8}28-1
1
smallint smaller integer Signed value: - 2 15 2^{15}215~ 2 15 2^{15} 215 -1
unsigned value: 0~2 16 2^{16}216-1
2
mediumint medium-sized integer Signed value: - 2 23 2^{23}223~ 2 23 2^{23} 223 -1
unsigned value: 0~2 24 2^{24}224-1
3
int standard integer Signed value: - 2 31 2^{31}231~ 2 31 2^{31} 231 -1
unsigned value: 0~2 32 2^{32}232-1
4
bigint larger integer Signed value: - 2 63 2^{63}263~ 2 63 2^{63} 263 -1
unsigned value: 0~2 64 2^{64}264-1
8
float single precision floating point Signed value: -3.402823466E+38~-1.175494351E-38
Unsigned value: 0 and 1.175494351E-38~3.402823466E+38
4
double double precision floating point Signed value: -1.7976931348623157E+308~2.2250738585072014E-308
Unsigned value: 0 and 2.2250738585072014E-308~1.7976931348623157E+308
8
decimal float as a string decimal(m,d) m

(2) String type

type illustrate The maximum length
char(M) Fixed-length string, fast retrieval but space-consuming
0<=M<=255
M character
varchar(M) Variable string
0<=M<=65535
variable length
tinytext micro text string 0~ 2 8 2^{8} 28-1 bytes
text text string 0~ 2 16 2^{16} 216 -1 bytes

(3) date type

type illustrate Ranges
DATE YYYY-MM-DD, date format 1000-01-01 to 9999-12-31
TIME hh:mm:ss, time format -838:59:59 to 838:59:59
DATETIME YYYY-MM-DD hh:mm:ss 1000-01-01 00:00:00 to 9999-12-31 23:59:59
TIMESTAMP Timestamp in YYYYMMDDhhmmss format 197010101000000 to sometime in 2037
YEAR 1901 to 2155

(4) NULL value

  • Interpreted as "no value" or "unknown value"
  • Do not perform arithmetic operations with NULL, the result is still NULL

4. MySQL data field attributes

  1. UnSigned: Unsigned, which declares that the data column cannot be negative.
  2. ZEROFILL: 0 filled, the insufficient number of digits is filled with 0.
  3. Auto_InCrement: auto-increment, every time a piece of data is added, 1 is automatically added to the previous record number.
  4. NULL and NOT NULL: The default is NULL, that is, there is no value inserted into the column; if it is set to NOT NULL, the column must have a value.
  5. DEFAULT: default, used to set the default value.

2. Operation of database and data table

1. Basic operation of the database

  • create database
create database [if not exists] 数据库名;
  • delete database
drop database [if exists] 数据库名;
  • view database
show databases;
  • use database
use 数据库名;

2. Basic operation of data table

(1) Create a table

It is a kind of DDL, syntax:

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

Create a student table example:

 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) Amendment table

  • modify table name
ALTER TABLE 旧表名 RENAME AS 新表名
  • add field
ALTER TABLE 表名 ADD字段名 列属性[属性]
  • modify field
ALTER TABLE 表名 MODIFY 字段名 列类型[属性]
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性]
  • delete field
ALTER TABLE 表名 DROP 字段名

(3) Delete table

DROP TABLE [IF EXISTS] 表名

3. Data management

1. Foreign keys

  1. Concept:
    If a common key is the primary key in one relation, then this common key is called a foreign key to the other relation. It can be seen that a foreign key represents a related link between two relationships. A table with a foreign key of another relationship as its primary key is called a primary table, and a table with a foreign key is called a secondary table of the primary table.
    In practice, the value of one table is put into the second table to represent the association, and the value used is the primary key value of the first table (including composite primary key values ​​if necessary). At this point, the attribute in the second table that holds these values ​​is called a foreign key. Foreign keys can link two tables.
  2. Create a foreign key
    (1) The first way to create a foreign key: create a child table and create a foreign key at the same time
-- 年级表 (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) Create foreign key method 2: After creating the subtable, modify the subtable to add the foreign key

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

2. Add data

grammar:

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

3. Modify data

grammar:

UPDATE 表名 SET column_name=value [,column_name2=value2,...] [WHERE condition];
  • column_name is the data column to be changed
  • value is the modified data, which can be a variable, specifically, an expression or a nested SELECT result
  • condition is the filter condition, if not specified, all column data of the table will be modified

where conditional clause: conditionally filter data from the table

operator meaning example result
= equal 5=6 false
<> or != not equal to 5!=6 true
> more than the 5>6 false
< less than 5<6 true
>= greater or equal to 5>=6 false
<= less than or equal to 5<=6 true
BETWEEN between a certain range BETWEEN 5 AND 10
AND 并且 5>1 AND 1>2 false
OR 5>1 OR 1>2 true

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 NULL a IS NULL 若操作符为NULL,则结果为真
IS NOT NULL a IS NOT NULL 若操作符不为NULL,则结果为真
BETWEEN a BETWEEN b AND c 若 a 范围在 b 与 c 之间,则结果为真
LIKE a LIKE b SQL 模式匹配,若a匹配b,则结果为真
IN a 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 即使左表中没有匹配,也从右表中返回所有的行

insert image description here

  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加载具体的驱动,就可以操作数据库。
insert image description here

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`='[email protected]' 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 is a subclass of Statement, and its instance object can be obtained by calling the Connection.preparedStatement() method. Compared with the Statement object: PreperedStatement can avoid the problem of SQL injection.
Statement will cause the database to compile SQL frequently, which may cause database buffer overflow. PreparedStatement can precompile SQL to improve the execution efficiency of the database. And PreperedStatement allows the use of placeholders to replace the parameters in SQL, which simplifies the writing of SQL statements.

An example of using the PreperedStatemen object to solve the injection problem is as follows:

    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. Affairs

Realize the test of the transaction in JDBC, so as to deepen the understanding of the transaction.
The accounts of the three ABCs each have 1,000 yuan, and now it is simulated that A transfers 100 yuan to B. First, you need to deduct 100 yuan from A's account, and then add 100 yuan to B's account.
If all executions are successful, the transaction is committed, and the two SQL statements take effect. If an error occurs in one of the links, the transaction will be rolled back, and all SQL statements will not take effect.
(1) Create a new account table:

id NAME money
1 A 800
2 B 1200
3 C 1000

(2) Execute JDBC code.

    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);
        }
    }

The code is successfully executed, A is reduced by 100 yuan, and B is increased by 100 yuan.
insert image description here
(3) Now simulate an error in the code, execute a code that will cause an error after deducting 100 yuan from A's account, and see if A will actually deduct 100 yuan.

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);
        }
    }
}

The execution results are as follows, and the program execution fails.
insert image description here
insert image description here
Neither the accounts of A nor B in the database have changed, indicating that the transaction rollback is successful.

Guess you like

Origin blog.csdn.net/qq_43647936/article/details/121097700