多線程操做數據庫,保證事務的惟一性(真實有效)

網上搜羅了一大堆所謂的多線程操做數據庫,保證事務同時提交回滾啊,有的文章有點含量,有的就是純屬胡謅,浪費同窗們的感情和時間,不過他們同時給了我一大堆經驗和教訓,從而讓我實現了我認爲有效可行的方案。廢話很少說,直接上代碼

1.首先建立一個自有的多線程執行器,固然你用spring已有的也沒毛病

@Component
public class MyExecutor {

    private static int maximumPoolSize = Runtime.getRuntime().availableProcessors();

    @Bean("/sqlTask")
    public ExecutorService getThreadPool() {
        ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("myThread-handle-%d").get();

        int corePoolSize = 2;
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(500),factory);
    }
}

2.建立一個獲取sqlsession的類,固然直接在你的方法類中實現也能夠java

@Component
public class SqlSessionContext {

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    public SqlSession getSqlSession(){
        SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
        return sqlSessionFactory.openSession();
    }
}

3.service層怎麼注入我就不寫了,這都不會的話直接就不用看了吧git

public Boolean batchAdd() throws Exception {
        boolean success = true;
        
        // 獲取執行器
        ExecutorService service = myExecutor.getThreadPool();

        // 構建對象信息
        User user1 = buildUser(1);
        User user2 = buildUser(2);
        
        List<Callable<Integer>> callable = new ArrayList<>();

        // 獲取數據庫鏈接(內部建立了自有事務)
        SqlSession sqlSession = sqlSessionContext.getSqlSession();
        Connection connection = sqlSession.getConnection();
        // 設置手動提交
        connection.setAutoCommit(false);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        try {
            // 多線程調用
            callable.add(()->userService1.call(user1,userMapper));
            callable.add(()->userService2.call(user2,userMapper));
            List<Future<Integer>> futures = service.invokeAll(callable);

            // 統計執行結果
            int num = 0;
            for (Future<Integer> future : futures) {
                num += future.get();
            }
            // 全部線程執行成功則=2,直接提交,不然回滾
            if (num == 2){
                connection.commit();
            }else {
                success = false;
                connection.rollback();
            }
        }catch (Exception e){
            e.printStackTrace();
            connection.rollback();
            success = false;
        }finally {
            connection.close();
        }
        return success;
    }

4.userService1的實現,userService2和它相似spring

@Component
public class UserService1 {

    Integer call(User user, UserMapper userMapper){

        try {
            userMapper.insert(user);
            Integer.parseInt("aa"); // 手動製造異常,看是否不插入數據
            return 1;
        }catch (Exception e){
            System.out.println(Thread.currentThread().getName()+"failure");
        }
        return 0;
    }
}

看着挺簡單,可是當時設計的時候費了很大的勁,用聲明式事務啊,多線程建立自有事務啊,整了老半天也沒成功。經過看日誌,發如今事務真正提交的時候,sqlsession已經釋放了,而且線程內的事務沒有所有綁定到主事務中,我就想爲什麼我不直接使用sqlsession去管理呢,畢竟最終的插入操做時mybatis控制的,看源碼發現sqlsession是控制mybatis事務的管家,並且mybatis也提供了便於獲取session的SqlSessionTemplate,趁着熱血上頭搞一波,最終還真好使。代碼看上去沒啥問題,併發測試也沒問題,可是沒有通過大數據量,複雜操做的驗證,全部只給各位同窗一點靈感,歡迎踩坑。sql

最後附上源代碼地址:多線程操做數據庫源碼數據庫

相關文章
相關標籤/搜索