![121efef1bba4fc29dc88f8fdcb76bef8.png](https://i-blog.csdnimg.cn/blog_migrate/35a3acc4f72a7f1b40c1d56ee0100b4d.jpeg)
温馨提示:如果想学扎实,一定要从头开始看凯哥的一系列文章(凯哥带你从零学大数据系列),千万不要从中间的某个部分开始看,知识前后是有很大关联,否则学习效果会打折扣.
系列文章第一篇是拥抱大数据:凯哥带你从零学大数据系列-学习大数据前奏(必读)(点我)!
正课内容
知识点:
1.JDBC概述
2.JDBC原理
3.JDBC使用
4.事务
教学重点:
1.JDBC的使用
2.事务
第一章:JDBC的简介
1.1 需求
早期的数据库应用程序开发,因为没有通用的针对与数据库的编程接口,所以,开发人员需要学习相关数据库的API,才可以进行应用程序,这样增加了学习成本和开发周期。因此整个开发市场一直在呼吁有一套通用的编程接口
1.2 ODBC
因为有市场需要,微软定义了一组用于数据库应用程序的编程接口ODBC(open database connectivity)。这一套方案大大缩短了程序的开发周期,可以让开发人员只需要调用同一套编程接口,无需考虑具体实现。
![935f7cb6b8fc3d97c39909f7b3d26ffd.png](https://i-blog.csdnimg.cn/blog_migrate/3206a5358cd752feba70eea7feaeb147.png)
ODBC分为四个部分:
1. 应用程序:开发人员所写的代码,ODBC提供的调用接口
2. 驱动程序管理器:用于管理驱动程序的。
3. 驱动程序:对接口的实现部分,各个数据库厂商来完成的。
4. 数据源:就是连接数据库的一些参数:url,username,password
1.3 JDBC
Sun公司参考了ODBC方案,制定了一组专门为java语言连接数据库的通用接口JDBC。方便了java开发人员,开发人员不需要考虑特定的数据库的DBMS。JDBC不直接依赖于DBMS,而是通过驱动程序将sql语句转发给DBMS,由DBMS进行解析并执行,处理结果返回。
<font color='red'>注意:</font>驱动程序:由数据库厂商自己实现,程序员只需要拿来使用即可。
![a8b4bcb413d8249418bfb627d3a21495.png](https://i-blog.csdnimg.cn/blog_migrate/27a88b89f0d056e53404cfeabb8ffc9d.jpeg)
1.4 JDBC的工作原理
![f20dda3ed17008f70e9f0f1092e8dbe7.png](https://i-blog.csdnimg.cn/blog_migrate/dda3a1248c2a8738ef7cb75969997104.jpeg)
第一步:注册驱动程序
第二步: 请求连接
第三步: 获取执行sql语句的对象,发送给DBMS
第四步:返回结果集,程序员进行处理
第五步: 关闭连接操作
1.5 JDBC中常用的接口和类
1.5.1 java.sql.DriverManager:类,用于连接数据库,返回Connection对象
getConnection(String url, String user, String password)
url: 连接指定数据库的地址 jdbc:mysql://ip:port/dbname
user: 连接用户名
password:密码
1.5.2 java.sql.Connection:是接口。
Statement createStatement();
作用:用于获取Statement对象
1.5.3 java.sql.Statement:接口
execute(String sql):通常用于DDL
executeUpdate(String sql):通常用于DML
executeQuery(String sql):用于DQL
1.5.4 java.sql.ResultSet:接口,其子类型封装了DQL的返回值
next():光标方法,向下移动一行,
getDate(int columnIndex)
getDate(String columnLabel)
getString(int columnIndex)
getString(String columnLabel)
getInt(int columnIndex)
getInt(String columnLabel)
getDouble(int columnIndex)
getDouble(String columnLabel)
第二章:JDBC入门编程
2.1 编写步骤
1. 创建项目,加载相应的静态资源,如图片、第三方jar包等
注意:jar包要add到Library里
2. 注册驱动
3. 建立连接
4. 获取执行对象
5. 处理结果集
6. 关闭连接
2.2 编写测试类:update
public static void main1(String[] args) throws ClassNotFoundException, SQLException {
//第一步:加载驱动
Class.forName("com.mysql.jdbc.Driver");
//第二步:建立连接
Connection conn =DriverManager
.getConnection("jdbc:mysql://localhost:3306/qianfeng","root","123456");
//第三步:获取执行对象Statement
Statement stat = conn.createStatement();
String sql = "update emp set sal = 10000,comm = 500 where empno = 9000";
//第四步:调用方法执行sql,即发送sql
int num = stat.executeUpdate(sql);
System.out.println("受影响的记录数目:"+num);
//第五步:关闭连接
conn.close();
}
3.3 编写DQL
public void test2(){
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try{
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//建立连接
conn = DriverManager
.getConnection("jdbc:mysql://localhost:3306/qianfeng","root","123456");
//获取执行对象,执行sql
stat = conn.createStatement();
//返回结果集
rs = stat.executeQuery("select * from emp");
while(rs.next()){
int empno = rs.getInt(1);
String ename = rs.getString("ename");
Date hiredate = rs.getDate("hiredate");
double salary = rs.getDouble("sal");
System.out.println(empno+","+ename+","+hiredate+","+salary);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
rs.close();
stat.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.4 练习:插入和删除的代码
1.完成向表中插入一条记录的代码
2.完成从表中删除一条记录的代码
2.5 DBUtil工具类的封装
2.5.1 配置文件db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/qianfeng
username=root
passwd=123456
2.5.2 DBUtil代码
/**
* 封装了一些对数据库的连接和关闭操作
*/
public class DBUtil {
private static String driver;
private static String url;
private static String username;
private static String password;
static{
try{
//读取配置文件:
InputStream is = DBUtil.class.getClassLoader()
.getResourceAsStream("db.properties");
//获取Properties对象
Properties p = new Properties();
//把流里的内容加载到p对象中
p.load(is);
//取value值
driver = p.getProperty("driver");
url = p.getProperty("url");
username = p.getProperty("username");
password = p.getProperty("pwd");
//注册驱动
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 连接数据库的方法
* @return Connection对象
*/
public static Connection getConnection() throws SQLException{
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
/**
* 关闭连接操作
* 关闭ResultSet对象
* 关闭Statement对象
* 关闭Connection对象
*/
public static void closeConnection(Connection conn, Statement stat, ResultSet rs){
try {
if(rs!=null){
rs.close();
}
if(stat!=null){
stat.close();
}
if(conn !=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws SQLException {
Connection conn = getConnection();
System.out.println(conn);
closeConnection(conn,null,null);
}
}`
2.5.3 修改代码:调用DBUtil工具
1)update代码的修改
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection conn = DBUtil.getConnection();
//第三步:获取执行对象Statement
Statement stat = conn.createStatement();
String sql = "update emp set sal = 10000,comm = 500 where empno = 9000";
//第四步:调用方法执行sql,即发送sql
int num = stat.executeUpdate(sql);
System.out.println("受影响的记录数目:"+num);
//第五步:关闭连接
DBUtil.closeConnection(conn,stat,null);
}
2)DQL代码的修改
public void test1(){
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try{
conn = DBUtil.getConnection();
//获取执行对象,执行sql
stat = conn.createStatement();
//返回结果集
rs = stat.executeQuery("select * from emp");
while(rs.next()){
int empno = rs.getInt(1);
String ename = rs.getString("ename");
Date hiredate = rs.getDate("hiredate");
double salary = rs.getDouble("sal");
System.out.println(empno+","+ename+","+hiredate+","+salary);
}
}catch (Exception e){
e.printStackTrace();
}finally {
DBUtil.closeConnection(conn,stat,rs);
}
}`
2.6 jdbc的批处理
2.6.1 概念
每一次的sql操作都会占用数据库的资源。如果将N条操作先存储到缓存区中,然后再一次性刷到数据库中,这就减少了与数据库的交互次数。因此可以提高效率。
2.6.2 Statement
addBatch(String sql):将sql语句添加到缓存中
executeBatch():将缓存中的sql一次性刷到数据库中
2.6.3 代码
@Test
public void testBatch(){
Connection conn = null;
Statement stat = null;
try{
conn = DBUtil.getConnection();
stat = conn.createStatement();
int num = 0;
while(num<1003){
String sql = "insert into testbatch values (null,'zs"+num+"','f')";
stat.addBatch(sql);//将sql语句添加到缓存中,
if(num%50==0){
stat.executeBatch();//缓存中每有50条都刷新一次。
}
num++;
}
stat.executeBatch();//循环结束后,将缓存中剩余的不足50条的全都刷新出去
}catch (Exception e){
e.printStackTrace();
}finally {
DBUtil.closeConnection(conn,stat,null);
}
}
第三章:SQL注入问题
3.1 登陆案例演示
3.1.1 登陆案例需求分析:
1.需求:实现输入用户名和密码后,实现跳转到主页面的功能
2.逻辑分析:
- 客户端:接收用户名和密码,并将这些信息发送到服务端
- 服务端:接收到客户端传过来的用户名和密码后,进行数据库校验是否存在这样的数据,如果存在,就将
用户名对应的这一条记录返回,并封装成一个User对象。返回给客户端。
- 客户端收到返回信息后,判断Account对象是否存在,如果存在,就实现跳转.....
数据:
drop table if exists bank_account;
create table bank_account (
id int primary key auto_increment comment '主键',
account_id varchar(18) not null comment '用户账号',
account_balance double(10,2) comment '账户余额',
user_name varchar(20) not null comment '用户名称',
user_pwd varchar(18) unique comment '用户密码',
user_idcard varchar(18) unique comment '身份证',
oper_time timestamp comment '操作日期',
gender enum('f','m') comment 'f 表示女性,m表示男性'
);
insert into bank_account values (null,'6225113088436225',200000,'zhugeliang','zgl123456','100000100010101000','2019-01-01 13:10:10','m');
insert into bank_account values (null,'6225113088436226',1000,'zhouyu','zy123456','100000100010101001','2019-03-01 14:10:10','m');
insert into bank_account values (null,'6225113088436227',210000,'caocao','cc123456','100000100010101002','2019-04-01 14:10:10','m');
insert into bank_account values (null,'6225113088436228',500,'niumo','nm123456','100000100010101003','2019-03-01 10:10:10','m');
commit;
select * from banK_account;
3.1.2 User类型的定义
package com.jdbc.day02.login;
import java.sql.Date;
import java.util.Objects;
/**
* @Description 把表中的每一条记录封装成一个Account对象
*/
public class Account {
private int id;
private String accountId;
private double balance;
private String username;
private String password;
private String idcard;
private Date operationTime;
private String gender;
public Account(){}
public Account(int id, String accountId, double balance, String username, String password, String idcard, Date operationTime, String gender) {
this.id = id;
this.accountId = accountId;
this.balance = balance;
this.username = username;
this.password = password;
this.idcard = idcard;
this.operationTime = operationTime;
this.gender = gender;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getIdcard() {
return idcard;
}
public void setIdcard(String idcard) {
this.idcard = idcard;
}
public Date getOperationTime() {
return operationTime;
}
public void setOperationTime(Date operationTime) {
this.operationTime = operationTime;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return id == account.id &&
Double.compare(account.balance, balance) == 0 &&
Objects.equals(accountId, account.accountId) &&
Objects.equals(username, account.username) &&
Objects.equals(password, account.password) &&
Objects.equals(idcard, account.idcard) &&
Objects.equals(operationTime, account.operationTime) &&
Objects.equals(gender, account.gender);
}
@Override
public int hashCode() {
return Objects.hash(id, accountId, balance, username, password, idcard, operationTime, gender);
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", accountId='" + accountId + ''' +
", balance=" + balance +
", username='" + username + ''' +
", password='" + password + ''' +
", idcard='" + idcard + ''' +
", operationTime=" + operationTime +
", gender='" + gender + ''' +
'}';
}
}
3.1.3 服务端的代码
package com.jdbc.day02.login;
import com.jdbc.util.DButil;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* @Description 用于检查数据库中是否存在含有客户端发送过来的username和password这样的账号信息
*/
public class AppServer {
public static Account checkLogin(String username,String password){
//将形式参数放到一个sql语句中,发送到数据库中,进行检验
Connection conn =null;
Statement stat = null;
ResultSet rs = null;
Account account = null;
try{
conn = DButil.getConnection();
stat = conn.createStatement();
String sql = "select * from bank_account where user_name='"+username+"' and user_pwd='"+password+"'";
rs = stat.executeQuery(sql);
if(rs.next()){
int id = rs.getInt("id");
String accountId = rs.getString("account_id");
double balance = rs.getDouble("account_balance");
String idcard = rs.getString("user_idcard");
Date operationTime = rs.getDate("oper_time");
String gender = rs.getString("gender");
account = new Account(id,accountId,balance,username,password,idcard,operationTime,gender);
}
}catch (Exception e){
e.printStackTrace();
}finally {
DButil.closeConnection(conn,stat,rs);
}
return account;
}
}
3.1.4 客户端的代码
package com.jdbc.day02.login;
import java.util.Scanner;
/**
* @Description 模拟银行客户端程序
*/
public class Client {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = "haha' or '1' = '1";
/**
* 将数据发送到服务端:(调用服务端的相关方法)
* 即调用服务端的静态方法checkLogin().
* 如果能查询到信息,就返回一个Account类型的对象
*/
Account account = AppServer.checkLogin(username,password);
if(account!=null){
System.out.println("--正在跳转到主页面--");
}else{
System.out.println("----用户名或密码错误,请重写输入----------");
}
}
}
3.2 SQL注入问题(安全隐患)
3.2.1 安全隐患
Statament对象发送的语句可以被改变结构,即如果之前在where中设置的是两个条件,那么可以通过一些参数 比如 添加or 后面再跟其他条件。此时,where子句中是三个条件。这种情况就叫做SQL注入。有安全隐患问题。
3.3 PreparedStatement类
3.3.1 预编译类的简介
- PreparedStatement是Statement的子类型
- 此类型可以确定SQL语句的结构,无法通过其它方式来增减条件。
- 此类型还通过占位符 "?"来提前占位,并确定语句的结构。
- 提供了相应的赋值方式:
ps.setInt(int index,int value)
ps.setString(int index,String value)
ps.setDouble(int index,double value)
ps.setDate(int index,Date value)
index:表示sql语句中占位符?的索引。从1开始
value:占位符所对应的要赋予的值
- 执行方法:
ps.execute() ;------用于DDL和DML
ps.executeUpdate();-----用于DML
ps.executeQuery();-----用于DQL
3.3.2 修改了Server里的代码
/**
* @Description 使用PreparedStatement实现服务端
*/
public class PrepareServer {
public static User checkLogin(String username,String pwd){
if(username==null||username.length()==0){
return null;
}
if(pwd==null||pwd.length()==0){
return null;
}
//将数据传入持久层
Connection conn = null;
// 将Statement 换成 PreparedStatement
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try{
conn = DBUtil.getConnection();
//sql语句放在获取预编译执行对象前,同时可以使用它占位符
String sql = "select * from emp where ename = ? and empno = ?";
//获取预编译执行对象,同时传入sql参数
ps = conn.prepareStatement(sql);
//给占位符赋值
ps.setString(1,username);
ps.setInt(2,Integer.valueOf(pwd));
//调用执行查询的方法,不带参数。
rs = ps.executeQuery();
if(rs.next()){
int empno = rs.getInt(1);
String ename = rs.getString(2);
String job = rs.getString(3);
int mgr = rs.getInt(4);
Date hiredate = rs.getDate(5);
double salary = rs.getDouble(6);
double comm = rs.getDouble(7);
int deptno = rs.getInt(8);
user = new User(empno,ename,job,mgr,hiredate,salary,comm,deptno);
}
}catch (Exception e){
e.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,rs);
}
return user;
}
}
3.3.3 客户端的代码
/**
* @Description 修改客户端实现
*/
public class Client {
public static void main(String[] args) {
// Scanner sc = new Scanner(System.in);
// System.out.println("请输入用户名:");
// String username = sc.next();
// System.out.println("请输入密码:");
// String password = sc.next();
String username = "smith";
String password = "222 or 1=1";
//调用服务端的代码
//User user = Server.checkLogin(username,password);
//换成修改后的服务端的代码
User user = PrepareServer.checkLogin(username,password);
if(user!=null){
System.out.println("登陆成功,页面跳转中..........");
}else{
System.out.println("登陆失败,请检查用户名或者密码");
}
}
}
第四章:JDBC的事务支持
4.1 银行转账案例演示
4.4.1 案例分析:
1.需求:一个账号fromAccount向另一个账号toAccount转入money元钱
2.分析:
- 检查两个账号是否存在,不存在的话,结束转账行为
- 检查转出账号的里金额是否充足,不充足,结束转账行为,充足的话,进行扣款money元
- 转入账号进行增加money元
4.4.2 代码实现:
/**
* @Description 银行转账案例
*/
public class AccountTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要出账的账号和进账的账号");
String fromAccount = sc.next();
String toAccount = sc.next();
System.out.println("请输入转账金额:");
double money = sc.nextDouble();
boolean flag = oneToOne(fromAccount,toAccount,money);
if(flag){
System.out.println("success");
}else{
System.out.println("fail");
}
}
/**
* 封装了两个人之间的转账逻辑
* true:表示转账成功
* false:表示转账失败
*/
public static boolean oneToOne(String fromAccount,String toAccount,double money){
/*第一步:先校验账户信息是否有效*/
if(fromAccount==null||fromAccount.length()==0){
return false;
}
if(toAccount==null||toAccount.length()==0){
return false;
}
if(money<=0){
return false;
}
Connection conn = null;
//预编译sql语句执行对象
PreparedStatement ps = null;
ResultSet rs = null;
try{
//验证转出账号是否存在
conn = DBUtil.getConnection();
String sql1 = "select * from bank_account where account_id=?";
ps = conn.prepareStatement(sql1);
ps.setString(1,fromAccount);
rs = ps.executeQuery();
boolean flag = rs.next();
if(!flag){
System.out.println("出账账号不存在");
return false;
}
//,如果存在,先提取出余额,保存到一个变量中
double balance = rs.getDouble("account_balance");
//验证进账账号是否存在
sql1 = "select * from bank_account where account_id=?";
ps = conn.prepareStatement(sql1);
ps.setString(1,toAccount);
rs = ps.executeQuery();
flag = rs.next();
if(!flag){
System.out.println("进账账号不存在");
return false;
}
/*第二步:检查formAccount账户内的余额,足够的前提下,修改余额*/
//判断出账账号的余额是否充足
if(money>balance){
System.out.println("余额不足");
return false;
}
//此时,来到此处后就可以进行转账业务了
//先扣除出账账号的money
String sql2 = "update bank_account set account_balance=account_balance-? where account_id = ?";
ps = conn.prepareStatement(sql2);
ps.setDouble(1,money);
ps.setString(2,fromAccount);
ps.executeUpdate();
/*第三步:修改toAccount账户内的余额*/
//进账账号再+money
String sql3 = "update bank_account set account_balance=account_balance+? where account_id = ?";
ps = conn.prepareStatement(sql3);
ps.setDouble(1,money);
ps.setString(2,toAccount);
ps.executeUpdate();
return true;
}catch (Exception e){
e.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,rs);
}
return false;
}
}
4.2 转账异常演示
在转出账户转出金额之后 和 转入账户收入金额之前模拟 空指针异常
String str = null;
System.out.println(str.length());
4.3 JDBC的事务支持
4.3.1 事务的概念:
当一个业务需求涉及到N个DML操作时,这个业务(或者时N个DML操作)当成一个整体来处理。在处理的过程中,如果有失败或异常,我们要回到业务开始时。如果成功处理,我们再将数据持久化到磁盘中。这样一个过程我们称之为一个事务。具有原子性。不可切割。
关键字:
commit
rollback
savepoint
4.3.2 事务的特性:
![59d55c178864fa06673d0702dca4b001.png](https://i-blog.csdnimg.cn/blog_migrate/73114da794af60fc6430ff1d7ed0fd67.jpeg)
4.3.3 MySQL事务
- 默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。
- 如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
开启事务:start transaction;
结束事务:commit或rollback;
回滚情况
START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
ROLLBACK;
提交情况
START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
COMMIT;
4.3.4 JDBC的事务支持
Connection.setAutoCommit(boolean flag):此方法可以取消事务的自动提交功能,值为false。
Connection.commit():进行事务提交 。
Connection.rollback():进行事务回滚。
4.3.3 多事务的情况:
![14ec78d363a1fe79b12408c19bd36fee.png](https://i-blog.csdnimg.cn/blog_migrate/46edb5e301e7b4ad7111fbd310d61e17.jpeg)
4.3.4 隔离机制
![55819cdded199220d682f88de871e83c.png](https://i-blog.csdnimg.cn/blog_migrate/e0532fca25b0ec9396e97ec043d3f54b.jpeg)
4.4 修改转账代码:改为手动提交
public static boolean oneToOne(String fromAccount, String toAccount, double money) {
/*第一步:先校验账户信息是否有效*/
if (fromAccount == null || fromAccount.length() == 0) {
return false;
}
if (toAccount == null || toAccount.length() == 0) {
return false;
}
if (money <= 0) {
return false;
}
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
//取消事务自动提交功能,改为手动提交
conn.setAutoCommit(false);
String sql1 = "select * from bank_account where account_id=?";
ps = conn.prepareStatement(sql1);
ps.setString(1, fromAccount);
rs = ps.executeQuery();
boolean flag = rs.next();
if (!flag) {
System.out.println("出账账号不存在");
return false;
}
/*第二步:检查formAccount账户内的余额,足够的前提下,修改余额*/
//取出余额
double balance = rs.getDouble("account_balance");
//sql1 = "select * from bank_account where account_id=?";
//ps = conn.prepareStatement(sql1);
ps.setString(1, toAccount);
rs = ps.executeQuery();
flag = rs.next();
if (!flag) {
System.out.println("进账账号不存在");
return false;
}
//判断出账账号的余额是否充足
if (money > balance) {
System.out.println("余额不足");
return false;
}
//此时,来到此处后就可以进行转账业务了
//先扣除出账账号的money
String sql2 = "update bank_account set account_balance=account_balance-? where account_id = ?";
ps = conn.prepareStatement(sql2);
ps.setDouble(1, money);
ps.setString(2, fromAccount);
ps.executeUpdate();
//模拟一个空指针异常
String str = null;
System.out.println(str.length());
/*第三步:修改toAccount账户内的余额*/
//进账账号再+money
String sql3 = "update bank_account set account_balance=account_balance+? where account_id = ?";
ps = conn.prepareStatement(sql3);
ps.setDouble(1, money);
ps.setString(2, toAccount);
ps.executeUpdate();
//手动提交事务
conn.commit();
return true;
} catch (Exception e) {
e.printStackTrace();
//当出现异常,我们才需要回滚事务
try{
conn.rollback();
}catch (Exception e1){
e1.printStackTrace();
}
} finally {
DBUtil.closeConnection(conn, ps, rs);
}
return false;
}