Join the social network of Tech Nerds, increase skill rank, get work, manage projects...
 
  • Spring @Transactional Annotation

    • 0
    • 2
    • 2
    • 0
    • 0
    • 0
    • 0
    • 0
    • 569
    Comment on it

    @Transactional Annotation in Spring:- A transaction is unit of work that have ACID (atomicity, consistency, isolation and durability) properties.

    Atomicity:- this means that the changes will completely happens or not. for example If money is debited from an account and credited to another account. the transaction can ensures that either both the debit and credit operation will complete or neither complete.
    Consistency:- Consistent implies that the changes leave the data in a consistent state.
    Isolation:-Isolated implies that changes do not interfere with other changes.
    Durability:-Durable implies that once the changes are committed, they stay committed.

    We can handle Declarative Transaction using Spring.

    spring-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <tx:annotation-driven transaction-manager="transactionManager" />
    
    <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/manish" />
    <property name="username" value="root" />
    <property name="password" value="root" />
    </bean>
    
    <bean id="accountService"
    class="com.evon.AccountService">
    <property name="accountDao" ref="accountDao" />
    </bean>
    
    <bean id="accountDao"
    class="com.evon.AccountDao">
    <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
    </bean>
    
    
    
    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
    </bean>
    
    </beans>
    

    Account.java

    package com.evon;
    
    public class Account {
    
    private String number;
    private Double balance;
    
    public Account() {
    
    }
    
    public Account(String number, Double initialBalance) {
    super();
    this.number = number;
    this.balance = initialBalance;
    }
    
    public String getNumber() {
    return number;
    }
    
    public void setNumber(String number) {
    this.number = number;
    }
    
    public Double getBalance() {
    return balance;
    }
    
    public void setBalance(Double amount) {
    this.balance = amount;
    }
    
    public void debit(Double debitAmount){
    this.balance = this.balance - debitAmount;
    }
    
    public void credit(Double creditAmount) {
    this.balance = this.balance + creditAmount;
    }
    
    @Override
    public String toString() {
    return "Account [number=" + number + ", balance=" + balance + "]";
    }
    
    }
    

    AccountDao.java

    package com.evon;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.springframework.jdbc.core.RowMapper;
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    public class AccountDao extends JdbcDaoSupport{
    
    public void insert(Account account){
    String insertSql ="INSERT INTO ACCOUNT (ACCOUNT_NUMBER, BALANCE) VALUES(?,?);";
    String accountNumber = account.getNumber();
    Double amount = account.getBalance();
    
    getJdbcTemplate().update(insertSql,new Object[]{accountNumber,amount});
    
    }
    
    public void update(Account account){
    String updateSql ="UPDATE ACCOUNT SET BALANCE = ? where ACCOUNT_NUMBER = ?;";
    Double amount = account.getBalance();
    String accountNumber = account.getNumber();
    
    getJdbcTemplate().update(updateSql,new Object[]{amount,accountNumber});
    
    }
    
    public Account select(String accountNumber) {
    String selectSql = "SELECT * FROM ACCOUNT WHERE ACCOUNT_NUMBER = ?;";
    List<Account> accounts = getJdbcTemplate().query(selectSql, new Object[]{accountNumber},new AccountRowMapper());
    return accounts.get(0);
    }
    
    private class AccountRowMapper implements RowMapper<Account>{
    
    @Override
    public Account mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
    Account account = new Account();
    account.setNumber(resultSet.getString("ACCOUNT_NUMBER"));
    account.setBalance(resultSet.getDouble("BALANCE"));
    return account;
    }
    
    }
    
    }
    

    AccountService.java

    package com.evon;
    
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
    public class AccountService {
    
    private AccountDao accountDao;
    
    public AccountDao getAccountDao() {
    return accountDao;
    }
    
    public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
    }
    
    public void create(Account account) {
    getAccountDao().insert(account);
    }
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public void transferFunds(final Account fromAccount,
    final Account toAccount, final Double transferAmount) {
    fromAccount.debit(transferAmount);
    toAccount.credit(transferAmount);
    getAccountDao().update(fromAccount);
    getAccountDao().update(toAccount);
    }
    
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false, rollbackFor=Exception.class)
    public void transferFundsException(final Account fromAccount,
    final Account toAccount, final Double transferAmount) throws Exception {
    fromAccount.debit(transferAmount);
    toAccount.credit(transferAmount);
    getAccountDao().update(fromAccount);
    getAccountDao().update(toAccount);
    // Simulate an exception that occurs during funds transfer
    throw new Exception();
    }
    
    public void createAccount(Account account) {
    getAccountDao().insert(account);
    }
    
    public Account getAccount(String accountNumber) {
    return getAccountDao().select(accountNumber);
    }
    }
    

    main.java

    package com.evon;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestSpringDeclarativeTransactionsAnnotation {
    private static final String fromAccountNumber = "ACC01";
    private static final String toAccountNumber = "ACC02";
    
    private static void printAccountInformation(AccountService accountService){
    
    Account fromAccount = accountService.getAccount(fromAccountNumber);
    Account toAccount = accountService.getAccount(toAccountNumber);
    System.out.println("Balance in account " + fromAccountNumber + " = " + fromAccount.getBalance());
    System.out.println("Balance in account " + toAccountNumber + " = " + toAccount.getBalance());
    }
    
    public static void main(String[] args) {
    System.out.println("************** START PROGRAM EXECUTION **************");
    
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    AccountService accountService = (AccountService) context.getBean("accountService");
    
    System.out.println("Creating new accounts " + fromAccountNumber + " and " + toAccountNumber);
    Account fromAccount = new Account(fromAccountNumber,100d);
    Account toAccount = new Account(toAccountNumber,200d);
    accountService.create(fromAccount);
    accountService.create(toAccount);
    printAccountInformation(accountService);
    System.out.println("New accounts created successfully");
    System.out.println("----");
    
    Double transferAmount = 50d;
    System.out.println("Transferring " + transferAmount + " from account " + fromAccountNumber + " to account " + toAccountNumber);
    accountService.transferFunds(fromAccount, toAccount, transferAmount);
    printAccountInformation(accountService);
    System.out.println("The amount " + transferAmount + " was transferred successfully");
    System.out.println("----");
    
    transferAmount = 10d;
    System.out.println("Transferring " + transferAmount + " from account " + fromAccountNumber + " to account " + toAccountNumber);
    try {
    accountService.transferFundsException(fromAccount, toAccount, transferAmount);
    } catch (Exception e) {
    System.out.println("ERROR IN TRANSACTION. THE AMOUNT "
    + transferAmount
    + " COULD NOT BE TRANSFERRED DUE TO EXCEPTION.");
    System.out.println("THE TRANSACTION  ROLLED BACK.");
    }
    printAccountInformation(accountService);
    System.out.println("The transfer of funds failed and the account balance remained unchanged");
    
    System.out.println("************** END PROGRAM EXECUTION **************");
    }
    
    
    }
    

    The point to notice here is that the transaction is declarative managed using annotations using the tx:annotation-driven tag that indicates to Spring that transactions are managed using annotations by @Transactional annotation.

    The transaction manager is defined using DataSourceTransactionManager class .

    The database parameters defined for dataSource bean correspond to the MySQL in-memory database.

    To add declarative transaction management to the above method use the following steps:

    Step 1: Define a transaction manager in spring-context.xml

    <bean id="txManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
    

    Step 2: Enable support for transaction annotations

    <tx:annotation-driven transaction-manager="txManager"/>
    

    Step 3: Add the @Transactional annotation to the transferFunds method

        @Transactional(propagation=Propagation.REQUIRED, readOnly=false, rollbackFor=Exception.class)
      public void transferFunds(final Account fromAccount,
    final Account toAccount, final Double transferAmount) {
    fromAccount.debit(transferAmount);
    toAccount.credit(transferAmount);
    getAccountDao().update(fromAccount);
    getAccountDao().update(toAccount);
    }
    

    @Transactional can take properties but we will go with default values which are:

    Propagation : Required

    Required means a transaction is required. If there is no transaction, the transaction manager have to start one. The other possible values is Requires_New, which tells the transaction manager to always suspend the existing transaction and start a new one.

    Isolation level : Default

    Use the default isolation level of the underlying resource manager.

    Rollback : Any runtime exception triggers a rollback

 0 Comment(s)

Sign In
                           OR                           
                           OR                           
Register

Sign up using

                           OR                           
Forgot Password
Fill out the form below and instructions to reset your password will be emailed to you:
Reset Password
Fill out the form below and reset your password: