第一部分:事务
1.事务的简介:
1.1 在一组操作中(比如增加操作,修改操作),只有增加和修改操作都成功之后,这两个操作才能真正的成功.
,如果这两个操作中,有一个失败了,这两个操作都失败了.

1.2 应用的场景:转账的例子.
(1) 有两个人:小奥和小温
(2) 小温转账5000给小奥
(3) 小温少5000,小奥多5000
(4) 产生问题:小温给小奥转账5000,小温少5000,发生异常错误,小奥没有得到钱
(5) 使用事务解决问题

2.mysql中操作事务:
2.0 两个概念
(1) 提交事务:让表中数据真正生效
(2) 回滚事务:回到操作之前的状态

2.1 在mysql中事务默认是自动提交的

2.2 设置mysql的事务提交方式不是自动提交,需要手动提交事务.
(1)查询当前mysql的事务提交方式:show variables like '%commit%';
(2)查询提交方式:set autocommit = off/0; 0---OFF 1---ON

(3)mysql数据库操作事务语句
= 打开事务:start transaction;
= 提交事务:commit;
= 回滚事务:rollback;

3.jdbc操作事务:
3.1 在jdbc操作中,事务也是自动提交的

3.2 设置事务不是自动提交
(1) 在Connection里面setAutoCommit(boolean autocommit),设置是否自动提交
=参数值,默认是true,设置不自动提交设置值false;
(2) 在connection里面commit(),提交事务
(3) 在Connection里面rollback(),回滚事务

3.3 演示转账的例子()

4.事务的特性:
4.1有四个特性
(1)原子性 : 在一组操作中,要么都成功,有一个失败所有的都失败.
(2)一致性 : 在操作之前和之后数据一致的
(3)隔离性 : 多个事务之间的操作不会互相影响的
= 开启第一个事务一
= 开启第二个事务二
== 如果在事务一,添加数据,不提交事务
== 在事务二厘米,查询不到添加的数据的
(4)持久性 : 提交事务之后,数据真正生效

5.如果不考虑事务的隔离性,产生问题(三个读的问题)
5.1脏读
(1)有两个事务,其中一个事务读到另一个事务没有提交的数据
insert into account values('小胖',30000);
insert into account values('小苍',30000);

(2)演示操作步骤:
第一步:打开两个cmd窗口,分别连接数据库,切换到day14;
第二步:在左边的窗口中设置事务的隔离级别 read uncommitted
set session transaction isolation level read uncommitter;
第三步:在两个窗口中分别开启事务
第四步:在右边窗口,小胖给小苍转账10000;
update account sel sal = sal -10000 where username = "小胖";
update account sal sal = sal + 10000 where username = "小苍";

(3)产生的问题: 如果小胖把事务回滚了,小苍查询不到数据,称为脏读.

(4)解决脏读的方法: 设置事务额隔离级别
(5)脏读是一个问题,不允许发生的

5.2不可重复读
(1)有两个事务,其中一个没有提交的事务读到另一事务提交的update的操作
(2) 操作步骤:
第一步:打开两个cmd窗口,分别连接数据库,切换到day14;
第二步:在左边的窗口设置隔离级别 read committed
set session set sal = sal-10000 where username = '小胖';
第三步:在两个窗口中分别开启事务
第四步:在右边窗口,小胖给小苍转账10000;
update account set sal = sal -10000 where username = '小胖';
update account set sal = sal + 10000 where username = '小苍';
第五步: 在左边查询结果,发现数据变化,左边是在一个事务中,没有提交的事务,却读到另一事务中提交的数据.
称为不可重复读
(3)是一种现象,在一些情况下允许发生的
(4)解决脏读的方法:设置事务的隔离级别解决
(5)演示步骤:
第一步:打开两个cmd窗口,分别连接数据库,切换到day14;
第二步:在左边的窗口中设置事务的隔离级别 repeatable read
set session transaction isolation level repeatable read;
第三步:在两个cmd窗口中分别开启事务
第四步:在右边窗口,小胖给小苍转账10000;
update account set sal = sal-10000 where username = '小胖';
update account set sal = sal + 10000 where username = "小苍";
(3)产生问题:如果小胖把事务回滚了,小苍查询不到数据,称为脏读

5.3虚读(幻读)
(1)有两个事务,其中一个没有提交的事务读到另一事务提交的insert的操作

5.4解决读的问题: 设置事务的隔离级别解决
数据库提供四个隔离级别,防止三类读问题:
serializable : 串行的.避免脏读,不可重复读和虚读发生
repeatable read : 重复读.避免脏读,但是不可重复读和虚读有可能发生
read committed : 已提交读.避免脏读,但是不可重复读和虚读有可能发生
read uncommitted : 未提交读.脏读,不可重复读,虚读

