【Java学习总结】MySQL与JDBC
一、MySQL概述
1.什么是MySQL
概念 : 是现在流行的开源的,免费的关系型数据库
历史 : 由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品
特点 :
- 免费 , 开源数据库
- 小巧 , 功能齐全
- 使用便捷
- 可运行于Windows或Linux操作系统
- 可适用于中小型甚至大型网站应用
2.MySQL的安装
- 在官网下载MySQL5.7的压缩包。官网:https://www.mysql.com/
- 将安装包解压到安装目录。
- 配置环境变量。
新建完系统变量后,在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
- 管理员启动CMD,并将路径切换至mysql下的bin目录,然后输入mysqld –install来安装mysql。
mysqld –instal
- 再输入 mysqld --initialize-insecure --user=mysql 初始化数据文件。
mysqld --initialize-insecure --user=mysql
- 然后再次启动mysql 然后用命令 mysql –u root –p 进入mysql管理界面(密码为空)。
- 通过以下SQL语句更改root密码。
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
- 刷新权限
flush privileges;
- 修改 my.ini文件删除最后一句skip-grant-tables 。恢复登录密码验证。
- 重启mysql即可正常使用
net stop mysql
net start mysql
- 使用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) 日期类型
类型 | 说明 | 取值范围 |
---|---|---|
DATE | YYYY-MM-DD,日期格式 | 1000-01-01至9999-12-31 |
TIME | hh:mm:ss,时间格式 | -838:59:59至838:59:59 |
DATETIME | YYYY-MM-DD hh:mm:ss | 1000-01-01 00:00:00至9999-12-31 23:59:59 |
TIMESTAMP | YYYYMMDDhhmmss格式表示的时间戳 | 197010101000000至2037年的某个时刻 |
YEAR | 1901至2155 |
(4)NULL值
- 理解为“没有值”或“未知值”
- 不要用NULL进行算术运算,结果仍为NULL
4.MySQL的数据字段属性
- UnSigned:无符号的,声明该数据列不能为负数。
- ZEROFILL:0填充的,不足的位数用0来填充。
- Auto_InCrement:自增,每添加一条数据,自动在上一个记录数上加1。
- NULL和NOT NULL:默认为NULL,即没有插入该列的数值;如果设置为NOT NULL,则该列必需有值。
- 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.外键
- 概念:
如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。外键可以使两张表形成关联。 - 创建外键
(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`);
- 删除外键
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=6 | false |
<>或!= | 不等于 | 5!=6 | true |
> | 大于 | 5>6 | false |
< | 小于 | 5<6 | true |
>= | 大于等于 | 5>=6 | false |
<= | 小于等于 | 5<=6 | true |
BETWEEN | 在某个范围之间 | 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 | 即使左表中没有匹配,也从右表中返回所有的行 |
- 内连接:
是一种最常用的连接类型。内连接查询实际上是一种任意条件的查询。使用内连接时,如果两个表的相关字段满足连接条件,就从这两个表中提取数据并组合成新的记录,也就是在内连接查询中,只有满足条件的元组才能出现在结果关系中。 - 外连接:
内连接的查询结果都是满足连接条件的元组。但有时我们也希望输出那些不满足连接条件的元组信息。比如,我们想知道每个学生的选课情况,包括已经选课的学生(这部分学生的学号在学生表中有,在选课表中也有,是满足连接条件的),也包括没有选课的学生(这部分学生的学号在学生表中有,但在选课表中没有,不满足连接条件),这时就需要使用外连接。外连接是只限制一张表中的数据必须满足连接条件,而另一张表中的数据可以不满足连接条件的连接方式。
(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表:
id | NAME | money |
---|---|---|
1 | A | 800 |
2 | B | 1200 |
3 | C | 1000 |
(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的账户都没有发生变化,说明事务回滚成功。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了