zoukankan      html  css  js  c++  java
  • java事务(二)

    本地事务

    事务类型

    事务可以分为本地事务和分布式事务两种类型。这两种事务类型是根据访问并更新的数据资源的多少来进行区分的。本地事务是在单个数据源上进行数据的访问和更新,而分布式事务是跨越多个数据源来进行数据的访问和更新。在这里要说的事务是基于数据库这种数据源的。

    JDBC事务

    在JAVA中,我们使用JDBC来连接数据库,访问和更新数据。那么在JDBC中是如何实现事务的,事务是被谁来管理的?这个答案当然是数据库,JDBC本身并没有处理事务的能力,而是依赖于底层数据库,底层数据库来提供事务的服务。在很多资料上会提到,JDBC的事务是基于连接的,也就是那个Connection对象,这个连接的本质其实是连接到数据库的一个Socket,与数据库建立连接以后就可以向数据库发送指令和数据,最终数据库会根据接收到的指令和数据来进行增删改查以及事务的处理。另外,事务是被限制在单个连接上的,这就好像我们去银行的营业厅办理业务,营业厅有多个窗口,我们只会在自己的窗口上与银行工作人员进行沟通并处理自身的业务,而不能跨窗口,告诉别的窗口的工作人员把那哥们的钱转到自己卡上,这事也就只能想想。

    数据库事务例子

    下面先看一个MYSQL数据库事务的例子:

    1、创建表

    创建用户表

    复制代码
    CREATE TABLE USERS
    (
        USER_ID INT NOT NULL AUTO_INCREMENT  COMMENT '自增主键',
        USER_NAME VARCHAR(25) NOT NULL  COMMENT '用户名',
        PASSWORD  VARCHAR(25) NOT NULL COMMENT '密码',
        GENDER    VARCHAR(1)  COMMENT '性别',
        PHONE_NO VARCHAR(11) NOT NULL COMMENT '手机号',
            CREATE_TIME DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '创建时间',
        PRIMARY KEY (USER_ID)
    )
    COMMENT = '用户表';
    复制代码

    创建用户和角色的关联关系表

    复制代码
    CREATE TABLE USER_ROLE_RELATION
    (
        REL_ID INT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
        USER_ID INT NOT NULL COMMENT '用户ID',
        ROLE_ID INT NOT NULL COMMENT '角色ID',
        PRIMARY KEY (REL_ID)
    )
    COMMENT = '用户和角色对应关系表'
    复制代码

    2、创建存储过程,在存储过程中使用事务

    复制代码
    BEGIN
        #声明一个标志位,默认为0
        DECLARE T_ERROR INTEGER DEFAULT 0;
        #如果事务执行过程中出现异常,那么把标志位设置为1
        DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET T_ERROR = 1;  
        #开始事务
        START TRANSACTION; 
    INSERT INTO USERS (USER_NAME, PASSWORD, GENDER, PHONE_NO) VALUES ('zhangsan', '123456', '男', '11111111111'); INSERT INTO USER_ROLE_RELATION (USER_ID, ROLE_ID) VALUES ('333', '1'); #如果执行成功,直接提交,否则回滚 IF T_ERROR = 0 THEN COMMIT; ELSE   ROLLBACK; END IF; END
    复制代码

    3、执行存储过程

    存储过程执行成功以后,数据库的这两个表中会分别出现一条记录。然后把

    INSERT INTO USER_ROLE_RELATION (USER_ID, ROLE_ID) VALUES ('333', '1');

    这个语句改为:

    #这条INSERT语句违反了非空约束,会抛出异常
    INSERT INTO USER_ROLE_RELATION (USER_ID, ROLE_ID) VALUES (NULL, '1');

    重新保存并执行存储过程,结果是两个表中都没有新增记录。两条SQL语句要么同时成功,要么同时失败。

     

    JDBC事务例子

    JDBC中如果需要手动提交事务的话,需要用到Connection对象中的三个方法:

    //设置是否自动提交
    setAutoCommit()
    //提交
    commit()
    //回滚
    rollback()

    在JDBC中事务的提交方式默认是自动提交的,手动提交的事务的话需要更改默认设置:

    Connection.setAutoCommit(false)

    下面这个例子是假设新增一个用户并给用户赋默认权限,那么需要同时向两张表中分别插入一条记录,把新增用户和赋权限看做是一个执行单元,放在一个事务中。

    复制代码
    package person.lb.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    
    public class TestJDBCTransAction {
    
        static {
            try {
                //加载驱动类
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } 
        }
        
        public static void main(String[] args) {
            //执行事务
            executeTransaction();
        }
    
        private static void executeTransaction() {
            Connection conn = null;
            Statement st1 = null;
            Statement st2 = null;
            ResultSet rs = null;
            try {
                //获取数据库连接
                conn = (Connection) DriverManager.getConnection(
                        "jdbc:mysql://192.168.0.105:3306/TEST", 
                        "root", 
                        "root");
                conn.setAutoCommit(false);
                
                st1 = (Statement) conn.createStatement();
                st2 = (Statement) conn.createStatement();
                //插入用户信息
                st1.execute("INSERT INTO USERS (USER_NAME, PASSWORD, GENDER, PHONE_NO) "
                          + "VALUES ('zhangsan', '123456', '男', '11111111111')"
                          , Statement.RETURN_GENERATED_KEYS);
                //获取增长列的新值
                rs = st1.getGeneratedKeys();
                String userID = "";
                if(rs.next()) {
                    userID = rs.getString(1);
                }
                //插入用户和角色关联数据
                st2.execute("INSERT INTO USER_ROLE_RELATION (USER_ID, ROLE_ID) VALUES (" +userID +", '1')");
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            } finally {
                try {
                    if(rs != null) {
                        rs.close();
                    }
                    if(st1 != null) {
                        st1.close();
                    }
                    if(st2 != null) {
                        st2.close();
                    }
                    if(conn != null) {
                        conn.close();
                    }
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    复制代码

    程序执行结果如下:

    USERS表

    USER_ROLE_RELATION表

     

  • 相关阅读:
    黑鲨2无限重启 把竞技按钮调到最上
    绿联 电池
    阿里云
    Centos 8 搭建时钟服务器
    CentOS8系统时间同步解决方法
    解决问题的人干活快的人
    【海通国际】Joe Lowry(Mr. Lithium)谈全球电池原材料供应危机
    Linux 实验楼
    用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程
    【随笔】阿里云修改DNS
  • 原文地址:https://www.cnblogs.com/yang82/p/7811020.html
Copyright © 2011-2022 走看看