第10天 Transaction事务

今日内容介绍

u 事务管理

u 转账案例

u 事务总结

第1章   事务管理

1.1  事务概述

l  事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.

l  事务作用:保证在一个事务中多次操作要么全都成功,要么全都失败.

1.2  mysql事务操作

sql语句

描述

start transaction;

开启事务

commit;

提交事务

rollback;

回滚事务

l  准备数据

# 创建一个表:账户表.

create database webdb;

# 使用数据库

use webdb;

# 创建账号表

create table account(

id int primary key auto_increment,

name varchar(20),

money double

);

# 初始化数据

insert into account values (null,'jack',10000);

insert into account values (null,'rose',10000);

insert into account values (null,'tom',10000);

l  操作:

n  MYSQL中可以有两种方式进行事务的管理:

u  自动提交:MySql默认自动提交。及执行一条sql语句提交一次事务。

u  手动提交:先开启,再提交

n  方式1:手动提交

start transaction;

update account set money=money-1000 where name='守义';

update account set money=money+1000 where name='凤儿';

commit;

#或者

rollback;

n  方式2:自动提交,通过修改mysql全局变量“autocommit”进行控制

show variables like '%commit%';

* 设置自动提交的参数为OFF:

set autocommit = 0;  -- 0:OFF  1:ON

l  扩展:Oracle数据库事务不自动提交

1.3  JDBC事务操作

Connection对象的方法名

描述

conn.setAutoCommit(false)

开启事务

conn.commit()

提交事务

conn.rollback()

回滚事务

1.3.1 案例代码一

JdbcTxDemo_01.java

package com.itheima_01_jdbc;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

import org.junit.Test;

import com.itheima.utils.C3P0Utils;

public class JdbcTxDemo_01 {

@Test

public void demo01() throws SQLException{

//模板

Connection conn = null;

try {

// 1 获得连接

conn = C3P0Utils.getConnection();

// 2 开启事务

conn.setAutoCommit(false);

//.....

// 3 提交事务

conn.commit();

} catch (Exception e) {

// 4 回滚事务

conn.rollback();

} finally{

// 5 释放资源

if(conn != null){

conn.close();

}

}

}

@Test

public void demo02() throws SQLException{

//转账

Connection conn = null;

PreparedStatement psmt = null;

try {

// 1 获得连接

conn = C3P0Utils.getConnection();

// 2 开启事务

conn.setAutoCommit(false);

String sql = "update account set money=money+? where name=?";

//具体操作

psmt = conn.prepareStatement(sql);

// @1 汇款

psmt.setInt(1, -100);

psmt.setString(2, "jack");

psmt.executeUpdate();

//模拟停电

int i = 1/0;

// @2 收款

psmt.setInt(1, 100);

psmt.setString(2, "rose");

psmt.executeUpdate();

// 3 提交事务

conn.commit();

} catch (Exception e) {

// 4 回滚事务

conn.rollback();

throw new RuntimeException("程序回滚",e);

} finally{

// 5 释放资源

if(conn != null){

conn.close();

}

}

}

}

1.4  DBUtils事务操作

Connection对象的方法名

描述

conn.setAutoCommit(false)

开启事务

new QueryRunner()

创建核心类,不设置数据源(手动管理连接)

query(conn , sql , handler, params )  或

update(conn, sql , params)

手动传递连接

DbUtils.commitAndClose(conn)  或

DbUtils.rollbackAndClose(conn)

提交并关闭连接

回顾并关闭连接

1.4.1 案例代码二

DbUtilsTxDemo_01.java

package com.itheima_02_dbutils;

import java.sql.Connection;

import org.apache.commons.dbutils.DbUtils;

import org.apache.commons.dbutils.QueryRunner;

import org.junit.Test;

import com.itheima.utils.C3P0Utils;

public class DbUtilsTxDemo_01 {

@Test

public void demo01(){

// 使用DBUtils 模板

Connection conn = null;

try {

// 获得连接

conn = C3P0Utils.getConnection();

// 开启事务

conn.setAutoCommit(false);

//.....

// 提交并释放资源

DbUtils.commitAndCloseQuietly(conn);

} catch (Exception e) {

// 回滚并释放资源

DbUtils.rollbackAndCloseQuietly(conn);

}

}

@Test

public void demo02(){

// 转账

Connection conn = null;

QueryRunner queryRunner = new QueryRunner();

try {

// 获得连接

conn = C3P0Utils.getConnection();

// 开启事务

conn.setAutoCommit(false);

String sql = "update account set money=money+? where name=?";

//@1汇款

int r = queryRunner.update(conn, sql, -100,"jack");

//@2收款

int r2 = queryRunner.update(conn, sql, 100 , "rose");

// 提交并释放资源

DbUtils.commitAndCloseQuietly(conn);

System.out.println("提交成功");

} catch (Exception e) {

e.printStackTrace();

// 回滚并释放资源

DbUtils.rollbackAndCloseQuietly(conn);

System.out.println("程序回滚");

}

}

}

