spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!

280 阅读3分钟

前言

  项目框架主要是spring,持久层框架没有用mybtis,用的是spring 的jdbc;

  业务需求:给应用添加领域(一个领域包含多个应用,一个应用可能属于多个领域,一般而言一个应用只属于一个领域),要求是给应用添加领域的时候,先将该应用已有的领域都删除,之后再将选中的领域添加到数据库;

  为了减少准备工作,我利用了以前的代码和数据建模,那么就成了:添加person的时候先删除已存在name为新添加person的name的person,再添加新person,说直白点就是:添加name为zhangsan的person,那么先删除数据库中name为zhangsan的所有person信息,然后再将新的zhangsan的person信息添加到数据库中;

  环境搭建过程我就不写了,完整代码会以附件形式上传;

  注意:druid连接池一般而言,jdbc设置成自动提交,不设置的话,默认也是自动提交(有兴趣的朋友可以去看下druid连接池的源码)

jdbcTemplate自动提交

  先来验证下,当前jdbcTempalte是否是自动提交的,如何验证了,我可以在jdbcTemplate执行完之后抛出一个异常,代码如下  

public int deleteOnePerson(String name) {
        int count = jdbcTemplate.update(DELETE_ONE_PERSON, new Object[]{name});        // jdbcTemplate执行完成
        count = count / 0;                                                            // 抛出RuntimeException
        return count;
    }

  没有配置事务

 View Code

  那么如果直接像如下方式来处理先删后加是不行的,如果删成功添加失败,那么数据库的数据却只是删了而没有添加成功

public int insertOnePerson(String name, int age) {
        int result = 0;
        int count = jdbcTemplate.update(DELETE_ONE_PERSON, new Object[]{name});
        if(count >= 0)                                                                    // =0的情况是数据库之前不存在该name的person信息
        {
            result = jdbcTemplate.update(INSERT_ONE_PERSON, new Object[]{name,age});
        }
        return result    ;
    }

 手动提交事务

  为了保证事务一致性,第一时间想到了jdbcTemplate是否有事务相关设置,然而并没有发现,但是发现了jdbcTemplate.getDataSource().getConnection(),于是飞快的写了如下代码:

  手动提交1

public int insertOnePerson(String name, int age) {
        int result = 0;
        try {
            jdbcTemplate.getDataSource().getConnection().setAutoCommit(false);
            int count = jdbcTemplate.update(DELETE_ONE_PERSON, new Object[]{name});
            if(count >= 0)                                                                    // =0的情况是数据库之前不存在该name的person信息
            {
                result = jdbcTemplate.update(INSERT_ONE_PERSON, new Object[]{name,"1ac"});
            }
            jdbcTemplate.getDataSource().getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                jdbcTemplate.getDataSource().getConnection().rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                jdbcTemplate.getDataSource().getConnection().setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return result    ;
    }

  本以为实现事务一致性,可执行结果如下:                                        

   发现没有实现事务一致性,这是为什么??????? 这里先留个悬念,大家好好思考下;当时我也没去仔细研究,因为完成任务才是第一要紧事,紧接着写出了如下代码:

  手动提交2

public int insertOnePerson(String name, int age) {
        int result = 0;
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = jdbcTemplate.getDataSource().getConnection();
            if(conn != null)
            {
                conn.setAutoCommit(false);
                pstmt = conn.prepareStatement(DELETE_ONE_PERSON);
                pstmt.setString(1, name);
                int count = pstmt.executeUpdate();
                pstmt.close();
                if(count >= 0)
                {
                    pstmt = conn.prepareStatement(INSERT_ONE_PERSON);
                    pstmt.setString(1, name);
                    pstmt.setString(2, "1adh");                                        // 引发异常
                    result = pstmt.executeUpdate();
                }
                conn.commit();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                System.out.println("rollback failed..");
                e1.printStackTrace();
            }
        } finally {
            try{
                conn.setAutoCommit(true);
                if(pstmt != null){
                    pstmt.close();
                }
                if(conn != null){
                    conn.close();
                }
            }catch(SQLException e){
                
            }
        }
        return result    ;
    }

   诡异的事情来了,居然和上面的情况一样:删除成功,添加失败! 我的天老爷,这是怎么回事????

  瞬间懵逼了,怎么回事?代码怎么改都不行!!!

mysql引擎

    查看数据库引擎,发现引擎是MyISAM!  瞬间爆炸!!!!

                                                             

  将引擎改成InnoDB后,手动提交2的代码是能够保证事务一致性的,那么手动提交1的代码是不是也能保证事务一致性了? 此处再留一个悬念,希望各位观众老爷们好好思考下。

事务自动管理

  任务虽然完成了,可是无论是手动提交2,还是手动提交1(姑且认为能保证事务一致性),代码的try catch简直让人无法接受;映像中,spring有事务管理,那么就来看看事务交给spring如何实现

  配置事务管理器:

 View Code

  配置事务

@Transactional
    public int insertOnePerson(String name, int age) {
        int result = 0;
        int count = jdbcTemplate.update(DELETE_ONE_PERSON, new Object[]{name});
        if(count >= 0)                                                                    
        {
            result = jdbcTemplate.update(INSERT_ONE_PERSON, new Object[]{name,"l123a"});
        }
        return result    ;
    }

  执行结果如下: