@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
最後附上源代碼地址:多線程操做數據庫源碼數據庫