国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > 数据库 > 数据库应用 > 事务

事务

来源:程序员人生   发布时间:2017-02-06 08:03:34 阅读次数:3237次

概述

事务的概念

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

事务的特点

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

分类

手动事务

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

自动事务

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

手动事务

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


案例分析

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

该表中有两个用户

这里写图片描述

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

结合上文的铺垫,转账就能够看成1个事务,这个事务由两个操作组成。转账要末两条SQL语句全部操作成功,要末全部失败。这类结果才是我们想的。

MySql摹拟

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

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

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

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

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

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

案例实现

  • 表单界面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF⑻">
<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⑻");

        //获得到要求的数据
        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次写这么长的博文,算是简单系统的梳理了1下事务。主要为了加深记忆,另外一方面希望能帮助1些初学者。

生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生