中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

實現類Spring聲明式事務

發布時間:2020-07-12 19:41:56 來源:網絡 閱讀:377 作者:nineteens 欄目:編程語言

  原生的jdbc 對事務管理也是比較繁瑣的, 需要手工進行提交和回滾, 還要一堆try-catch. 而熟悉spring 的同學都知道, spring采用了聲明式事務方式來管理事務, 使事務管理變得很簡單. Spring 事務很強大, 筆者這里僅使用jdbc 來模擬簡單的幾個屬性.

  1. 聲明式事務方案設計

  聲明式事務主要依據java 動態代理實現

  通過將Connection 存放在ThreadLocal 變量中, 來解決并發問題. Spring 底層也是用的ThreadLocal.

  通過記錄Connection 的創建者, 來解決事務的嵌套問題.

  自定義注解@EnableTranscation: 用于標明方法是否開啟事務

  Service工廠: 用于模擬Spring容器創建Bean過程, 如果Service 中包含使用@EnableTranscation修飾的方法, 則創建Service的代理對象, 否則返回Service 實例

  自定義Dao時, 不能直接創建Connection, 需要獲取當前線程中保存的Connection.

  2. 連接池管理

  筆者對數據庫的連接采用c3p0 連接池.

  2.1 c3po 配置

  root

  root

  com.mysql.jdbc.Driver

  jdbc:mysql://localhost:3306/learn-jdbc?characterEncoding=UTF-8

  10

  5

  5

  50

  100

  10

  2.2 數據庫連接工具類

  封裝獲取數據庫連接和關閉數據庫連接資源的方法

  public class DbConnUtil {

  // c3P0配置名

  private static final String c3p0PoolName = "myC3p0Pool";

  // 配置數據源

  private static final DataSource dataSource = new ComboPooledDataSource(c3p0PoolName);

  // 配置本地連接

  private static ThreadLocal txConnectionLocal = new ThreadLocal<>();

  /** 獲取數據庫連接

  * @param autoCommitTx 是否開啟提供提交事務

  * @return Connection 數據庫連接

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static Connection getConnection(boolean autoCommitTx) {

  try {

  Connection connection = dataSource.getConnection();

  connection.setAutoCommit(autoCommitTx);

  return connection;

  } catch (SQLException e) {

  e.printStackTrace();

  }

  return null;

  }

  /**

  * @Description: 獲取本線程連接

  * @return: Connection 數據庫連接

  * @author: zongf

  * @time: 2019-06-26 14:37:00

  * @since 1.0

  */

  public static TxConnection getTxConnection() {

  TxConnection txConnection = null;

  // 如果ThreadLocal 中連接為空, 則創建新的連接

  if (txConnectionLocal.get() == null || txConnectionLocal.get().getConnection() == null) {

  txConnection = new TxConnection(getConnection(true));

  txConnectionLocal.set(txConnection);

  } else {

  txConnection = txConnectionLocal.get();

  }

  return txConnection;

  }

  /** 獲取當前線程內的數據庫連接

  * @return Connection

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static Connection getLocalConnection() {

  return getTxConnection().getConnection();

  }

  /** 獲取當前線程的數據庫連接對象

  * @return ThreadLocal

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static ThreadLocal getLocalTxConnection() {

  return txConnectionLocal;

  }

  /** 當歸還連接時, 需要設置自動提交事務為true.

  * @param connection

  * @return null

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static void release(Connection connection) throws SQLException {

  try {

  if (connection != null && !connection.isClosed()) {

  connection.setAutoCommit(true);

  }

  } catch (SQLException e) {

  e.printStackTrace();

  } finally {

  connection.close();

  }

  }

  }

  2.3 定義事務連接對象

  由于在事務嵌套時, 需要遵循哪一層動態代理開啟的事務, 由哪一層動態代理負責事務的開啟和回滾, 因此需要記錄事務的開啟者. 因此筆者創建了一個TxConnneciton 對象.

  public class TxConnection {

  private Connection connection;

  private String creator;

  // 省略setter/getter 方法

  }

  3. 自定義開啟事務注解

  定義一個類似于spring @Transcation 的注解, 用于開啟事務.

  openNewTx 用于模擬spring七種事務傳播行為之一的 Propagation.REQUIRES_NEW

  @Documented

  @Target(ElementType.METHOD)

  @Retention(RetentionPolicy.RUNTIME)

  public @interface EnableTranscation {

  // 是否開啟新的事務

  boolean openNewTx() default false;

  }

  4. 動態代理處理器

  /**事務動態代理處理器

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public class TranscationHandler implements InvocationHandler {

  private Object target;

  public TranscationHandler(Object target) {

  this.target = target;

  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  // 獲取當前數據庫連接

  TxConnection txConnection = DbConnUtil.getTxConnection();

  // 保存老的連接對象

  TxConnection oldTxConnection = null;

  try {

  // 獲取目標對象方法

  Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());

  // 看當前方法是否開啟了事務

  boolean enableTx = targetMethod.isAnnotationPresent(EnableTranscation.class);

  // 如果開啟事務, 則設置當前連接為手動提交事務

  if (enableTx) {

  // 獲取注解信息

  EnableTranscation annotation = targetMethod.getAnnotation(EnableTranscation.class);

  // 獲取是否開啟新事務

  boolean openNewTx = annotation.openNewTx();

  if (!txConnection.getConnection().getAutoCommit()) { //為false, 表示已經開啟了事務

  if (openNewTx) { // 如果需要開啟的事務

  // 保存原數據庫連接

  oldTxConnection = txConnection;

  // 獲取新的連接

  txConnection = new TxConnection(DbConnUtil.getConnection(false), this.toString());

  // 替換當前線程中的數據庫連接

  DbConnUtil.getLocalTxConnection().set(txConnection);

  }

  } else { // 為true, 表示未開啟事務

  // 沒有開啟事務, 設置自動提交為false. 表示已經開始了事務

  txConnection.getConnection().setAutoCommit(false);

  txConnection.setCreator(this.toString());

  }

  }

  // 執行目標方法

  Object object = targetMethod.invoke(this.target, args);

  // 如果事務是當前handler對象創建, 那么提交事務

  if (this.toString().equals(txConnection.getCreator())) {

  txConnection.getConnection().commit();

  }

  return object;

  } catch (Exception e) {

  if (txConnection != null && this.toString().equals(txConnection.getCreator())) {

  if (txConnection.getConnection() != null && !txConnection.getConnection().isClosed()) {

  txConnection.getConnection().rollback();

  txConnection.getConnection().setAutoCommit(true);

  }

  }

  throw new RuntimeException("發生異常, 事務已回滾!", e);

  } finally {

  // 釋放數據庫連接

  if (txConnection != null && this.toString().equals(txConnection.getCreator())) {

  DbConnUtil.release(txConnection.getConnection());

  }

  // 如果新連接不為null, 則表示開啟了新事務. 則回滾原連接

  if (oldTxConnection != null) {

  DbConnUtil.getLocalTxConnection().set(oldTxConnection);

  }

  }

  }

  }

  5. ServiceFactory 工廠

  創建Service 工廠類, 用于模擬Spring 容器. 當目標Service中包含@EnableTransaction 注解時, 創建Service 的動態代理, 否則創建Service 對象.

  /** Service工廠, 模擬spring 容器

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public class ServiceFactory {

  /** 獲取Service 實例

  * @param clz Service 實現類類型

  * @return T Service 對象或動態代理對象

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static T getService(Class clz) {

  T t = null;

  try {

  t = clz.newInstance();

  } catch (Exception e) {

  e.printStackTrace();

  throw new RuntimeException("創建對象失敗");

  }

  // 判斷不能是接口, 接口不能創建實現類

  if(clz.isInterface()){

  throw new RuntimeException("接口不能創建實例!");

  }

  // 是否開啟動態代理

  boolean enableTx = false;

  // 遍歷所有非私有方法, 如果方法有@EnableTx注解, 則說明需要創建代理

  Method[] methods = clz.getMethods();

  for (Method method : methods) {

  if (method.getAnnotation(EnableTranscation.class) != null) {

  enableTx = true;

  break;

  }

  }

  // 如果需要創建代理, 則返回代理對象

  if (enableTx) {

  return (T) Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new TranscationHandler(t));

  }

  return t;

  }

  }

  6. 聲明式事務測試

  測試用例, 筆者借助于之前寫的BaseDao來簡化基本步驟的開發.

  1.1 定義接口

  public interface IMixService {

  // 模擬正常

  void success();

  // 模擬異常操作, 事務回滾

  void error();

  void show();

  }

  6.2 定義實現類

  public class MixService implements IMixService {

  private IUserService userService = ServiceFactory.getService(UserService.class);

  private IPersonService personService = ServiceFactory.getService(PersonService.class);

  @EnableTranscation

  @Override

  public void success() {

  this.userService.save(new UserPO("user-01", "123456"));

  this.personService.save(new PersonPO("person-01", "abcdefg"));

  }無錫婦科醫院 http://www.bhnnk120.com/

  @EnableTranscation

  @Override

  public void error() {

  this.userService.save(new UserPO("user-01", "123456"));

  this.personService.save(new PersonPO("person-01", "abcdefg"));

  // 模擬異常會館

  int a = 1/0;

  }

  @Override

  public void show() {

  List userPOS = this.userService.queryAll();

  List personPOS = this.personService.queryAll();

  System.out.println("\n****** t_user: *****");

  userPOS.forEach(System.out::println);

  System.out.println("\n****** t_person: *****");

  personPOS.forEach(System.out::println);

  }

  }

  6.3 測試用例

  public class MixService implements IMixService {

  private IUserService userService = ServiceFactory.getService(UserService.class);

  private IPersonService personService = ServiceFactory.getService(PersonService.class);

  @EnableTranscation

  @Override

  public void success() {

  this.userService.save(new UserPO("user-01", "123456"));

  this.personService.save(new PersonPO("person-01", "abcdefg"));

  }

  @EnableTranscation

  @Override

  public void error() {

  this.userService.save(new UserPO("user-01", "123456"));

  this.personService.save(new PersonPO("person-01", "abcdefg"));

  // 模擬異常會館

  int a = 1/0;

  }

  @Override

  public void show() {

  List userPOS = this.userService.queryAll();

  List personPOS = this.personService.queryAll();

  System.out.println("\n****** t_user: *****");

  userPOS.forEach(System.out::println);

  System.out.println("\n****** t_person: *****");

  personPOS.forEach(System.out::println);

  }

  }


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

三门县| 宝山区| 藁城市| 金阳县| 通州区| 九寨沟县| 潼关县| 南川市| 微山县| 乌拉特中旗| 九龙城区| 玉门市| 宁阳县| 墨玉县| 长兴县| 加查县| 洛川县| 临猗县| 彰武县| 襄城县| 通江县| 聂荣县| 辛集市| 怀来县| 集安市| 沁水县| 枣强县| 朝阳县| 藁城市| 沙河市| 大英县| 钟祥市| 新民市| 河北区| 茶陵县| 东宁县| 军事| 彝良县| 南京市| 隆德县| 绍兴市|