第一部分:事务

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.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.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.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.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.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 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.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事务_java核心技术第五篇之事务和MVC模式相关推荐

  1. 浅析java中的死锁_Java学习笔记五十五(死锁问题)

    多线程死锁问题. 我们知道,多线程可以改善系统的资源利用率,并且可以提高程序的运行效率.但是,多线程也带来了新的问题,即:死锁问题. 1.死锁的概念 死锁可以理解为多个线程为了争夺同一个资源,而出现互 ...

  2. Java中的四个核心技术思想

    Java中的四个核心技术思想 对Java核心概念和思想的掌握有助于提升我们对整个Java平台的理解力.这里将介绍四个Java中的核心技术思想,包括Java虚拟机.类装载器的体系结构.class文件和A ...

  3. java中输入日期_Java中的日期操作

    在日志中常用的记录当前时间及程序运行时长的方法: public void inject(Path urlDir) throws Exception { SimpleDateFormat sdf = n ...

  4. Java实现二十三种设计模式(五)—— 十一种行为型模式 (中)——解释器模式、迭代器模式、中介者模式、备忘录模式

    Java实现二十三种设计模式(五)-- 十一种行为型模式 (中)--解释器模式.迭代器模式.中介者模式.备忘录模式 一.解释器模式 我国 IT 界历来有一个汉语编程梦,虽然各方对于汉语编程争论不休,甚 ...

  5. java中策略设计模式_Java中的设计模式(五):策略模式

    策略设计模式是行为设计模式之一.当我们为特定任务使用多个算法时,使用策略模式,客户端决定在运行时使用的实际实现. 策略模式的最佳示例之一是Collections.sort()采用Comparator参 ...

  6. java中参数存储_Java中函数参数传递和数据存储

    值传递是将要传递的值作为一副本传递的.. 引用传递,传递的是引用对象的内存地址.. 例如: int i=4; int j=i;   //相当于把4复制了一个副本赋给了j 输出结果是i=4 ,j=4 C ...

  7. java中大数开方_Java中的大数运算

    # 一:大数运算出现的背景 java里面整型int与浮点型float,double它们存放数据的范围是有限的,当出行更大的数值时会发生溢出. 最典型的场景是金融行业,直接使用单精度或者双精浮点数来表示 ...

  8. java中循环语句_Java语法基础之循环结构语句详解

    一.循环结构 循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循 ...

  9. java中 移位运算_java中关于移位运算符的demo与总结(推荐)

    首先,移位运算符有三种,其操作类型只支持:byte / short / char / int和long五种. << 左移运算符,表示将左边的操作数的二进制数据向左移动*位,移动后空缺位以0 ...

最新文章

  1. 深入聊一聊 Spring AOP 实现机制
  2. 详解RMQ LCA
  3. 漫谈C++重载运算符
  4. 给NavigationCtrl 增强动画.
  5. python面试题总结(5)--数据类型(字典)
  6. python实时读取日志并打印关键字怎么实现_python pytest测试框架介绍五---日志实时输出...
  7. git 历史操作日志_Git - 查看提交历史
  8. Django项目部署:使用uwsgi和nginx的方式
  9. 《进化——我们在互联网上奋斗的故事》一一1.9 职业素养中的品德细节
  10. 解决无法下载安装Android SDK的问题
  11. Android 开发中常用的库
  12. skill快捷键设置
  13. 一文带你看懂自然语言处理——word表示技术的变迁(从bool模型到BERT)
  14. c语言建立循环链表,C语言实现循环链表
  15. Android中你不得不知的几个问题及解决方法
  16. 图片批量下载 +图片马赛克:多张图片组成端午安康!
  17. 【干货】Dialog的高冷用法
  18. canvas制作圆型印章
  19. 目标检测经典论文——Faster R-CNN论文翻译:Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Net
  20. 关于ROS(Robot OS 机器人操作系统)

热门文章

  1. 【2D目标跟踪】SIMPLE ONLINE AND REALTIME TRACKING阅读笔记(2017)
  2. MDERank A Masked Document Embedding Rank Approach for Unsupervised Keyphrase Extraction阅读笔记
  3. 【MySQL】如何导入SQL数据库
  4. Java中死锁产生的原因及解决方法
  5. python中symbols函数用法_Python中偏函数用法示例
  6. 离零服务费再进一步 摩根大通推出最低收费美股ETF
  7. By not providing “FindOpenCV.cmake“ in CMAKE_MODULE_PATH this project has asked CMake to find a pack
  8. 艾蒂机器人_奥特曼格斗进化3艾斯机器人怎么玩厉害_奥特曼格斗进化3艾斯机器人使用心得分享_斗蟹游戏网...
  9. 英语名言录:富兰克林 十三条箴言(双语)
  10. Redux 学习之 middleware详解