Java Transactions (iii)

Source: Internet
Author: User

Java Transactions (iii)--Implementing distributed transactions on their own

The type of transaction has been mentioned in the previous Java Transaction (ii)-local transaction, and a description of the local transaction has been made. Distributed transactions are accessed and updated across multiple data sources, using JTA (Java Transaction API) for distributed transaction management in Java. However, this article does not show how to use JTA, but instead of relying on other frameworks and jar packages to implement the distributed transaction, as an understanding of distributed transactions.

Suppose you now have two databases, either on a single machine or on a different machine, and now you want to update the user account information to one of the databases, and another to add the user's consumption information. First of all, the distributed transaction is also a transaction, in the transaction characteristics of the blog has explained the four characteristics of the transaction: atomicity, consistency, isolation and persistence, then the distributed transaction must be consistent with these four characteristics, This requires data access and updates to two databases at the same time as a single unit of work to process, and rollback after success or failure. However, when explaining the local transaction has been mentioned, the local transaction is based on the connection, there are now two databases, respectively, to save the data, so in order to achieve this transaction, there will inevitably be two database connections, which seems to be contrary to the transaction-based connection. Now for example: Before going to go to a hospital, and then in the discharge formalities when it is so, the hospital should be discharged when the director of the nurse Station to fill out the discharge list, and then carry the bill to pay the fee and go to the pharmacy to take medicine, then back to the nurse station stamp, discharge formalities completed. If the window of a different location is considered a different connection, the discharge procedure must ensure that the transaction on each business window is successful and that the final discharge procedure is truly complete. In the final seal, it is necessary to check whether the list given in each window has been processed, and only a combination of all the list to determine whether the discharge procedure is successful. This is mainly to illustrate that the key to the implementation of a distributed transaction is to manage the transactions on each connection, using one thing to determine the transaction execution on each connection, and synthesize it as the basis for the successful execution of the distributed transaction. This is probably what the transaction manager is going to do. Although this example is not very appropriate, there is a place to find fault, but in the case of not too much, can still be used to explain what to express.

implementation Examples

I opened two virtual machines, the command for Node1, Node2, each virtual machine installed MySQL database, now to the database on Node1 Update user account information, to the database on the Node2 to add user consumption information.

Create an Account table on Node1, with the following table statements:

CREATE TABLE ACCOUNTS (    ID INT not NULL auto_increment COMMENT ' self-increment primary key ',    customer_no    VARCHAR) NOT null Commen T ' client number ',    customer_name  varchar (+) NOT null COMMENT ' customer name ',    card_id         varchar NOT null COMMENT ' Social Security number ' ,    bank_id         VARCHAR (+) NOT null COMMENT ' bank ID ',    BALANCE      DECIMAL not  null COMMENT ' account balance ',    CURRENCY     VARCHAR (+) not NULL COMMENT ' currency ',    PRIMARY KEY (ID)) COMMENT  = ' Account table ';

Then insert a record into the table, such as:

Create a user consumption history table on Node2, with the following table statements:

CREATE TABLE user_purchase_his (    ID INT not NULL auto_increment COMMENT ' self-increment primary key ',    customer_no VARCHAR) NOT null CO Mment ' client number ',    serial_no   VARCHAR (+) NOT NULL COMMENT ' trade serial number ',    AMOUNT         DECIMAL not     null COMMENT ' transaction amount ',    CURRENCY    varchar (TEN) NOT null COMMENT ' currency ',    REMARK         varchar (+) NOT null COMMENT ' remarks ',    PRIMARY KEY (ID)) COMMENT = ' User consumption history table ';

The following is a humble example, the code is as follows:

1. Create the Dbutil class to get and close the connection