隔离级别优先级:read uncommitted << read commited << repeatble read << serializable

(2)mysql数据库默认隔离级别: repeatble read

(3)操作语句
select @@tx isolation; 查询当前事务隔离级别
set session transaction isolation level 设置事务隔离级别

6.JDBC中设置事务的隔离级别
6.1 Connection里面setTransactionIsolation(int lexel)方法设置
= 方法的参数:使用Connection里面常量表示不同的隔离级别

关于事务中掌握内容:
1.mysql里面操作事务语句:
(1) 开启事务:start transaction
(2) 提交事务:commit
(3) 回滚事务:rollback
(4) 在mysql里面默认自动提交

2.jdbc操作事务的三个方法

3.事务的四个特性

4.如果不考虑隔离性,产生三个读的问题
(1)设置事务的隔离级别解决
(2)mysql默认的隔离级别,repeatable read

*/

/*

01.事务_概述
1)."事务"是"数据库"中的概念,它是指针对一个业务,在数据库中要执行多个操作。
例如:银行转账
张三给李四转账1000元;
在数据库中至少要做两个操作:
1).将张三的账户减少1000元;
2).将李四的账户增加1000元;
对于数据库软件,要有能力将多个SQL语句作为一个"整体",要么全部成功,要么全部失败。
这个整体被执行的业务就叫:事务。
2).我们今天讲到的事务处理的方式:
1).在MySQL中怎样直接操作事务;
2).通过JDBC怎样操作数据库中的事务;
3).通过DBUtils怎样操作数据库中的事务;

02.事务_MySQL中的事务处理
1).自动事务:MySQL的默认事务处理方式
将每条SQL作为一个独立的事务的进行处理,会被立即修改到数据库中。
2).手动事务:
1).关闭自动事务[不常用]:
A).查看当前的事务处理方式:show variables like 'autocommit';
结果:autocommit ON (自动提交--打开)
B).关闭自动提交:
set autocommit = off;
C).发送SQL语句
update users set ....
....
update users set ....
D).提交:
commit;//将把之前发送的所有SQL语句全部更新
或者
回滚:
rollback;//将把之前所有的SQL语句全部取消
注意:此设置只对当前的连接用户有效
2).在"自动事务"的情况下,临时开启一个事务【常用】:
A).临时开启一个事务:
start transaction;
B).发送SQL语句
update users set ....
....
update users set ....
C).提交:
commit;
或者
回滚:
rollback;
注意:不论提交或者回滚,当前的事务立即结束。而且立即恢复到之前的提交模式。
如果不提交或者回滚,就断开连接,后期数据库会将此次事务的所有操作全部回滚。

03.事务_JDBC中的事务处理
......
conn.setAutoCommit(false);//开启事务--设置Connection对象的"自动提交-false"

try{
int row1 = stmt.executeUpdate("update users set loginName = 'aa33' where uid = 1");

int row2 = stmt.executeUpdate("update users set loginName = 'bb33' where uid = 2");

conn.commit();//5.提交

}catch(Exception e){
conn.rollback();//6.回滚事务
}finally{
conn.setAutoCommit(true);//开启自动提交
conn.close();//关闭连接
}

System.out.println("完毕!");

源码:

package cn.baidu.demo01_JDBC中的事务处理;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class Demo {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/hei66_day21","root","123");
//3.开启事务--设置Connection对象的"自动提交-false"
conn.setAutoCommit(false);
//4.发送SQL语句
Statement stmt = conn.createStatement();
try{
int row1 = stmt.executeUpdate("update users set loginName = 'aa33' where uid = 1");

int row2 = stmt.executeUpdate("update users set loginName = 'bb33' where uid = 2");
//5.提交
conn.commit();
System.out.println("提交事务......");
}catch(Exception e){
//6.回滚事务
conn.rollback();
System.out.println("回滚事务......");
}finally{
//开启自动提交
conn.setAutoCommit(true);
//关闭连接
conn.close();
}

System.out.println("完毕!");
}
}

04.事务_DBUtils中的事务处理

QueryRunner qr = new QueryRunner();//1.创建一个QueryRunner对象
//2.获取一个Connection对象
ComboPooledDataSource ds = new ComboPooledDataSource();
Connection conn = ds.getConnection();
//设置为手动提交
conn.setAutoCommit(false);
//3.发送SQL语句
String sql1 = "update users set loginName = 'aa55' where uid = 1";
String sql2 = "update users2 set loginName = 'bb55' where uid = 2";
try{
int row1 = qr.update(conn, sql1);//【注意--调用的update方法要接收一个Connection对象】
int row2 = qr.update(conn, sql2);//【注意--调用的update方法要接收一个Connection对象】
//提交
conn.commit();
System.out.println("提交事务......");
}catch(Exception e){
//回滚
conn.rollback();
System.out.println("回滚事务......");
}finally{
//设置为自动提交
conn.setAutoCommit(true);
//回收连接
conn.close();
}

源码:

package cn.baidu.demo02_DBUtils中的事务处理;

import java.sql.Connection;
import java.sql.Statement;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class Demo {
public static void main(String[] args) throws Exception {
//1.创建一个QueryRunner对象
QueryRunner qr = new QueryRunner();

//2.获取一个Connection对象
ComboPooledDataSource ds = new ComboPooledDataSource();
Connection conn = ds.getConnection();
//设置为手动提交
conn.setAutoCommit(false);
//3.发送SQL语句
String sql1 = "update users set loginName = 'aa55' where uid = 1";
String sql2 = "update users2 set loginName = 'bb55' where uid = 2";
try{
int row1 = qr.update(conn, sql1);
int row2 = qr.update(conn, sql2);
//提交
conn.commit();
System.out.println("提交事务......");
}catch(Exception e){
//回滚
conn.rollback();
System.out.println("回滚事务......");
}finally{
//设置为自动提交
conn.setAutoCommit(true);
//回收连接
conn.close();
}

}
}

-----------------------------------------------------------------------------------
05.MVC模式_不使用MVC模式实现登录注册案例

package cn.baidu.demo03_不使用MVC模式实现登录注册案例;

import java.sql.SQLException;
import java.util.Scanner;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

public class Demo {
public static void main(String[] args) throws SQLException {
Scanner sc = new Scanner(System.in);
while(true){
System.out.println("1.登录 2.注册 3.退出:");
int op = sc.nextInt();
switch(op){
case 1://登录
toLogin();
break;
case 2://注册
toRegist();
break;
case 3://退出
System.out.println("谢谢使用!!");
System.exit(0);
default:
System.out.println("错误的输入!");
break;
}
}
}
//注册
private static void toRegist() throws SQLException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String loginName = sc.next();
System.out.println("请输入密码:");
String loginPwd = sc.next();

//1.验证用户名和密码的字符
//略

//2.数据库验证--用户名不能重复
QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from users where loginName = ?";
User userBean = qr.query(sql, new BeanHandler<User>(User.class),loginName);
if(userBean != null){
System.out.println("用户名:" + loginName + " 已存在!");
return;
}

//3.将这条信息写入到数据库
sql = "insert into users values(null,?,?)";
int row = qr.update(sql,loginName,loginPwd);
if(row > 0){
System.out.println("注册成功!");
}else{
System.out.println("注册失败!");
}
}
//登录
private static void toLogin() throws SQLException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入登录名:");
String loginName = sc.next();
System.out.println("请输入密码:");
String loginPwd = sc.next();

//查询数据
QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from users where loginName = ? and loginPwd = ?";
User user = qr.query(sql, new BeanHandler<User>(User.class),loginName,loginPwd);
if(user != null){
System.out.println("欢迎:" + loginName + " 登录系统!!");
}else{
System.out.println("用户名或密码错误!!");
}
}
}

package cn.baidu.demo03_不使用MVC模式实现登录注册案例;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

public static DataSource getDataSource(){
return dataSource;
}
}

06.MVC模式_使用MVC模式实现登录

MVC模式:(MVC模式产生的原因)如果将程序的所有代码写在一个类中,这样代码量太大,会对后期程序维护造成困难.

什么是MVC模式呢?
A:任何的程序都可以分为两部分代码:
1.用于接收用户数据,为用户显示数据的代码:视图层 例如:键盘录入和输出语句
2.用于业务逻辑处理的代码 :控制层 例如:数据逻辑处理和执行SQL语句等
3.作为逻辑模型 :模型层 例如:JavaBean

B:作为企业级开发,要分五层:
1.视图层(View)(1.接收数据;2.显示数据)
2.控制层(Controller)(1.业务分发:查找相应的业务层)
3.业务层(Service)(1.负责处理具体的业务逻辑)
4.持久层(DAO)(1.所有访问数据库的代码)
5.数据类型(类:Model)
(从1-5,进过数据库再返回去5-1)

好处:1.将不同功能的代码分到不同的类中,使一个类中都具有相同功能的代码(高内聚)
2.从视图层-->控制层-->业务层-->持久层 有一个"顺序的依赖关系",反向,则没有这种依赖关系,相互独立.(低耦合)

注意:1.从视图层到持久层,之间是顺序的依赖关系,不能跳级.
2.返现不存在依赖关系,不能在底层主动调用上一层的类中的方法.

/*

package cn.baidu.demo04_MVC实现登录注册;

public class UserModel {
private Integer uid;
private String loginName;
private String loginPwd;
public UserModel(Integer uid, String loginName, String loginPwd) {
super();
this.uid = uid;
this.loginName = loginName;
this.loginPwd = loginPwd;
}
public UserModel() {
super();
// TODO Auto-generated constructor stub
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getLoginPwd() {
return loginPwd;
}
public void setLoginPwd(String loginPwd) {
this.loginPwd = loginPwd;
}
@Override
public String toString() {
return "User [uid=" + uid + ", loginName=" + loginName + ", loginPwd=" + loginPwd + "]";
}

}

package cn.baidu.demo04_MVC实现登录注册;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

public static DataSource getDataSource(){
return dataSource;
}
}

/*
* 视图层:只接受和输出语句,显示
*
*/
public class View {
private Controller control = new Controller();

public void start() {
Scanner sc = new Scanner(System.in);
while(true) {
System.out.println("1.登录 2.注册 3.退出");
int op = sc.nextInt();
switch(op) {
case 1://登录
toLogin();
break;
case 2://注册
toRegist();
break;
case 3://退出
System.out.println("谢谢使用!!");
System.exit(0);
default :
System.out.println("错误的输入!");
break;
}

}
}

//注册
private void toRegist() {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String loginName = sc.nextLine();
System.out.println("请输入密码:");
String loginPwd = sc.nextLine();

//把接收的数据封装到一个User对象
UserModel user = new UserModel();
user.setLoginName(loginName);
user.setLoginPwd(loginPwd);

//调用控制层
try{
if(control.toRegist(user)) {
System.out.println("注册成功!");
}else {
System.out.println("注册失败!");
}
} catch (Exception e) {
e.printStackTrace();
}
}

//登录
private void toLogin() {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
System.out.println("请输入登录名:");
String loginName = sc.nextLine();
System.out.println("请输入密码:");
String loginPwd = sc.nextLine();

//封装JavaBean
UserModel user = new UserModel();
user.setLoginName(loginName);
user.setLoginPwd(loginPwd);

//调用控制层
try {
if(this.control.toLogin(user)) {
System.out.println("欢迎:" + loginName + "登录系统!");
//启动学员信息管理的视图层代码
}else {
System.out.println("登录失败!可能的原因:用户名和密码错误!");
}
}catch (Exception e) {
e.printStackTrace();
}

}

}

