Java Transaction full resolution (ii)--Case of failure

Source: Internet
Author: User
Tags parse error

In the previous article in this series, we talked about the basic issues of Java transaction Processing and the service layer and the DAO layer, and in this article we will take Bankservice as an example to learn a case of failed transaction processing.

Bankservice's function is: a user has two accounts, respectively, a bank account and an insurance account, and have their own account number, Bankservice transfer method from the user's bank account to the insurance account transfer, two DAO respectively for two account table access operations.

Define a Bankservice interface as follows:

Package Davenkin;public interface Bankservice {public    void transfer (int fromid, int toid, int amount);}

In two DAO objects, we get connection by passing in the same datasource, and then manipulate the database directly with the API provided by JDBC.

The DAO classes that define the Operations Bank Account table are as follows:

Package Davenkin.step1_failure;import Javax.sql.datasource;import Java.sql.connection;import    Java.sql.preparedstatement;import Java.sql.resultset;import Java.sql.sqlexception;public class FailureBankDao {    Private DataSource DataSource;    Public Failurebankdao (DataSource DataSource) {this.datasource = DataSource; } public void Withdraw (int bankid, int amount) throws SQLException {Connection Connection = Datasource.getconne        Ction (); PreparedStatement selectstatement = connection.preparestatement ("Select Bank_amount from bank_account WHERE BANK_ID =?")        ;        Selectstatement.setint (1, bankid);        ResultSet ResultSet = Selectstatement.executequery ();        Resultset.next ();        int previousamount = Resultset.getint (1);        Resultset.close ();        Selectstatement.close ();        int newamount = Previousamount-amount; PreparedStatement updatestatement = connection.preparestatement ("UPDATE bank_account SET bank_amount =?") WHERE bank_id = ?");        Updatestatement.setint (1, Newamount);        Updatestatement.setint (2, BankID);        Updatestatement.execute ();        Updatestatement.close ();    Connection.close (); }}

Failurebankdao Withdraw method, the amount of amount is removed from the user account in the Bank Account table (Bank_account) account number BankID.

In the same way, the DAO classes that define an insurance account are as follows:

Package Davenkin.step1_failure;import Javax.sql.datasource;import Java.sql.connection;import Java.sql.preparedstatement;import Java.sql.resultset;import Java.sql.sqlexception;public class FailureInsuranceDao    {Private DataSource DataSource;    Public Failureinsurancedao (DataSource DataSource) {this.datasource = DataSource; } public void deposit (int insuranceid, int amount) throws SQLException {Connection Connection = datasource.getc        Onnection (); PreparedStatement selectstatement = connection.preparestatement ("Select Insurance_amount from INSURANCE_ACCOUNT WHERE        insurance_id =? ");        Selectstatement.setint (1, Insuranceid);        ResultSet ResultSet = Selectstatement.executequery ();        Resultset.next ();        int previousamount = Resultset.getint (1);        Resultset.close ();        Selectstatement.close ();        int newamount = Previousamount + amount; PreparedStatement updatestatement = connection.preparestatement ("UPDATE Insurance_accouNT SET Insurance_amount =?        WHERE insurance_id =? ");        Updatestatement.setint (1, Newamount);        Updatestatement.setint (2, Insuranceid);        Updatestatement.execute ();        Updatestatement.close ();    Connection.close (); }}

The deposit method of the Failureinsurancedao class deposits the amount of amount amount into the Insurance Account table (Insurance_account), so that in Bankservice, We can call Failurebankdao's withdraw method to withdraw a certain amount of deposit, and then call Failureinsurancedao deposit method to deposit the deposit into the insurance account table, everything seems OK, Implement the Bankservice interface as follows:

Package Davenkin.step1_failure;import Davenkin. Bankservice;import Javax.sql.datasource;import Java.sql.connection;import Java.sql.sqlexception;public class    Failurebankservice implements bankservice{private Failurebankdao Failurebankdao;    Private Failureinsurancedao Failureinsurancedao;    Private DataSource DataSource;    Public Failurebankservice (DataSource DataSource) {this.datasource = DataSource;        } public void Transfer (int fromid, int toid, int amount) {Connection Connection = null;            try {connection = datasource.getconnection ();            Connection.setautocommit (FALSE);            Failurebankdao.withdraw (Fromid, amount);            Failureinsurancedao.deposit (toid, amount);        Connection.commit ();                } catch (Exception e) {try {assert connection! = NULL;            Connection.rollback ();            } catch (SQLException E1) {e1.printstacktrace (); }} finally {try {assert connection! = NULL;            Connection.close ();            } catch (SQLException e) {e.printstacktrace (); }}} public void Setfailurebankdao (Failurebankdao failurebankdao) {This.failurebankdao = Failurebank    Dao; } public void Setfailureinsurancedao (Failureinsurancedao failureinsurancedao) {This.failureinsurancedao = Failu    Reinsurancedao; }}

In Failurebankservice's transfer method, we first get connection through DataSource and then call Connection.setautocommit (false) to turn on manual commit mode, If all goes well, commit, or rollback if an exception occurs. Next, start testing our bankservice.

To prepare the test data, we define a Bankfixture class, which is responsible for preparing the test data before each test and depositing $1000 into the bank account (1111) and the insurance account (2222) respectively. Bankfixture also provides two helper methods (Getbankamount and Getinsuranceamount) to help us extract data from the database for data validation. We use the in-memory mode of the Hsql database, which makes it easy to test without starting the database server. The Bankfixture class is defined as follows:

Package Davenkin;import org.junit.before;import javax.sql.datasource;import java.sql.*;p ublic class BankFixture{    Protected final DataSource DataSource = Datasourcefactory.createdatasource ();        @Before public void SetUp () throws SQLException {Connection Connection = datasource.getconnection ();        Statement Statement = Connection.createstatement ();        Statement.execute ("DROP TABLE bank_account IF EXISTS");        Statement.execute ("DROP TABLE insurance_account IF EXISTS"); Statement.execute ("CREATE TABLE bank_account (\ n" + "bank_id int,\n" + "Bank_amount int,\n"        + "PRIMARY KEY (bank_id) \ n" + "); Statement.execute ("CREATE TABLE insurance_account (\ n" + "insurance_id int,\n" + "Insurance_        AMOUNT int,\n "+" PRIMARY KEY (insurance_id) \ n "+");        Statement.execute ("INSERT into Bank_account VALUES (1111, 1000);"); StatemEnt.execute ("INSERT into Insurance_account VALUES (2222, 1000);");        Statement.close ();    Connection.close (); } protected int Getbankamount (int bankid) throws SQLException {Connection Connection = Datasource.getconnect        Ion (); PreparedStatement selectstatement = connection.preparestatement ("Select Bank_amount from bank_account WHERE BANK_ID =?")        ;        Selectstatement.setint (1, bankid);        ResultSet ResultSet = Selectstatement.executequery ();        Resultset.next ();        int amount = Resultset.getint (1);        Resultset.close ();        Selectstatement.close ();        Connection.close ();    return amount; } protected int Getinsuranceamount (int insuranceid) throws SQLException {Connection Connection = DataSource.        Getconnection (); PreparedStatement selectstatement = connection.preparestatement ("Select Insurance_amount from INSURANCE_ACCOUNT WHERE        insurance_id =? ");        Selectstatement.setint (1, Insuranceid); REsultset ResultSet = Selectstatement.executequery ();        Resultset.next ();        int amount = Resultset.getint (1);        Resultset.close ();        Selectstatement.close ();        Connection.close ();    return amount; }}

The JUnit tests you write inherit from the Bankfixture class, and the test code is as follows:

Package Davenkin.step1_failure;import Davenkin. Bankfixture;import org.junit.test;import java.sql.sqlexception;import static junit.framework.Assert.assertEquals;    public class Failurebankservicetest extends bankfixture{@Test public void transfersuccess () throws SQLException        {Failurebankdao Failurebankdao = new Failurebankdao (DataSource);        Failureinsurancedao Failureinsurancedao = new Failureinsurancedao (DataSource);        Failurebankservice bankservice = new Failurebankservice (DataSource);        Bankservice.setfailurebankdao (Failurebankdao);        Bankservice.setfailureinsurancedao (Failureinsurancedao);        Bankservice.transfer (1111, 2222, 200);        Assertequals (Getbankamount (1111));    Assertequals (Getinsuranceamount (2222)); } @Test public void Transferfailure () throws SQLException {Failurebankdao Failurebankdao = new Failureban        Kdao (DataSource); Failureinsurancedao Failureinsurancedao = new Failureinsurancedao (datAsource);        Failurebankservice bankservice = new Failurebankservice (DataSource);        Bankservice.setfailurebankdao (Failurebankdao);        Bankservice.setfailureinsurancedao (Failureinsurancedao);        int tononexistid = 3333;        Bankservice.transfer (1111, Tononexistid, 200);        Assertequals (Getinsuranceamount (2222));    Assertequals (Getbankamount (1111)); }}

Run the test, the first Test (transfersuccess) succeeds, and the second Test (transferfailure) fails.

Parse error because: we called Three datasource.getconnection () from Failurebankservice,failurebankdao and Failureinsurancedao respectively, That is, we have created three different connection objects, and Java transactions are on connection, so we have opened three different transactions in three places instead of the same transaction.

The first Test succeeded because no exception occurred during the process. Although the connection commit mode is changed to manual commit in Failurebankservice, the connection in the two DAO is still the default autocommit mode because two DAO uses its own connection object.

In the second Test, we gave a non-existent insurance account ID (tononexistid), just to make the program produce an exception, and then verify in the assertion statement that there were no changes in the two tables, but the test went wrong at the second assertion statement. When an exception occurs, the amount in the bank account has been reduced, while the program has rollback, but the rollback is called connection in Failurebankservice, Instead of Failureinsurancedao in connection, the operation of the insurance account is not executed at all, so the insurance account is still 1000, while the bank account becomes 800.

Therefore, in order for two DAO to be in the same transaction, we should use a connection object throughout the transaction, and in the next article we will talk about how to achieve the transaction by sharing the connection object.

Java Transaction full resolution (ii)--Case of failure

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.