Package Person.lb.example1;import Java.sql.connection;import Java.sql.drivermanager;import java.sql.ResultSet;            Import Java.sql.sqlexception;import Java.sql.statement;public class Dbutil {static {try {//Load driver class        Class.forName ("Com.mysql.jdbc.Driver");        } catch (ClassNotFoundException e) {e.printstacktrace ();        }}//Get database connection on Node1 public static Connection getnode1connection () {Connection conn = null; try {conn = (Connection) drivermanager.getconnection ("Jdbc:mysql://192.168.0.108:3306/test        "," root "," root ");        } catch (SQLException e) {e.printstacktrace ();    } return conn;        }//Get database connection on Node2 public static Connection getnode2connection () {Connection conn = null; try {conn = (Connection) drivermanager.getconnection ("jdbc:mysql://192.168.0.109:3306/tEST "," root "," root ");        } catch (SQLException e) {e.printstacktrace ();    } return conn; }//close connection public static void Close (ResultSet rs, Statement St, Connection conn) {try {if (rs!)            = null) {rs.close ();            } if (st! = null) {st.close ();            } if (conn! = null) {conn.close ();        }} catch (SQLException e) {//TODO auto-generated catch block E.printstacktrace (); }    }}

2. Create a Xademo class to test the transaction

Package Person.lb.example1;import Java.sql.connection;import Java.sql.sqlexception;import java.sql.Statement; public class Xademo {public static void main (string[] args) {//get connection Connection Node1conn = Dbutil.getno        De1connection ();        Connection node2conn = Dbutil.getnode2connection ();            try {//Set connection as non-autocommit node1conn.setautocommit (false);            Node2conn.setautocommit (FALSE);            Update account information Updateaccountinfo (node1conn);            Increase user consumption information adduserpurchaseinfo (node2conn);            Submit Node1conn.commit ();        Node2conn.commit ();            } catch (SQLException e) {e.printstacktrace ();                Rollback try {node1conn.rollback ();            Node2conn.rollback ();            } catch (SQLException E1) {e1.printstacktrace ();            }} finally {//close connection dbutil.close (null, NULL, NODE1CONN); Dbutil.cLose (NULL, NULL, NODE2CONN); }}/** * Update account INFORMATION * @param conn * @throws SQLException */private static void Updateaccountin        FO (Connection conn) throws SQLException {Statement st = conn.createstatement ();    St.execute ("UPDATE ACCOUNTS SET BALANCE = CAST (' 9900.00 ' as DECIMAL" WHERE customer_no = ' 88888888 ' "); }/** * Increase user consumption information * @param conn * @throws SQLException * * * private static void Adduserpurchaseinf        O (Connection conn) throws SQLException {Statement st = conn.createstatement (); St.execute ("INSERT into User_purchase_his (Customer_no, Serial_no, AMOUNT, CURRENCY, REMARK)" + "VALUES ('    88888888 ', ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', +, ' CNY ', ' buy clothes '); }}

This is an example of no exception, and the result is that the value of the Balance field in the Accounts table has been successfully updated to 9900, and a new record has been added to the User_purchase_his table on Node2, and the transactions on the two connections have been completed successfully, NOD1. The transaction target implementation. If the reverse test, change the INSERT statement, one of the values to be inserted to NULL, because the field is non-null limit, so an exception occurs, the transaction on this connection will fail, then the transaction associated with it must be rolled back, node1 the database to make any changes. Tested, the results are consistent with the expected goals. This example is consistent with the transactional nature.

However, this example is poor in terms of both readability and maintainability of the code. When using the Spring development project, the transaction manager is configured, and in our business logic, it is almost imperceptible to transaction control, and the code for transaction control is not visible. So what is the implementation of the transaction control in spring, this blog will not be described in detail, but to mention two things, transaction manager and resource manager, now to implement a simple transaction manager and resource Manager to control the transaction.

The code examples are as follows:

1. Create Abstractdatasource class

Package Person.lb.datasource;import Java.sql.connection;import Java.sql.sqlexception;public abstract class Abstractdatasource {    //Get connection Public    abstract Connection getconnection () throws SQLException;    Close connection public    abstract void Close () throws SQLException;        }

2. Create the Node1datasource class to connect to the database on the Node1

Package Person.lb.datasource;import Java.sql.connection;import Java.sql.drivermanager;import java.sql.SQLException ;p ublic class Node1datasource extends Abstractdatasource {//Use threadlocal class to save connection protected static FINA used by the current thread        L threadlocal<connection> threadsession = new threadlocal<connection> ();        static {try {//Load driver class Class.forName ("Com.mysql.jdbc.Driver");        } catch (ClassNotFoundException e) {e.printstacktrace ();        }} private Final static Node1datasource Node1datasource = new Node1datasource ();    Private Node1datasource () {} public static Node1datasource getinstance () {return node1datasource; }/** * Get connection */@Override public Connection getconnection () throws SQLException {Connection C        Onn = null; if (threadsession.get () = = NULL) {conn = (Connection) drivermanager.getconnection ("jdbc : mysql://192.168.0.108:3306/test "," root "," root ");        Threadsession.set (conn);        } else {conn = Threadsession.get ();    } return conn; /** * Close and remove connection */@Override public void Close () throws SQLException {Connection conn = threadses        Sion.get ();            IF (conn! = null) {conn.close ();        Threadsession.remove (); }    }}

3. Create a Node2datasource class to connect to the database on the Node2 machine

Package Person.lb.datasource;import Java.sql.connection;import Java.sql.drivermanager;import java.sql.SQLException ;p ublic class Node2datasource extends Abstractdatasource {//Use threadlocal class to save connection protected static FINA used by the current thread            L threadlocal<connection> threadsession = new threadlocal<connection> ();        static {try {//Load driver class Class.forName ("Com.mysql.jdbc.Driver");        } catch (ClassNotFoundException e) {e.printstacktrace ();        }} private static final Node2datasource Node2datasource = new Node2datasource ();        Private Node2datasource () {};    public static Node2datasource getinstance () {return node2datasource; }/** * Get connection */@Override public Connection getconnection () throws SQLException {Connection C        Onn = null; if (threadsession.get () = = NULL) {conn = (Connection) drivermanager.getconnection ("jdbc : mysql://192.168.0.109:3306/test "," root "," root ");        Threadsession.set (conn);        } else {conn = Threadsession.get ();    } return conn; /** * Close and remove connection */@Override public void Close () throws SQLException {Connection conn = threadses        Sion.get ();            IF (conn! = null) {conn.close ();        Threadsession.remove (); }    }}

4. Create the Node1dao class to update the account information in the Node1 database

Package Person.lb.dao;import Java.sql.connection;import Java.sql.sqlexception;import java.sql.statement;import Person.lb.datasource.node1datasource;public class Node1dao {    private Node1datasource DataSource = Node1datasource.getinstance ();        /**     * Update account information     * @throws SQLException     *    /public void Updateaccountinfo () throws SQLException {        Connection conn = Datasource.getconnection ();        Statement st = Conn.createstatement ();        St.execute ("UPDATE ACCOUNTS SET BALANCE = CAST (' 9900.00 ' as DECIMAL" WHERE customer_no = ' 88888888 ' ");    }}

5, the creation of Node2dao, on the Node2 machine to increase user consumption information

Package Person.lb.dao;import Java.sql.connection;import Java.sql.sqlexception;import java.sql.statement;import Person.lb.datasource.node2datasource;public class Node2dao {    private Node2datasource DataSource = Node2datasource.getinstance ();        /**     * Increase user consumption information     * @throws SQLException     *    /public void Adduserpurchaseinfo () throws SQLException {        Connection conn = Datasource.getconnection ();        Statement st = Conn.createstatement ();        St.execute ("INSERT into User_purchase_his (Customer_no, Serial_no, AMOUNT, CURRENCY, REMARK)"                + "VALUES (' 88888888 ') , ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', null, ' CNY ', ' buy clothes ');    }

6, create the Nodeservice class, the two operations as a transaction to execute

Package Person.lb.service;import Java.sql.sqlexception;import Person.lb.dao.node1dao;import Person.lb.dao.Node2Dao Import Person.lb.transaction.transactionmanager;public class Nodeservice {public        void execute () {        //Start transaction        Transactionmanager.begin ();                Node1dao Node1dao = new Node1dao ();        Node2dao Node2dao = new Node2dao ();        try {            node1dao.updateaccountinfo ();            Node2dao.adduserpurchaseinfo ();            Commit TRANSACTION            transactionmanager.commit ();        } catch (SQLException e) {            e.printstacktrace ();}}    }

7, the final Test class Testtx

Package Person.lb.test;import Person.lb.service.nodeservice;public class Testtx {public    static void Main (string[] args) {        Nodeservice nodeservice = new Nodeservice ();        Nodeservice.execute ();    }}

The test is consistent with the first example, but the code is more readable and maintainable than the first example. However, this example does not explain the actual principle of the transaction manager and the resource manager in a distributed transaction, nor is it a code that can be used, after all, there are flaws, and the DAO layer needs to throw an exception to implement the rollback of the transaction. I think it's enough as an example to understand the role of distributed transactions.

Java Transactions (iii)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.