package com.baidu_02;

/*
* 控制层:进行业务分发,发给业务层
*
*/
public class Controller {
private Service service = new Service();
//1.注册
public boolean toRegist(UserModel user) throws Exception {
//1.调用Service
return service.toRegist(user);
}

//2.登录
public boolean toLogin(UserModel user) throws Exception {
//调用Service
return service.toLogin(user);
}

}

//业务层:主要进行逻辑处理

public class Service {
private DAO dao = new DAO();
//1.注册:
public boolean toRegist(UserModel user) throws Exception {
/*
* 1.验证用户名和密码的字符
* 用户名6-12个字符,由数字,大小字母,小写字母组成
* 密码:必须是6位数字
*
*/
String regex = "[0-9,A-Z,a-z]{6,12}";
if(!user.getLoginName().matches(regex)) {
return false;
}
// \\d 表示数字
regex = "\\d{6}";
if(!user.getLoginPwd().matches(regex)) {
return false;
}

//2.数据库验证--用户名不能重复--调用DAO
UserModel resultUser = dao.findByLoginName(user.getLoginName());
//此用户名已经被使用
if(resultUser != null) {
return false;
}

//3.将这条信息写入到数据库
return dao.save(user);
}

//2.登录
public boolean toLogin(UserModel user) throws Exception {
//1.做一些用户名和密码的字符验证
//...
//2.查询此用户
UserModel resultBean = dao.findByLoginAndPassword(user);
return resultBean != null;
}

}

