Spring's declarative transaction implementation class

  Native jdbc transaction management is relatively complicated, requiring manual commit and rollback, but also a bunch of try-catch. The students know the familiar spring, spring uses a declarative approach to transaction management services, the transaction management change very simple. Spring transaction is very strong, I only used here to simulate a few simple jdbc property.

  1. declarative transaction design

  Declarative transaction based primarily on java dynamic proxy implementation

  By Connection stored in a ThreadLocal variable to solve concurrency issues. Spring is also the underlying use ThreadLocal.

  By recording Connection creator, to solve the problem of nested transactions.

  Custom annotation @EnableTranscation: the method used to indicate whether the transaction open

  Service Factory: create a container used to simulate the Spring Bean course, if you are using a modified method @EnableTranscation Service included, Service proxy object is created, otherwise Service instance

  Custom Dao, Connection can not be created directly, you need to get hold of the current thread Connection.

  2. Connection Pool Management

  It used c3p0 connection pool to connect to the database.

  2.1 c3po Configuration

  root

  root

  com.mysql.jdbc.Driver

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

  10

  5

  5

  50

  100

  10

  2.2 Database Connectivity Tools

  Package and method of database connection resource database connections closed

  public class DbConnUtil {

  // c3P0 configuration name

  private static final String c3p0PoolName = "myC3p0Pool";

  // configuration data source

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

  // Configure the local connection

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

  / ** database connection

  * @Param whether autoCommitTx open offer to commit the transaction

  * @Return Connection database 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: get this thread connection

  * @Return: Connection Database Connection

  * @author: zongf

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

  * @since 1.0

  */

  public static TxConnection getTxConnection() {

  TxConnection txConnection = null;

  // If ThreadLocal connection is empty, create a new connection

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

  txConnection = new TxConnection(getConnection(true));

  txConnectionLocal.set(txConnection);

  } else {

  txConnection = txConnectionLocal.get();

  }

  return txConnection;

  }

  / ** database connection in the current thread

  * @return Connection

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static Connection getLocalConnection() {

  return getTxConnection().getConnection();

  }

  / ** database connection object for the current thread

  * @return ThreadLocal

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public static ThreadLocal getLocalTxConnection() {

  return txConnectionLocal;

  }

  / ** When the return connection, you need to set auto-commit to 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 Transaction defined connection object

  Because nested transaction, which one needs to follow a dynamic proxy open transaction is rolled back by the opening and which layer dynamic agent in charge of affairs, and therefore need to record transactions opener. So I created a TxConnneciton object.

  public class TxConnection {

  private Connection connection;

  private String creator;

  // omitted setter / getter method

  }

  3. Customize the Transaction open comment

  The definition of a similar spring @Transcation comment for opening transactions.

  Propagation.REQUIRES_NEW openNewTx one of seven for the propagation of transaction simulation of spring

  @Documented

  @Target(ElementType.METHOD)

  @Retention(RetentionPolicy.RUNTIME)

  public @interface EnableTranscation {

  // whether to open a new transaction

  boolean openNewTx() default false;

  }

  4. The dynamic proxy processor

  / ** Affairs dynamic proxy processor

  * @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 {

  // Get the current database connection

  TxConnection txConnection = DbConnUtil.getTxConnection();

  // Save the old connection objects

  TxConnection oldTxConnection = null;

  try {

  // Get the target object methods

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

  // method to see whether the current open transaction

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

  // if open transactions, current connection is set to manual commit the transaction

  if (enableTx) {

  // Get the information notes

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

  // Get whether to open a new transaction

  boolean openNewTx = annotation.openNewTx();

  if (! txConnection.getConnection (). getAutoCommit ()) {// false, indicating that the transaction has been opened

  if (openNewTx) {// If you need to open the transaction

  // Save the original database connection

  oldTxConnection = txConnection;

  // Get a new connection

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

  // replace the database connection in the current thread

  DbConnUtil.getLocalTxConnection().set(txConnection);

  }

  } Else {// true, it indicates that the transaction is not turned on

  // no open transactions, set auto-commit to false. Said it has started business

  txConnection.getConnection().setAutoCommit(false);

  txConnection.setCreator(this.toString());

  }

  }

  // execute the target method

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

  // If the transaction is the current handler object is created, then commit the transaction

  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 ( "exception occurs, the transaction has been rolled back!", e);

  } finally {

  // release the database connection

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

  DbConnUtil.release(txConnection.getConnection());

  }

  // If the new connection is not null, it means opening a new business. Rollback original connection

  if (oldTxConnection != null) {

  DbConnUtil.getLocalTxConnection().set(oldTxConnection);

  }

  }

  }

  }

  5. ServiceFactory Factory

  Creating Service factory class, used to simulate the Spring container. When the target Service included @EnableTransaction notes, create a dynamic proxy Service, or the creation Service object.

  / ** Service plant simulation spring containers

  * @since 1.0

  * @author zongf

  * @created 2019-07-18

  */

  public class ServiceFactory {

  / ** Gets Service instances

  * @Param clz Service implementation class type

  * @Return T Service objects or dynamic proxy object

  * @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 ( "Failed to create object");

  }

  // judgments can not be an interface, the interface implementation class can not be created

  if(clz.isInterface()){

  throw new RuntimeException ( "interface can not create an instance!");

  }

  // whether to open dynamic proxies

  boolean enableTx = false;

  // loop through all non-private methods, if the method has @EnableTx notes, then you need to create a proxy

  Method[] methods = clz.getMethods();

  for (Method method : methods) {

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

  enableTx = true;

  break;

  }

  }

  // If you need to create a proxy, the proxy object is returned

  if (enableTx) {

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

  }

  return t;

  }

  }

  6. declarative transaction test

  Test, written before the author BaseDao to simplify development by means of basic steps.

  1.1 Definition Interface

  public interface IMixService {

  // simulate normal

  void success();

  // simulate abnormal operation, transaction rollback

  void error();

  void show();

  }

  6.2 define the implementation class

  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"));

  } Wuxi Women's Hospital 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"));

  // simulate abnormal Hall

  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 Test Case

  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"));

  // simulate abnormal Hall

  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);

  }

  }


Guess you like

Origin blog.51cto.com/14335413/2433858