diff options
Diffstat (limited to 'spring-framework/07-bank-jdbc-ds-transaction')
12 files changed, 508 insertions, 0 deletions
diff --git a/spring-framework/07-bank-jdbc-ds-transaction/.gitignore b/spring-framework/07-bank-jdbc-ds-transaction/.gitignore new file mode 100644 index 0000000..ec4e05e --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/.gitignore @@ -0,0 +1,9 @@ +# Eclipse +bin +.settings +.metadata +.classpath +.project + +# Maven +target diff --git a/spring-framework/07-bank-jdbc-ds-transaction/pom.xml b/spring-framework/07-bank-jdbc-ds-transaction/pom.xml new file mode 100644 index 0000000..6a3307d --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/pom.xml @@ -0,0 +1,42 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.example.spring</groupId> + <artifactId>base-config</artifactId> + <version>0.0.1-SNAPSHOT</version> + <relativePath>../00-config</relativePath> + </parent> + + <artifactId>bank-jdbc-ds-transaction</artifactId> + <packaging>jar</packaging> + + <dependencies> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-jdbc</artifactId> + <version>${springVersion}</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-dbcp2</artifactId> + <version>${commonsDBCP2Version}</version> + <exclusions> + <exclusion> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>org.mariadb.jdbc</groupId> + <artifactId>mariadb-java-client</artifactId> + <version>${mariadbClientVersion}</version> + </dependency> + </dependencies> + +</project> diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/App.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/App.java new file mode 100644 index 0000000..1060f2d --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/App.java @@ -0,0 +1,11 @@ +package com.example.spring; + +import com.example.spring.bank.BankApp; + +public class App { + public static void main(String[] args) { + // Actually, BankApp is the class with the main method. + // It should have been executed directly instead of this class. + BankApp.main(args); + } +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/Account.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/Account.java new file mode 100644 index 0000000..6222af8 --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/Account.java @@ -0,0 +1,21 @@ +package com.example.spring.bank; + +import java.math.BigDecimal; + +public class Account { + private int accountNumber; + private BigDecimal balance; + + public int getAccountNumber() { + return accountNumber; + } + public void setAccountNumber(int accountNumber) { + this.accountNumber = accountNumber; + } + public BigDecimal getBalance() { + return balance; + } + public void setBalance(BigDecimal balance) { + this.balance = balance; + } +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManager.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManager.java new file mode 100644 index 0000000..f299d60 --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManager.java @@ -0,0 +1,21 @@ +package com.example.spring.bank; + +import java.math.BigDecimal; + +public interface AccountManager { + + public Account create(); + + public Account find(int accountNumber); + + public Account deposit(int accountNumber, BigDecimal amount); + public Account withdraw(int accountNumber, BigDecimal amount); + + public void delete(int accountNumber); + + /** Returns the first account */ + public Account transfer(int accountNumber1, int accountNumber2, BigDecimal amount); + + public void chargeForLowBalance(BigDecimal minimumBalance, BigDecimal amount); + +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManagerImpl.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManagerImpl.java new file mode 100644 index 0000000..39dc63e --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/AccountManagerImpl.java @@ -0,0 +1,73 @@ +package com.example.spring.bank; + +import java.math.BigDecimal; +import java.util.Iterator; +import java.util.List; + +import com.example.spring.bank.dao.AccountDAO; + +public class AccountManagerImpl implements AccountManager { + + private AccountDAO accountDAO; + + public void setAccountDAO(AccountDAO accountDAO) { + this.accountDAO = accountDAO; + } + + @Override + public void chargeForLowBalance(BigDecimal minimumBalance, BigDecimal amount) { + List<Account> accounts = accountDAO.findAccountsWithLowBalance(minimumBalance); + for (Iterator<Account> iterator = accounts.iterator(); iterator.hasNext();) { + Account account = (Account) iterator.next(); + // Check if the balance will go beyond 0. If yes, set the balance to 0 + account.setBalance(account.getBalance().subtract(amount)); + accountDAO.update(account); + } + } + + @Override + public Account create() { + return accountDAO.createAccount(); + } + + @Override + public Account find(int accountNumber) { + return accountDAO.getAccount(accountNumber); + } + + @Override + public void delete(int accountNumber) { + accountDAO.delete(accountNumber); + } + + @Override + public Account deposit(int accountNumber, BigDecimal amount) { + Account account = accountDAO.getAccount(accountNumber); + account.setBalance(account.getBalance().add(amount)); + accountDAO.update(account); + + return account; + } + + @Override + public Account withdraw(int accountNumber, BigDecimal amount) { + Account account = accountDAO.getAccount(accountNumber); + account.setBalance(account.getBalance().subtract(amount)); + accountDAO.update(account); + + return account; + } + + @Override + public Account transfer(int accountNumber1, int accountNumber2, BigDecimal amount) { + Account account1 = accountDAO.getAccount(accountNumber1); + account1.setBalance(account1.getBalance().subtract(amount)); + accountDAO.update(account1); + + Account account2 = accountDAO.getAccount(accountNumber2); + account2.setBalance(account2.getBalance().add(amount)); + accountDAO.update(account2); + + return account1; + } +}
\ No newline at end of file diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/BankApp.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/BankApp.java new file mode 100644 index 0000000..26b6eaf --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/BankApp.java @@ -0,0 +1,65 @@ +package com.example.spring.bank; + +import java.math.BigDecimal; + +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class BankApp { + + /** + * @param args + */ + public static void main(String[] args) { + + AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] {"classpath:META-INF/spring/applicationContext.xml"}); + + // Register a shutdown hook with the JVM runtime, closing this context on JVM shutdown unless it has already been closed at that time. + // Delegates to doClose() for the actual closing procedure. + // This will trigger the destroy-method of the DataSource in our application (for example, in case of unexpected JVM shutdown). + ctx.registerShutdownHook(); + + AccountManager am = ctx.getBean("accountManager", AccountManager.class); + + // Create an account + System.out.println("Creating a new account..."); + Account account = am.create(); + + System.out.println("Account number: " + account.getAccountNumber()); + System.out.println("Balance: " + account.getBalance()); + + // Deposit some amount + System.out.println("Depositing 200.0..."); + account = am.deposit(account.getAccountNumber(), new BigDecimal("200.0")); + System.out.println("New balance: " + account.getBalance()); + + +// am.chargeForLowBalance(150, 10); + + System.out.println("Withdrawing 50.0..."); + account = am.withdraw(account.getAccountNumber(), new BigDecimal("50.0")); + System.out.println("New balance: " + account.getBalance()); + + // Create another account + Account account2 = am.create(); + // Try transfer + System.out.println("Trying transfer, 1st account balance: " + account.getBalance()); + account = am.transfer(account.getAccountNumber(), account2.getAccountNumber(), new BigDecimal("10.00")); + System.out.println("After transfer, 1st account balance: " + account.getBalance()); + + + System.out.println("Trying transfer to possibly non-existing account, 1st account balance: " + account.getBalance()); + try { + account = am.transfer(account.getAccountNumber(), 5000, new BigDecimal("10.00")); + } catch (Exception e) { + System.err.println("An error occurred!"); + e.printStackTrace(); + } + + account = am.find(account.getAccountNumber()); + System.out.println("After transfer, 1st account balance: " + account.getBalance()); + + ctx.close(); + } + +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/AccountDAO.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/AccountDAO.java new file mode 100644 index 0000000..735aed2 --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/AccountDAO.java @@ -0,0 +1,15 @@ +package com.example.spring.bank.dao; + +import java.math.BigDecimal; +import java.util.List; + +import com.example.spring.bank.Account; + +public interface AccountDAO { + public Account createAccount(); + public Account getAccount(int accountNumber); + public void update(Account account); + public void delete(int accountNumber); + public List<Account> findAllAccounts(); + public List<Account> findAccountsWithLowBalance(BigDecimal lessThanAmount); +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/jdbc/JDBCAccountDAO.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/jdbc/JDBCAccountDAO.java new file mode 100644 index 0000000..d285a89 --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/dao/jdbc/JDBCAccountDAO.java @@ -0,0 +1,94 @@ +package com.example.spring.bank.dao.jdbc; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import javax.sql.DataSource; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import com.example.spring.bank.Account; +import com.example.spring.bank.dao.AccountDAO; + +public class JDBCAccountDAO implements AccountDAO { + + private JdbcTemplate jdbcTemplate; + + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + + @Override + public Account createAccount() { +// Connection con = dataSource.getConnection(); +// PreparedStatement stmt = con.prepareStatement("...", autoGeneratedKeys); +// stmt.execute(); +// con.close(); + + + final String INSERT_SQL = "insert into account (balance) values(0)"; + + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update( + new PreparedStatementCreator() { + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = + connection.prepareStatement(INSERT_SQL, new String[] {"account_number"}); + // ps.setString(1, name); + return ps; + } + }, keyHolder); + + Account account = new Account(); + // keyHolder.getKey() now contains the generated key + account.setAccountNumber(keyHolder.getKey().intValue()); + account.setBalance(new BigDecimal("0.0")); + + return account; + } + + @Override + public void delete(int accountNumber) { + jdbcTemplate.update("delete from account where account_number = ?", Integer.valueOf(accountNumber)); + } + + @Override + public List<Account> findAccountsWithLowBalance(BigDecimal lessThanAmount) { + return this.jdbcTemplate.query( "select * from account where balance < ?", new Object[] {lessThanAmount.doubleValue()}, new AccountMapper()); + } + + @Override + public List<Account> findAllAccounts() { + return this.jdbcTemplate.query( "select * from account", new AccountMapper()); + } + + @Override + public Account getAccount(int accountNumber) { + return this.jdbcTemplate.queryForObject( "select * from account where account_number = ?", new Object[] {accountNumber}, new AccountMapper()); + } + + @Override + public void update(Account account) { + this.jdbcTemplate.update("update account set balance = ? where account_number = ?", account.getBalance(), account.getAccountNumber()); + } + + + private static final class AccountMapper implements RowMapper<Account> { + + public Account mapRow(ResultSet rs, int rowNum) throws SQLException { + Account account = new Account(); + account.setAccountNumber(rs.getInt("account_number")); + account.setBalance(rs.getBigDecimal("balance")); + return account; + } + } +}
\ No newline at end of file diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/tx/TxAccountManager.java b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/tx/TxAccountManager.java new file mode 100644 index 0000000..a5b310d --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/java/com/example/spring/bank/tx/TxAccountManager.java @@ -0,0 +1,109 @@ +package com.example.spring.bank.tx; + +import java.math.BigDecimal; + +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import com.example.spring.bank.Account; +import com.example.spring.bank.AccountManager; + +/** + * This class decorates a given AccountManager by adding transaction boundaries. + * + * @author kamal + * + */ +public class TxAccountManager implements AccountManager { + + private AccountManager accountManager; + + public void setAccountManager(AccountManager accountManager) { + this.accountManager = accountManager; + } + + private TransactionTemplate txTemplate; + + public void setTxManager(DataSourceTransactionManager txManager) { + txTemplate = new TransactionTemplate(txManager); + } + + @Override + public Account create() { + return txTemplate.execute(new TransactionCallback<Account>() { + @Override + public Account doInTransaction(TransactionStatus arg0) { + return accountManager.create(); + } + }); + } + + @Override + public Account find(int accountNumber) { + return accountManager.find(accountNumber); + } + + @Override + public Account deposit(int accountNumber, BigDecimal amount) { + return txTemplate.execute(new TransactionCallback<Account>() { + + @Override + public Account doInTransaction(TransactionStatus arg0) { + return accountManager.deposit(accountNumber, amount); + } + }); + } + + @Override + public Account withdraw(int accountNumber, BigDecimal amount) { + return txTemplate.execute(new TransactionCallback<Account>() { + + @Override + public Account doInTransaction(TransactionStatus arg0) { + return accountManager.withdraw(accountNumber, amount); + } + }); + } + + @Override + public void delete(int accountNumber) { + txTemplate.execute(new TransactionCallbackWithoutResult() { + + @Override + protected void doInTransactionWithoutResult(TransactionStatus arg0) { + accountManager.delete(accountNumber); + } + }); + } + + @Override + public void chargeForLowBalance(BigDecimal minimumBalance, BigDecimal amount) { + txTemplate.execute(new TransactionCallbackWithoutResult() { + + @Override + protected void doInTransactionWithoutResult(TransactionStatus arg0) { + accountManager.chargeForLowBalance(minimumBalance, amount); + } + }); + } + + @Override + public Account transfer(int accountNumber1, int accountNumber2, BigDecimal amount) { + return txTemplate.execute(new TransactionCallback<Account>() { + + @Override + public Account doInTransaction(TransactionStatus status) { + try { + return accountManager.transfer(accountNumber1, accountNumber2, amount); + } catch (Exception e) { + // Some exception occurred. + status.setRollbackOnly(); + throw e; + } + } + }); + } +} diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/applicationContext.xml b/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/applicationContext.xml new file mode 100644 index 0000000..f57f28b --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/applicationContext.xml @@ -0,0 +1,39 @@ +<?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:jdbc="http://www.springframework.org/schema/jdbc" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/jdbc + http://www.springframework.org/schema/jdbc/spring-jdbc.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context.xsd"> + + <context:property-placeholder location="classpath:META-INF/spring/jdbc.properties"/> + + <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" > + <property name="driverClassName" value="${jdbc.driverClassName}"/> + <property name="url" value="${jdbc.url}"/> + <property name="username" value="${jdbc.username}"/> + <property name="password" value="${jdbc.password}"/> + </bean> + + <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> + <property name="dataSource" ref="dataSource"/> + </bean> + + <bean id="accountDAO" class="com.example.spring.bank.dao.jdbc.JDBCAccountDAO" > + <property name="dataSource" ref="dataSource"/> + </bean> + + <bean id="accountManagerWithNoTransactionSupport" class="com.example.spring.bank.AccountManagerImpl"> + <property name="accountDAO" ref="accountDAO"/> + </bean> + + <bean id="accountManager" class="com.example.spring.bank.tx.TxAccountManager"> + <property name="accountManager" ref="accountManagerWithNoTransactionSupport"/> + <property name="txManager" ref="txManager"/> + </bean> + +</beans>
\ No newline at end of file diff --git a/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/jdbc.properties b/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/jdbc.properties new file mode 100644 index 0000000..83220af --- /dev/null +++ b/spring-framework/07-bank-jdbc-ds-transaction/src/main/resources/META-INF/spring/jdbc.properties @@ -0,0 +1,9 @@ +jdbc.driverClassName=com.mysql.cj.jdbc.Driver +#jdbc.url=jdbc:mysql://localhost/bankdb?useSSL=false +#jdbc.url=jdbc:mysql://localhost/bankdb?useLegacyDatetimeCode=false&serverTimezone=UTC +jdbc.url=jdbc:mysql://localhost/bankdb?serverTimezone=Asia/Colombo +jdbc.username=bankdbuser +jdbc.password=abc123 + +#jdbc.driverClassName=org.postgresql.Driver +#jdbc.url=jdbc:postgresql://localhost/bank |