第2章   转账案例

2.1  案例分析

l  开发中,常使用分层思想

n  不同的层次结构分配不同的解决过程,各个层次间组成严密的封闭系统

n  不同层级结构彼此平等

n  分层的目的是:

u  解耦

u  可维护性

u  可扩展性

u  可重用性

l  不同层次,使用不同的包表示

n  com.itheima           公司域名倒写

n  com.itheima.dao              dao层

n  com.itheima.service  service层

n  com.itheima.domain javabean

n  com.itheima.utils             工具

2.2  代码实现

l  步骤1:编写入口程序

public static void main(String[] args) {

try {

String outUser = "jack";

String inUser = "rose";

Integer money = 100;

//2 转账

AccountService accountService = new AccountService();

accountService.transfer(outUser, inUser, money);

//3 提示

System.out.println("转账成功");

} catch (Exception e) {

System.out.println("转账失败");

}

}

l  步骤3:编写AccountService

public class AccountService {

/**

* 业务层转账的方法:

* @param from    :付款人

* @param to :收款人

* @param money :转账金额

*/

public void transfer(String from, String to, double money) {

// 调用DAO:

AccountDao accountDao = new AccountDao();

try {

accountDao.outMoney(from, money);

// int d = 1/0;

accountDao.inMoney(to, money);

} catch (SQLException e) {

e.printStackTrace();

}

}

}

步骤4:编写AccountDao.java

public class AccountDao {

/**

* 付款的方法

* @param name

* @param money

* @throws SQLException

*/

public void outMoney(String name,double money) throws SQLException{

Connection conn = null;

PreparedStatement pstmt = null;

try{

// 获得连接:

conn = JDBCUtils.getConnection();

// 编写一个SQL:

String sql = "update account set money = money-? where name=?";

// 预编译SQL:

pstmt = conn.prepareStatement(sql);

// 设置参数:

pstmt.setDouble(1, money);

pstmt.setString(2, name);

// 执行SQL:

pstmt.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

pstmt.close();

conn.close();

}

}

/**

* 收款的方法

* @param name

* @param money

* @throws SQLException

*/

public void inMoney(String name,double money) throws SQLException{

Connection conn = null;

PreparedStatement pstmt = null;

try{

// 获得连接:

conn = JDBCUtils.getConnection();

// 编写一个SQL:

String sql = "update account set money = money+? where name=?";

// 预编译SQL:

pstmt = conn.prepareStatement(sql);

// 设置参数:

pstmt.setDouble(1, money);

pstmt.setString(2, name);

// 执行SQL:

pstmt.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

pstmt.close();

conn.close();

}

}

}

2.3  事务管理:传递Connection

l  修改service和dao,service将connection传递给dao,dao不需要自己获得连接

2.3.1 service层

public void transfer(String outUser,String inUser,int money){

Connection conn =null;

try{

//1 获得连接

conn = JdbcUtils.getConnection();

//2 开启事务

conn.setAutoCommit(false);

accountDao.outMoney(conn,outUser, money);

//断电

//int i = 1 / 0;

accountDao.inMoney(conn,inUser, money);

//3 提交事务

conn.commit();

} catch (Exception e) {

try {

//回顾

if (conn != null) {

conn.rollback();

}

} catch (Exception e2) {

}

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, null, null);

}

}

2.3.2 dao层

/**

* 汇款

* @param outUser 汇款人

* @param money -

*/

public void outMoney(Connection conn, String outUser , int money){

//Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

try {

//1 获得连接

//conn = JdbcUtils.getConnection();

//2 准备sql语句

String sql = "update account set money = money - ? where username = ?";

//3预处理

psmt = conn.prepareStatement(sql);

//4设置实际参数

psmt.setInt(1, money);

psmt.setString(2, outUser);

//5执行

int r = psmt.executeUpdate();

System.out.println(r);

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//6释放资源

JdbcUtils.closeResource(null, psmt, rs);

}

}

/**

* 收款

* @param inUser 收款人

* @param money +

*/

public void inMoney(Connection conn,String inUser , int money){

//Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

try {

//1 获得连接

//conn = JdbcUtils.getConnection();

//2 准备sql语句

String sql = "update account set money = money + ? where username = ?";

//3预处理

psmt = conn.prepareStatement(sql);

//4设置实际参数

psmt.setInt(1, money);

psmt.setString(2, inUser);

//5执行

int r = psmt.executeUpdate();

System.out.println(r);

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//6释放资源

JdbcUtils.closeResource(null, psmt, rs);

}

}

2.4  提高:ThreadLocal

2.4.1 案例介绍

在“事务传递参数版”中,我们必须修改方法的参数个数,传递链接,才可以完成整个事务操作。如果不传递参数,是否可以完成?在JDK中给我们提供了一个工具类:ThreadLocal,此类可以在一个线程中共享数据。

2.4.2 相关知识:ThreadLocal

java.lang.ThreadLocal 该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具类底层就是一个Map,key存放的当前线程,value存放需要共享的数据。

2.4.3 分析

2.4.4 实现

2.4.4.1       工具类JDBCUtils

//连接池

private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itcast");

//给当前线程绑定 连接

private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();

/**

* 获得连接

* @return

*/

public static Connection getConnection(){

try {

//#1从当前线程中, 获得已经绑定的连接

Connection conn = local.get();

if(conn == null){

//#2 第一次获得,绑定内容 – 从连接池获得

conn = dataSource.getConnection();

//#3 将连接存 ThreadLocal

local.set(conn);

}

return conn; //获得连接

} catch (Exception e) {

//将编译时异常 转换 运行时 , 以后开发中 运行时异常使用比较多的。

// * 此处可以编写自定义异常。

throw new RuntimeException(e);

// * 类与类之间 进行数据交换时,可以使用return返回值。也可以自定义异常返回值,调用者try{} catch(e){ e.getMessage() 获得需要的数据}

//throw new MyConnectionException(e);

}

}

2.4.4.2       service层

public void transfer(String outUser,String inUser,int money){

Connection conn =null;

try{

//1 获得连接

conn = JdbcUtils.getConnection();

//2 开启事务

conn.setAutoCommit(false);

accountDao.out(outUser, money);

//断电

//int i = 1 / 0;

accountDao.in(inUser, money);

//3 提交事务

conn.commit();

} catch (Exception e) {

try {

//回顾

if (conn != null) {

conn.rollback();

}

} catch (Exception e2) {

}

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, null, null);

}

}

2.4.4.3       dao层

/**

* 汇款

* @param outUser 汇款人

* @param money -

*/

public void out(String outUser , int money){

Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

try {

//1 获得连接

conn = JdbcUtils.getConnection();

//2 准备sql语句

String sql = "update account set money = money - ? where username = ?";

//3预处理

psmt = conn.prepareStatement(sql);

//4设置实际参数

psmt.setInt(1, money);

psmt.setString(2, outUser);

//5执行

int r = psmt.executeUpdate();

System.out.println(r);

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//6释放资源--不能关闭连接

JdbcUtils.closeResource(null, psmt, rs);

}

}

/**

* 收款

* @param inUser 收款人

* @param money +

*/

public void in(String inUser , int money){

Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

try {

//1 获得连接

conn = JdbcUtils.getConnection();

//2 准备sql语句

String sql = "update account set money = money + ? where username = ?";

//3预处理

psmt = conn.prepareStatement(sql);

//4设置实际参数

psmt.setInt(1, money);

psmt.setString(2, inUser);

//5执行

int r = psmt.executeUpdate();

System.out.println(r);

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//6释放资源--注意:不能关闭链接

JdbcUtils.closeResource(null, psmt, rs);

}

}

第3章   事务总结

3.1  事务特性:ACID

l  原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

l  一致性(Consistency)事务前后数据的完整性必须保持一致。

l  隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

l  持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

3.2  并发访问问题

如果不考虑隔离性,事务存在3中并发访问问题。

  1. 脏读:一个事务读到了另一个事务未提交的数据.
  2. 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。
  3. 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。

3.3  隔离级别:解决问题

l  数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。

  1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

a)     存放:3个问题(脏读、不可重复读、虚读)。

b)     解决:0个问题

  1. read committed 读已提交,一个事务读到另一个事务已经提交的数据。

a)     存放:2个问题(不可重复读、虚读)。

b)     解决:1个问题(脏读)

  1. repeatable read  :可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。

a)     存放:1个问题(虚读)。

b)     解决:2个问题(脏读、不可重复读)

  1. serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

a)     存放:0个问题。

b)     解决:3个问题(脏读、不可重复读、虚读)

l  安全和性能对比

n  安全性:serializable > repeatable read > read committed > read uncommitted

n  性能 : serializable < repeatable read < read committed < read uncommitted

l  常见数据库的默认隔离级别:

n  MySql:repeatable read

n  Oracle:read committed

3.4  演示

l  隔离级别演示参考:资料/隔离级别操作过程.doc【增强内容,了解】

l  查询数据库的隔离级别

show variables like '%isolation%';

select @@tx_isolation;

l  设置数据库的隔离级别

n  set session transaction isolation level 级别字符串

u  级别字符串:read uncommitted、read committed、repeatable read、serializable

n  例如:set session transaction isolation level read uncommitted;

l  读未提交:read uncommitted

n  A窗口设置隔离级别

n  AB同时开始事务

n  A 查询

n  B 更新,但不提交

n  A 再查询?-- 查询到了未提交的数据

n  B 回滚

n  A 再查询?-- 查询到事务开始前数据

l  读已提交:read committed

n  A窗口设置隔离级别

n  AB同时开启事务

n  A查询

n  B更新、但不提交

n  A再查询?--数据不变,解决问题【脏读】

n  B提交

n  A再查询?--数据改变,存在问题【不可重复读】

l  可重复读:repeatable read

n  A窗口设置隔离级别

n  AB 同时开启事务

n  A查询

n  B更新, 但不提交

n  A再查询?--数据不变,解决问题【脏读】

n  B提交

n  A再查询?--数据不变,解决问题【不可重复读】

n  A提交或回滚

n  A再查询?--数据改变,另一个事务

l  串行化:serializable

n  A窗口设置隔离级别

n  AB同时开启事务

n  A查询

n  B更新?--等待(如果A没有进一步操作,B将等待超时)

n  A回滚

n  B 窗口?--等待结束,可以进行操作

java基础-day33的更多相关文章

  1. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  2. [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html   谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...

  3. 【JAVA面试题系列一】面试题总汇--JAVA基础部分

    JAVA基础 基础部分的顺序: 基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法 线程的语法,集合的语法,io 的语法,虚拟机方面的语法 每天几道,持续更新!! 1.一个". ...

  4. 最适合作为Java基础面试题之Singleton模式

    看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...

  5. java基础练习 字符串,控制流,日历,日期等

    1,对基本控制流程的一些练习 package org.base.practice3; import org.junit.Test; /** * Created with IntelliJ IDEA. ...

  6. Java基础知识【下】( 转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608360 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

  7. Java基础知识【上】(转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608272 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

  8. java基础学习03(java基础程序设计)

    java基础程序设计 一.完成的目标 1. 掌握java中的数据类型划分 2. 8种基本数据类型的使用及数据类型转换 3. 位运算.运算符.表达式 4. 判断.循环语句的使用 5. break和con ...

  9. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

随机推荐

  1. overflow visibility opacity(透明度) vertical-align 等等

     一,overflow属性:   1,四个值:    visible     默认值.内容不会被修剪,会呈现在元素框之外.    hidden        内容会被修剪,并且其余内容是不可见的.   ...

  2. 5A - 超级楼梯

    有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法? Input 输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1< ...

  3. C#泛型的学习

    编码: class Program { static void Main(string[] args) { ; Test<int> test1 = new Test<int>( ...

  4. Luogu 2467[SDOI2010]地精部落 - DP

    Solution 这题真秒啊,我眼瞎没有看到这是个排列 很显然, 有一条性质: 第一个是山峰 和 第一个是山谷的情况是一一对应的, 只需要把每个数 $x$  变成 $n-x+1$ 然后窝萌定义数组 $ ...

  5. read temperature

    button1, button2, richtexbox1, serialport1, using System;using System.Collections.Generic;using Syst ...

  6. 最新Dashboard设计实例、技巧和资源集锦,视觉和功能两不误,妥妥的!

    Dashboard设计,尽管设计师们叫法各不相同(例如:“数据面板设计”, “控制面板设计”, “仪表盘设计”或“后台界面设计”等等).但,此类设计的最终目都是力求以最直观.最简洁的方式呈现各种信息和 ...

  7. 使用UIkit的uk-form-icon后input框无法输入的问题

    相关版本UIkit2.27.5 uikit.min.css默认使用uk-form-icon的属性pointer-events: none:因此表框无法点击. <style type=text/c ...

  8. Python中的实例方法、类方法、静态方法和普通方法

    为了辨析它们的差别,写了如下代码: class C: def self_method(self, a): return a @classmethod def class_method(cls, a): ...

  9. 从hash算法到java hashcode()

    转载 https://blog.csdn.net/Walk_er/article/details/74976146 hash算法是一个摘要算法(yy:描述性算法:可以给一个物体确切的描述,但是不能通过 ...

  10. [网络]10M、100M、1000M网线的水晶头接法

    在网络维护过程中经常要自己制作网线,水晶头理论上是这样接的: 10M和100M和1000M以太网在使用网线时,对网线各自有不同的要求. 10M和100M在目前来说,连接网络的时候,只用到两对线来传输网 ...