//持久层:主要对数据库进行操作

public class DAO {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());

//1.使用用户名查询一条记录
public UserModel findByLoginName(String loginName) throws Exception {
String sql = "select * from user where loginName = ?";
UserModel user = qr.query(sql, new BeanHandler<UserModel>(UserModel.class), loginName);
//查到返回:UserModel对象:否则:返回NULL
return user;
}

//2.写入一条记录
public boolean save(UserModel user) throws Exception {
String sql = "insert into user values(null,?,?)";
int row = qr.update(sql, user.getLoginName(),user.getLoginPwd());
//如果不影响1行: 返回true;如果影响0行:返回false
return row > 0;
}

//3.用户名和密码查询一个用户
public UserModel findByLoginAndPassword(UserModel user) throws Exception {
String sql = "select * from user where loginName = ? and loginPwd = ?";
//这里的BeanHandler是因为查询满足条件的数据的第一条记录
UserModel query = qr.query(sql, new BeanHandler<UserModel>(UserModel.class), user.getLoginName(),user.getLoginPwd());
return query;

}

}
/*
07.转账案例_MVC模式部署分析
08.转账案例_MVC模式事务处理实现
10.线程间共享对象实现_ThreadLocal
11.转账案例_使用ThreadLocal实现在service层和DAO层实现共享Connection对象

其实从程序角度看,tlt变量的确是一个,毫无疑问的。但是为什么打印出来的数字就互不影响呢?
是因为使用了Integer吗?-----不是。
原因是:protected T initialValue()和get(),因为每个线程在调用get()时候,发现Map中不存在就创建。调用它的时候,就创建了一个新变量
,类型为T。每次都新建,当然各用个的互不影响了。

意义:

1.提供了保存对象的方法:每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。

2.避免参数传递的方便的对象访问方式:将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()
方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

理解ThreadLocal中提到的变量副本
“当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本” —— 并不是通过ThreadLocal.set( )实现的,而是每个线程使用“new对象”(或拷贝) 的操作来创建对象副本, 通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象(ThreadLocal实例是作为map的key来使用的)。

如果ThreadLocal.set( )进去的对象是多线程共享的同一个对象,那么ThreadLocal.get( )取得的还是这个共享对象本身 —— 那么ThreadLocal还是有并发访问问题的!

解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段:

1)可重入(不依赖环境);2)互斥(同一时间段只允许一个线程使用);3)原子操作;4)Thread-Local

为了保证函数是可重入的,需要做到一下几点:

1,不在函数内部使用静态或者全局数据

2,不返回静态或者全局数据,所有的数据都由函数调用者提供

3,使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

4, 如果必须访问全局数据,使用互斥锁来保护

5,不调用不可重入函数

可重入函数:可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。
可重入函数通常要比不可重入的线程安全函数效率高一些,因为它们不需要同步操作。更进一步说,将第2类线程不安全函数转化为线程安全函数的唯一方法就是重写它,使之可重入。
显式可重入函数:如果所有函数的参数都是传值传递的(没有指针),并且所有的数据引用都是本地的自动栈变量(也就是说没有引用静态或全局变量),那么函数就是显示可重入的,也就是说不管如何调用,我们都可断言它是可重入的。

隐式可重入函数:可重入函数中的一些参数是引用传递(使用了指针),也就是说,在调用线程小心地传递指向非共享数据的指针时,它才是可重入的。例如rand_r就是隐式可重入的。
我们使用可重入(reentrant)来包括显式可重入函数和隐式可重入函数。然而,可重入性有时是调用者和被调用者共有的属性,并不只是被调用者单独的属性。

原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切[1] 换到另一个线程).

public class View {
public void start(){
Scanner sc = new Scanner(System.in);
System.out.println("转出方:");
String outUserName = sc.next();
System.out.println("转入方:");
String inUserName = sc.next();
System.out.println("转账金额:");
int money = sc.nextInt();

//封装JavaBean
TransBean t = new TransBean();
t.setOutUserName(outUserName);
t.setInUserName(inUserName);
t.setMoney(money);
Thread tt;
//调用控制层
Controller contrl = new Controller();
try {
if(contrl.toTrans(t)){
System.out.println("转账成功!");
}else{
System.out.println("转账失败,所有操作被取消!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public class Controller {
public boolean toTrans(TransBean t) throws SQLException{
//调用服务层
Service service = new Service();
return service.toTrans(t);
}
}

public class Service {
private DAO dao = new DAO();
public boolean toTrans(TransBean t) throws SQLException{
//1.验证账户是否存在;
AccountBean acc = dao.findByUserName(t.getOutUserName());
if(acc == null){
return false;
}
acc = dao.findByUserName(t.getInUserName());
if(acc == null){
return false;
}
//2.验证转出方的余额是否支持转账;
Integer outBalance = dao.findBalanceByUserName(t.getOutUserName());
if(t.getMoney() > outBalance){
return false;
}
//3.事务处理实施转账业务;
//1.获取一个连接对象
Connection conn = JDBCUtils.getDataSource().getConnection();
//2.关闭自动提交
conn.setAutoCommit(false);
//将conn通过ThreadLocal存储到当前线程的map中
Const.local.set(conn);
//3.调用dao,执行转账
try{
boolean b1 = dao.updateBalanceByUserName(t.getOutUserName(), -t.getMoney());
int i = 10 / 0;
boolean b2 = dao.updateBalanceByUserName(t.getInUserName(), t.getMoney());
if(b1 && b2){
//4.提交
conn.commit();
return true;
}else{
//5.回滚
conn.rollback();
return false;
}
}catch(Exception e){
//5.回滚
conn.rollback();
return false;
}finally{
conn.setAutoCommit(true);//还原为自动事务
conn.close();//归还到连接池中
}
}
}

public class DAO {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
//1.使用用户名查找一个用户
public AccountBean findByUserName(String userName) throws SQLException{
String sql = "select * from account where username = ?";
AccountBean acc = qr.query(sql, new BeanHandler<AccountBean>(AccountBean.class),userName);
return acc;
}
//2.获取某个用户的余额
public Integer findBalanceByUserName(String userName) throws SQLException{
String sql = "select balance from account where username = ?";
Object result = qr.query(sql, new ScalarHandler(),userName);
if(result != null){
return Integer.valueOf(result.toString());
}
return null;
}
//3.修改某个账户的余额
public boolean updateBalanceByUserName(String userName,Integer money) throws SQLException{
String sql = "update account set balance = balance + ? where username = ?";
//通过ThreadLocal从当前线程对象的Map中获取conn
int row = qr.update(Const.local.get(), sql, money,userName);
return row > 0;
}
}

public class Const {
public static ThreadLocal<Connection> local = new ThreadLocal<>();
}

07.转账案例_MVC模式部署分析

/*
* 前端的JavaBean,跟前端表单对应
*/
public class TransBean {
private String outUserName;
private String inUserName;
private Integer money;
public TransBean(String outUserName, String inUserName, Integer money) {
super();
this.outUserName = outUserName;
this.inUserName = inUserName;
this.money = money;
}
public TransBean() {
super();
// TODO Auto-generated constructor stub
}
public String getOutUserName() {
return outUserName;
}
public void setOutUserName(String outUserName) {
this.outUserName = outUserName;
}
public String getInUserName() {
return inUserName;
}
public void setInUserName(String inUserName) {
this.inUserName = inUserName;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
@Override
public String toString() {
return "TransBean [outUserName=" + outUserName + ", inUserName=" + inUserName + ", money=" + money + "]";
}
}

/*
* 后端的JavaBean,跟表结构相同
*/
public class AccountBean {
private Integer id;
private String username;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
public AccountBean() {
super();
// TODO Auto-generated constructor stub
}
public AccountBean(Integer id, String username, Integer money) {
super();
this.id = id;
this.username = username;
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", username=" + username + ", money=" + money + "]";
}
}

package cn.baidu.demo05_MVC_结合事务处理实现转账案例;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

public static DataSource getDataSource(){
return dataSource;
}
}

