스프링 JDBC
자바 은행에서 사용한 JDBC 코드를 스프링 JDBC로 바꾸어 보자. 다음 의존성을 추가한다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
MyBankDao 클래스가 NamedParameterJdbcDaoSupport 클래스를 상속하도록 선언한다. 생성자와 getConnection(), close(rs,pstmt,con) 메소드는 제거한다.
MyBankDao.java
package net.java_school.bank;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
public class MyBankDao extends NamedParameterJdbcDaoSupport implements BankDao {
//필드 URL, USER, PASSWORD 삭제
//생성자와 getConnection(), close(rs,pstmt,con) 메소드 삭제
//..중간 생략..
}
기존 insertAccount() 메소드
@Override
public void insertAccount(String accountNo, String name, String kind) {
Connection con = null;
PreparedStatement pstmt = null;
String sql = "INSERT INTO bankaccount VALUES (?, ?, 0, ?)";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, accountNo);
pstmt.setString(2, name);
pstmt.setString(3, kind);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(null, pstmt, con);
}
}
수정 후 insertAccount() 메소드
private static final String INSERT_ACCOUNT =
"INSERT INTO " +
"bankaccount (accountno, owner, balance, kind) " +
"VALUES (:accountNo, :name, 0, :kind)";
@Override
public void insertAccount(String accountNo, String name, String kind) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("accountNo", accountNo);
params.put("name", name);
params.put("kind", kind);
getNamedParameterJdbcTemplate().update(INSERT_ACCOUNT, params);
}
기존 selectOneAccount() 메소드
@Override
public Account selectOneAccount(String accountNo) {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Account account = null;
String sql = "SELECT accountNo,owner,balance,kind " +
"FROM bankaccount " +
"WHERE accountNo = ?";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, accountNo);
rs = pstmt.executeQuery();
if (rs.next()) {
account = new Account();
account.setAccountNo(rs.getString("accountNo"));
account.setName(rs.getString("owner"));
account.setBalance(rs.getDouble("balance"));
account.setKind(rs.getString("kind"));
return account;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs, pstmt, con);
}
return null;
}
수정 후 selectOneAccount() 메소드
private static final String SELECT_ONE_ACCOUNT =
"SELECT accountno,owner,balance,kind " +
"FROM bankaccount " +
"WHERE accountno = :accountNo";
@Override
public Account selectOneAccount(String accountNo) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("accountNo", accountNo);
return getNamedParameterJdbcTemplate().queryForObject(
SELECT_ONE_ACCOUNT,
params,
new RowMapper<Account>() {
public Account mapRow(ResultSet rs,int rowNum) throws SQLException {
Account account = new Account();
account.setAccountNo(rs.getString("accountNo"));
account.setName(rs.getString("owner"));
account.setBalance(rs.getDouble("balance"));
account.setKind(rs.getString("kind"));
return account;
}
}
);
}
기존 selectAccountsByName() 메소드
@Override
public List<Account> selectAccountsByName(String name) {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Account> matched = new ArrayList<Account>();
Account account = null;
String sql = "SELECT accountNo,owner,balance,kind " +
"FROM bankaccount " +
"WHERE owner = ? " +
"ORDER By accountNo DESC";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, name);
rs = pstmt.executeQuery();
while (rs.next()) {
account = new Account();
account.setAccountNo(rs.getString("accountNo"));
account.setName(rs.getString("owner"));
account.setBalance(rs.getDouble("balance"));
account.setKind(rs.getString("kind"));
matched.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs, pstmt, con);
}
return matched;
}
수정 후 selectAccountsByName() 메소드
private static final String SELECT_ACCOUNTS_BY_NAME =
"SELECT accountno,owner,balance,kind " +
"FROM bankaccount " +
"WHERE owner = :name";
@Override
public List<Account> selectAccountsByName(String name) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("name", name);
RowMapper<Account> rowMapper = new AccountRowMapper();
return getNamedParameterJdbcTemplate().query(SELECT_ACCOUNTS_BY_NAME,params,rowMapper);
}
protected class AccountRowMapper implements RowMapper<Account> {
public Account mapRow(ResultSet rs,int rowNum) throws SQLException {
String accountNo = rs.getString("accountNo");
String name = rs.getString("owner");
double balance = rs.getDouble("balance");
String kind = rs.getString("kind");
Account account = new Account();
account.setAccountNo(accountNo);
account.setName(name);
account.setBalance(balance);
account.setKind(kind);
return account;
}
}
기존 selectAllAccounts() 메소드
@Override
public List<Account> selectAllAccounts() {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Account> all = new ArrayList<Account>();
Account account = null;
String sql = "SELECT accountNo,owner,balance,kind " +
"FROM bankaccount " +
"ORDER By accountNo DESC";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
account = new Account();
account.setAccountNo(rs.getString("accountNo"));
account.setName(rs.getString("owner"));
account.setBalance(rs.getDouble("balance"));
account.setKind(rs.getString("kind"));
all.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs, pstmt, con);
}
return all;
}
수정 후 selectAllAccounts() 메소드
private static final String SELECT_ALL_ACCOUNTS =
"SELECT accountNo,owner,balance,kind " +
"FROM bankaccount " +
"ORDER BY accountNo DESC";
@Override
public List<Account> selectAllAccounts() {
RowMapper<Account> rowMapper = new AccountRowMapper();
return getJdbcTemplate().query(SELECT_ALL_ACCOUNTS,rowMapper);
}
기존 deposit() 메소드
@Override
public void deposit(String accountNo, double amount) {
Connection con = null;
PreparedStatement pstmt = null;
String sql = "UPDATE bankaccount " +
"SET balance = balance + ? " +
"WHERE accountNo = ?";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setLong(1, amount);
pstmt.setString(2, accountNo);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(null, pstmt, con);
}
}
수정 후 deposit() 메소드
private static final String DEPOSIT =
"UPDATE bankaccount " +
"SET balance = balance + :amount " +
"WHERE accountno = :accountNo";
@Override
public void deposit(String accountNo, double amount) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("amount", amount);
params.put("accountNo", accountNo);
getNamedParameterJdbcTemplate().update(DEPOSIT, params);
}
기존 withdraw() 메소드
@Override
public void withdraw(String accountNo, double amount) {
Connection con = null;
PreparedStatement pstmt = null;
String sql = "UPDATE bankaccount " +
"SET balance = balance - ? " +
"WHERE accountNo = ?";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setLong(1, amount);
pstmt.setString(2, accountNo);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(null, pstmt, con);
}
}
수정 후 withdraw() 메소드
private static final String WITHDRAW =
"UPDATE bankaccount " +
"SET balance = balance - :amount " +
"WHERE accountno = :accountNo";
@Override
public void withdraw(String accountNo, double amount) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("amount", amount);
params.put("accountNo", accountNo);
getNamedParameterJdbcTemplate().update(WITHDRAW, params);
}
기존 selectAllTransactions() 메소드
@Override
public List<Transaction> selectAllTransactions(String accountNo) {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Transaction> all = new ArrayList<Transaction>();
Transaction transaction = null;
String sql = "SELECT transactionDate,kind,amount,balance " +
"FROM transaction " +
"WHERE accountNo = ? " +
"ORDER By transactionDate ASC";
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, accountNo);
rs = pstmt.executeQuery();
while (rs.next()) {
transaction = new Transaction();
String date = Account.DATE_FORMAT.format(rs.getTimestamp("transactionDate"));
String time = Account.TIME_FORMAT.format(rs.getTimestamp("transactionDate"));
transaction.setTransactionDate(date);
transaction.setTransactionTime(time);
transaction.setKind(rs.getString("kind"));
transaction.setAmount(rs.getDouble("amount"));
transaction.setBalance(rs.getDouble("balance"));
all.add(transaction);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs, pstmt, con);
}
return all;
}
수정 후 selectAllTransactions() 메소드
private static final String SELECT_ALL_TRANSACTIONS =
"SELECT transactionDate,kind,amount,balance " +
"FROM transaction " +
"WHERE accountno = :accountNo " +
"ORDER By transactionDate ASC";
@Override
public List<Transaction> selectAllTransactions(String accountNo) {
RowMapper<Transaction> rowMapper = new TransactionRowMapper();
Map<String, Object> params = new HashMap<String, Object>();
params.put("accountNo", accountNo);
return getNamedParameterJdbcTemplate().query(SELECT_ALL_TRANSACTIONS,params,rowMapper);
}
protected class TransactionRowMapper implements RowMapper<Transaction> {
public Transaction mapRow(ResultSet rs,int rowNum) throws SQLException {
String date = Account.DATE_FORMAT.format(rs.getTimestamp("transactionDate"));
String time = Account.TIME_FORMAT.format(rs.getTimestamp("transactionDate"));
String kind = rs.getString("kind");
double amount = rs.getDouble("amount");
double balance = rs.getDouble("balance");
Transaction transaction = new Transaction();
transaction.setTransactionDate(date);
transaction.setTransactionTime(time);
transaction.setKind(kind);
transaction.setAmount(amount);
transaction.setBalance(balance);
return transaction;
}
}
XML 설정
applicationContext.xml에 강조된 부분을 추가한다.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<bean id="testLogger" class="net.java_school.commons.TestLogger" />
<bean id="bankUi" class="net.java_school.bank.BankUi">
<property name="stream" value="#{T(System).out}" />
<property name="bank" ref="myBank" />
</bean>
<bean id="myBank" class="net.java_school.bank.MyBank">
<property name="dao" ref="myBankDao" />
</bean>
<bean id="myBankDao" class="net.java_school.bank.MyBankDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:XE" />
<property name="username" value="scott" />
<property name="password" value="tiger" />
</bean>
</beans>
테스트
테스트하기 전에 BankUi.java의 메인 메소드를 아래를 참조해 수정한다.
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML
//AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig
101계좌는 존재하나 505계좌는 존재하는 않는다고 가정한다. 101계좌에서 505계좌로 이체 테스트를 한다. 이체 후 101계좌는 이체 금액만큼 잔액이 줄어든다. (이 문제는 트랜잭션에서 다룬다)
JavaConfig 설정
BankConfig.java
package net.java_school.bank;
import javax.sql.DataSource;
import net.java_school.commons.TestLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
@Configuration
@EnableAspectJAutoProxy
public class BankConfig {
@Bean
public TestLogger testLogger() {
return new TestLogger();
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@127.0.0.1:1521:XE");
dataSource.setUsername("scott");
dataSource.setPassword("tiger");
return dataSource;
}
@Bean
public BankDao myBankDao() {
MyBankDao bankDao = new MyBankDao();
bankDao.setDataSource(dataSource());
return bankDao;
}
@Bean
public Bank myBank() {
Bank bank = new MyBank();
bank.setDao(myBankDao());
return bank;
}
@Bean
public BankUi bankUi() {
BankUi ui = new BankUi();
ui.setBank(myBank());
ui.setStream(System.out);
return ui;
}
}
테스트하기 전에, applicationContext.xml 설정을 모두 주석 처리한다. BankUi.java의 메인 메소드를 아래를 참조해 수정한다.
//ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig
