事务

来源:互联网 发布:今日头条乱下软件 编辑:程序博客网 时间:2024/06/08 06:28

概述

事务的概念

事务是访问并可能更新数据库中各种数据项的一个程序执行单元。

事务的特点

原子性:一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性:持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

分类

手动事务

在操作数据库的时候,执行了一条增删改的语句,但是磁盘里的数据没有改变,需要手动提交事务之后,磁盘里的数据才发生改变。典型的数据库就是oracle(默认的情况下,可以更改事务类型)。

自动事务

一天增删改语句执行后,磁盘里的数据改变。典型的数据库就是MySql(默认的情况下,可以更改事务类型)。

手动事务

手动事务操作分三个部分,如下:
开启事务:开启一个事务
提交事务:从开启到提交之间的有效SQL语句,将会执行,磁盘数据将会改写,事务结束
事务回滚:从事务到回滚之间的有效SQL语句,将不生效,事务没有结束


案例分析

以MySql数据库为例,实现一个银行转账的小案例。假设有一张数据库表,表有三个字段。
这里写图片描述

该表中有两个用户

这里写图片描述

假设张三要给李四转账,要想实现两者的转账,需要两条SQL语句。如果完美的情况下,在程序执行了全部的语句,没问题。但是,如果在只执行第一个语句后,服务器停电了,这样就出现了诡异的现象,张三的钱转出去了,而李四没有收到。这是银行系统不允许的。

结合上文的铺垫,转账就可以看成一个事务,这个事务由两个操作组成。转账要么两条SQL语句全部操作成功,要么全部失败。这种结果才是我们想的。

MySql模拟

  • 理想状态
    两条语句都执行:
    这里写图片描述
    结果:
    这里写图片描述

  • 非理想状态
    只执行一条语句:
    这里写图片描述
    结果:
    这里写图片描述

可以看到自动事务,在非理想状态下,相当的不靠谱,为了避免出现这种情况,MySql提供的手动事务的语句
start transaction开启事务
rollback回滚事务
commit提交事务

  • 理想状态
    在执行更改操作前先执行start transaction
    这里写图片描述
    结果:
    这里写图片描述
    执行commit
    这里写图片描述
    结果:
    这里写图片描述

  • 非理想状态(懒得更改表单数据了,在上次更改的基础上进行)
    在执行更改操作前先执行start transaction
    这里写图片描述
    执行rollback
    这里写图片描述
    执行commit
    这里写图片描述
    结果:
    这里写图片描述

通过以上四个模拟,手动事务的优越性就体现出来了。

案例实现

  • 表单界面
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Insert title here</title></head><body>    <h1>转账窗口</h1>    <form action="/day19/TransAccServlet" method="post">        转出人:<input type="text" name="outer"><br>        转入人:<input type="text" name="inner"><br>        总金额:<input type="number" name="money"><br>        <input type="submit" value="确定">    </form></body></html>
  • XML配置
  <servlet>    <description></description>    <display-name>TransAccServlet</display-name>    <servlet-name>TransAccServlet</servlet-name>    <servlet-class>com.itheima.servlet.TransAccServlet</servlet-class>  </servlet>  <servlet-mapping>    <servlet-name>TransAccServlet</servlet-name>    <url-pattern>/TransAccServlet</url-pattern>  </servlet-mapping>
  • 工具类
package com.itheima.utils;import java.sql.Connection;import java.sql.SQLException;import javax.sql.DataSource;import com.mchange.v2.c3p0.ComboPooledDataSource;public class DBUtils {    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();    /**     * 获得数据源     */    public static DataSource getDataSource() {        return dataSource;    }    /**     * 获得数据库连接     * @throws SQLException      */    public static Connection getConnection() throws SQLException {        return dataSource.getConnection();    }    /**     * 获得当前线程绑定的连接     * @throws SQLException      */    public static Connection getCurrentConnection() throws SQLException {        Connection conn = tl.get();        if (conn==null) {            conn = getConnection();            tl.set(conn);        }        return conn;    }    /**     * 开启事务     * @throws SQLException      */    public static void beginTransaction() throws SQLException {        Connection conn = getCurrentConnection();        conn.setAutoCommit(false);    }    /**     * 回滚事务     * @throws SQLException      */    public static void rollbackTransaction() throws SQLException {        Connection conn = getCurrentConnection();        conn.rollback();    }    /**     * 提交事务     * @throws SQLException      */    public static void commitTransaction() throws SQLException {        Connection conn = getCurrentConnection();        conn.commit();        conn.close();        tl.remove();    }}
  • servlet类
package com.itheima.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.itheima.service.TransferService;public class TransAccServlet extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        response.setContentType("text/html;charset=utf-8");        //获取到请求的数据        String outer = request.getParameter("outer");        String inner = request.getParameter("inner");        String money = request.getParameter("money");        //将数据传递给业务逻辑        TransferService service = new TransferService();        boolean result = service.transferAcc(outer, inner, money);         //根据业务逻辑的反馈信息,处理后响应给浏览器        if (result) {            //转账成功            response.getWriter().write("转账成功");        } else {            //转账失败            response.getWriter().write("转账失败");        }    }    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        doGet(request, response);    }}
  • service类
package com.itheima.service;import java.sql.SQLException;import com.itheima.dao.TransferDao;import com.itheima.utils.DBUtils;public class TransferService {    public boolean transferAcc(String outer, String inner, String money) {        boolean flag = true;        TransferDao dao = new TransferDao();        try {            // 开启事务            DBUtils.beginTransaction();            dao.out(outer, money);            dao.in(inner, money);        } catch (Exception e) {            try {                flag = false;                //回滚事务                DBUtils.rollbackTransaction();            } catch (SQLException e1) {                e1.printStackTrace();            }            e.printStackTrace();        } finally {            try {                //提交事务                DBUtils.commitTransaction();            } catch (SQLException e) {                e.printStackTrace();            }        }        return flag;    }}
  • 数据库操作类
package com.itheima.dao;import java.sql.Connection;import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import com.itheima.utils.DBUtils;public class TransferDao {    public void out(String outer, String money) throws SQLException {        QueryRunner qr = new QueryRunner();        Connection conn = DBUtils.getCurrentConnection();        String sql = "update account set money=money-? where username=?";        int line = qr.update(conn, sql, money, outer);        if (line<1) {            throw new SQLException();        }    }    public void in(String inner, String money) throws SQLException {        QueryRunner qr = new QueryRunner();        Connection conn = DBUtils.getCurrentConnection();        String sql = "update account set money=money+? where username=?";        int line = qr.update(conn, sql, money, inner);        if (line<1) {            throw new SQLException();        }    }}

头一次写这么长的博文,算是简单系统的梳理了一下事务。主要为了加深记忆,另一方面希望能帮助一些初学者。

1 0
原创粉丝点击