import java.util.Scanner;
//视图层
public class View {
public void start(){
Scanner sc = new Scanner(System.in);
System.out.println("转出方:");
String outUserName = sc.next();
System.out.println("转入方:");
String inUserName = sc.next();
System.out.println("转账金额:");
int money = sc.nextInt();

//封装JavaBean
TransBean t = new TransBean();
t.setOutUserName(outUserName);
t.setInUserName(inUserName);
t.setMoney(money);
Thread tt;
//调用控制层
Controller contrl = new Controller();
try {
if(contrl.toTrans(t)){
System.out.println("转账成功!");
}else{
System.out.println("转账失败,所有操作被取消!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

//控制层
public class Controller {
public boolean toTrans(TransBean t) throws SQLException{
//调用服务层
Service service = new Service();
return service.toTrans(t);
}
}

//业务层或者服务层
public class Service {
private DAO dao = new DAO();
public boolean toTrans(TransBean t) throws SQLException{
//1.验证账户是否存在;
AccountBean acc = dao.findByUserName(t.getOutUserName());
if(acc == null){
return false;
}
acc = dao.findByUserName(t.getInUserName());
if(acc == null){
return false;
}
//2.验证转出方的余额是否支持转账;
Integer outBalance = dao.findBalanceByUserName(t.getOutUserName());
if(t.getMoney() > outBalance){
return false;
}
//3.事务处理实施转账业务;
//1.获取一个连接对象
Connection conn = JDBCUtils.getDataSource().getConnection();
//2.关闭自动提交
conn.setAutoCommit(false);
//3.调用dao,执行转账
try{
boolean b1 = dao.updateBalanceByUserName(conn,t.getOutUserName(), -t.getMoney());
// int i = 10 / 0;
boolean b2 = dao.updateBalanceByUserName(conn,t.getInUserName(), t.getMoney());
if(b1 && b2){
//4.提交
conn.commit();
return true;
}else{
//5.回滚
conn.rollback();
return false;
}
}catch(Exception e){
//5.回滚
conn.rollback();
return false;
}finally{
conn.setAutoCommit(true);//还原为自动事务
conn.close();//归还到连接池中
}
}
}

public class DAO {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
//1.使用用户名查找一个用户
public AccountBean findByUserName(String userName) throws SQLException{
String sql = "select * from account where username = ?";
AccountBean acc = qr.query(sql, new BeanHandler<AccountBean>(AccountBean.class),userName);
return acc;
}
//2.获取某个用户的余额
public Integer findBalanceByUserName(String userName) throws SQLException{
String sql = "select balance from account where username = ?";
Object result = qr.query(sql, new ScalarHandler(),userName);
if(result != null){
return Integer.valueOf(result.toString());
}
return null;
}
//3.修改某个账户的余额
public boolean updateBalanceByUserName(Connection conn,String userName,Integer money) throws SQLException{
String sql = "update account set balance = balance + ? where username = ?";
int row = qr.update(conn, sql, money,userName);
return row > 0;
}
}

08.转账案例_MVC模式事务处理实现
10.线程间共享对象实现_ThreadLocal
11.转账案例_使用ThreadLocal实现在service层和DAO层实现共享Connection对象

------------------------以下内容【了解】-------------------------------------------
12.事务的特性_ACID
原子性:强调事务的不可分割.多条语句要么都成功,要么都失败。
一致性:强调的是事务的执行的前后,数据要保持一致.
隔离性:一个事务的执行不应该受到其他事务的干扰.
持久性:事务一旦结束(提交/回滚)数据就持久保持到了数据库.

13.事务的特性_不考虑事务的隔离性可能引发的问题
* 脏读 :一个事务读到另一个事务还没有提交的数据.
* 不可重复读 :一个事务读到了另一个事务已经提交的update的数据,导致在当前的事务中多次查询结果不一致.
* 虚读/幻读 :一个事务读到另一个事务已经提交的insert的数据,导致在当前的事务中多次的查询结果不一致.

14.事务的特性_解决引发的读问题
设置事务的隔离级别:
1 read uncommitted :未提交读.脏读,不可重复读,虚读都可能发生.
2 read committed :已提交读.避免脏读.但是不可重复读和虚读有可能发生.(Oracle默认)
4 repeatable read :可重复读.避免脏读,不可重复读.但是虚读有可能发生.(MySql默认)
8 serializable :串行化的.避免脏读,不可重复读,虚读的发生.
1).查看当前事务的隔离级别:
select @@tx_isolation;
2).设置当前事务的隔离级别:
set session transaction isolation level 上面1,2,4,8四个隔离级别名称之一;
3).级别超高,越安全,效率越低。
==========================================================================================================================
学习目标总结:
1、理解事务的概念
a. 能够说出事务的概念
事务指的是逻辑上的一组操作(多条sql语句),组成这组操作的各个单元要么全都成功,要么全都失败。
b. 能够说出事务的特性
原子性:强调事务的不可分割.多条语句要么都成功,要么都失败。
一致性:强调的是事务的执行的前后,数据要保持一致.
隔离性:一个事务的执行不应该受到其他事务的干扰.
持久性:事务一旦结束(提交/回滚)数据就持久保持到了数据库.
2、理解脏读,不可重复读,幻读的概念及解决办法
a. 能够说出不考虑事务的隔离性会出现的问题
1.脏读:一个事务读到另一个事务还没有提交的数据.
2.不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在当前的事务中多次查询结果不一致.
3.虚读/幻读:一个事务读到另一个事务已经提交的insert的数据,导致在当前的事务中多次的查询结果不一致.

b. 能够说出如何使用事务的隔离级别分别可以解决哪些问题
1 read uncommitted:什么都没解决。
2 read committed:只解决脏读。(Oracle默认)
4 repeatable read:解决了脏读、不可重复读、(MYSQL中也解决了虚读)(MySql默认)
8 serializable:将两个事务完全隔离,一个事务没有执行完毕,另一个事务只能等待;

3、能够在MySQL中使用事务
a. 说出mysql中对事务支持的特点
1.自动事务:MySQL默认
2.手动事务:
b. 使用命令行手动开启事务
start transaction;
c. 使用命令行手动提交事务
commit;
d. 使用命令行手动回滚事务
rollback;
e. 说出MySQL数据库的默认隔离级别
repeatable read

4、能够在转账功能中使用DBUtils开发包完成事务控制
a. 能够编写一个转账的程序功能
b. 能够在程序中添加事务保证转账程序的数据正确
5、能够使用DBUtils并通过ThreadLocal绑定Connection的方式控制事务
a. 独立编写操作数据库的JDBC程序
b. 能够在程序中使用ThreadLocal绑定Connection的方式控制事务

java核心技术第五篇之事务和MVC模式的更多相关文章

  1. Java核心技术第五章——1.类、超类、子类(2)

    继上一篇Java核心技术第五章——1.类.超类.子类(1) 6.重载解析 假如调用ClassName.Method(args) 1.编译器列出类ClassName所有名为Method的方法. 2.编译 ...

  2. Java系列--第五篇 基于Maven的SSME之Token及Parameterized单元测试

    本来在第四篇要说完的,但是写着写着,我觉得内容有点多起来了,所以就另开这篇,在这里专门讲述Token的定义,JSP自定义标签以及如何用Parameterized的来做单元测试. 1,新建包com.va ...

  3. Java【第五篇】基本语法之--数组

    数组概述 数组是多个相同类型数据的组合,实现对这些数据的统一管理数组属引用类型,数组型数据是对象(Object),数组中的每个元素相当于该对象的成员变量数组中的元素可以是任何数据类型,包括基本类型和引 ...

  4. java核心技术第六篇之断言、日志、包装类型和工具类

    JDK1.5新特性: 1.自动拆装箱. 2.泛型 3.可变参数 4.静态导入 5.增强for循环 6.互斥锁 7.枚举 8.注解 JDK1.6新特性: 1.Desktop类和SystemTray类 2 ...

  5. java核心技术第四篇之JDBC第二篇

    01.JDBC连接池_连接池的概念: 1).什么是连接池:对于多用户程序,为每个用户单独创建一个Connection,会使程序降低效率.这时我们可以创建一个"容器", 这个容器中, ...

  6. Java学习第五篇:二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题

    一.二进制,位运算,移位运算 1.二进制 对于原码, 反码, 补码而言, 需要注意以下几点: (1).Java中没有无符号数, 换言之, Java中的数都是有符号的; (2).二进制的最高位是符号位, ...

  7. Java 学习 第五篇;面向对象

    1:基本数据类型的拆装: 基本变量类型 通过 new WrapperClass(primitive) 创建包装类对象: 包装类的对象 通过 WrapperInstance.XXXValue() 获取包 ...

  8. Java核心技术第五章——2.Object类

    Object类:所有类的超类 Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来的.但是并不需要这样写: public class Emloyee extends Object ...

  9. Java核心技术第五章——1.类、超类、子类(1)

    1.定义子类: 关键字extends表明正在构造的新类派生与一个已存在的类.已存在的类称为超类.基类或父类:新类称为子类.派生类或孩子类.超类和子类是Java程序员最常用的两个术语(emmm~~我觉得 ...

随机推荐

  1. Web安全测试学习笔记-DVWA-CSRF

    CSRF(Cross-site request forgery)跨站请求伪造,CSRF的原理简单来说就是攻击者以用户的名义对服务器发起请求,从而达到攻击目的.与XSS不同之处在于,XSS是盗取用户co ...

  2. Cypress安装使用(E2E测试框架)

    一.简介 Cypress是为现代网络打造的下一代前端测试工具,解决了开发人员和QA工程师在测试现代应用程序时面临的关键难点问题. Cypress包含免费的.开源的.可本地安装的Test Runner  ...

  3. zookeeper启动失败,但是状态显示已启动的原因

    今天在起zookeeper集群的时候,其他两台机子都能起起来,只有这一台机子起不起来: 对比了 这个路径下的 文件后发现多了一个这个文件 根据名字推测应该是放进程id.突然明白这个应该是上次非正常退出 ...

  4. Docker组成三要素

    目录 镜像 容器 仓库 总结 Docker的基本组成三要素 镜像 容器 仓库 镜像 Docker 镜像(Image)就是一个只读的模板.镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器. ...

  5. Python xlwt 写Excel相关操作记录

    1.安装xlwt pip install xlwt 2.写Excel必要的几步 import xlwt book = xlwt.Workbook() #创建一个workbook,无编码设置编码book ...

  6. 8.1 Spark MLlib简介

    一.什么是机器学习 机器学习可以看做是一门人工智能的科学,该领域的主要研究对象是人工智能.机器学习利用数据或以往的经验,以此优化计算机程序的性能标准. 机器学习强调三个关键词:算法.经验.性能 二.基 ...

  7. 04-Node.js学习笔记-相对路径VS绝对路径

    4.1相对路径VS绝对路径 大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录 在读取文件或者设置文件路径时都会选择绝对路径 4.2使用__dirname 获取当前文件所在的 ...

  8. vue项目中引入特殊字体

    特殊字体指的是默认电脑中没有的 1.下载字体 2.新建文件夹font,把字体放进去 3.新建font.css 4.使用@font-face设置字体 @font-face { font-family: ...

  9. ELK 安装

    ELK 是 Elasticesarch Logstash kibana  三个开源软件 Elasticsearch是个开源分布式搜索引擎,提供搜集.分析.存储数据三大功能.它的特点有:分布式,零配置, ...

  10. [ Python入门教程 ] Python生成随机数模块(random)使用方法

    1.使用randint(a,b)生成指定范围内的随机整数.randint(a,b)表示从序列range([a,b])中获取一个随机数,包括b. >>> random.randint( ...