서비스 계층의 분리 - 비지니스 로직의 분리
RegisterController : @Controller
UserService : @Service
UserDao, UserHistoryDao : @Repository
TrasactionManager
DAO 의 각 메서드는 개별 Connection 을 사용
@Repository
public class A1Dao {
@Autowired
DataSource ds;
public int insert(int key, int value) throws Exception{
Connection conn = null;
PreparedStatement pstmt = null;
try {
// conn = ds.getConnection();
conn = DataSourceUtils.getConnection(ds);
System.out.println("conn = "+conn);
pstmt = conn.prepareStatement("insert into a1 values(?, ?)");
pstmt.setInt(1, key);
pstmt.setInt(2, value);
return pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw e;
} finally {
// close(conn, pstmt);
close(pstmt);
DataSourceUtils.releaseConnection(conn, ds);
}
}
같은 Tx 내에서 같은 Connection 을 사용할 수 있게 관리
DAO 에서 Connection 을 얻거나 반환할 때 DataSourceUtils 를 사용해야
try {
// conn = ds.getConnection();
conn = DataSourceUtils.getConnection(ds);
} catch (SQLException e) {
e.printStackTrace();
throw e;
} finally {
// close(conn, pstmt);
close(pstmt);
DataSourceUtils.releaseConnection(conn, ds);
}
Transaction 적용하기
public void insertTest() throws Exception {
// TxManager 를 생성
PlatformTransactionManager tm = new DataSourceTransactionManager(ds);
TransactionStatus status = tm.getTransaction(new DefaultTransactionDefinition());
// Tx 시작
try {
a1Dao.deleteAll();
a1Dao.insert(1, 100); // 성공
a1Dao.insert(1, 200); // 실패
tm.commit(status);
} catch (Exception e) {
e.printStackTrace();
tm.rollback(status);
} finally {
}
빈으로 등록
root-context.xml
<bean id="transactionManager" class="org.springframework.jdbc.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/> // @Transactional 사용가능
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3301/usedhunter?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="jcy"></property>
<property name="password" value="zzz!"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>
<context:component-scan base-package="com.jcy.usedhunter" />
</beans>
package com.jcy.usedhunter;
import static org.junit.Assert.*;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class A1DaoTest {
@Autowired
A1Dao a1Dao;
@Autowired
DataSource ds;
@Autowired
DataSourceTransactionManager tm;
@Test
public void insertTest() throws Exception {
// TxManager 를 생성
// PlatformTransactionManager tm = new DataSourceTransactionManager(ds);
TransactionStatus status = tm.getTransaction(new DefaultTransactionDefinition());
// Tx 시작
try {
a1Dao.deleteAll();
a1Dao.insert(1, 100); // 성공
a1Dao.insert(1, 200); // 실패
tm.commit(status);
} catch (Exception e) {
e.printStackTrace();
tm.rollback(status);
} finally {
}
}
}
AOP를 이용한 핵심 기능과 부가 기능 분리
@Transactional 은 클래스나 인터페이스에도 붙일 수 있음
A1Dao.java
package com.jcy.usedhunter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class A1Dao {
@Autowired
DataSource ds;
public int insert(int key, int value) throws Exception{
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement("insert into a1 values(?, ?)");
pstmt.setInt(1, key);
pstmt.setInt(2, value);
return pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, pstmt);
}
return 0;
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
}
A1DaoTest.java
테스트 실행 시 두 값 모두 저장 확인
package com.jcy.usedhunter;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class A1DaoTest {
@Autowired
A1Dao a1Dao;
@Test
public void insertTest() throws Exception {
a1Dao.insert(1, 100);
a1Dao.insert(2, 200);
}
}
A1DaoTest.java
key 값을 같은 1 로 했을 때
package com.jcy.usedhunter;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class A1DaoTest {
@Autowired
A1Dao a1Dao;
@Test
public void insertTest() throws Exception {
a1Dao.insert(1, 100); // 성공
a1Dao.insert(1, 200); // 실패
}
}
실패 했을 때 commit 하지 말고 rollback 하기
A1Dao.java
package com.jcy.usedhunter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;
@Repository
public class A1Dao {
@Autowired
DataSource ds;
public int insert(int key, int value) throws Exception{
Connection conn = null;
PreparedStatement pstmt = null;
try {
// conn = ds.getConnection();
conn = DataSourceUtils.getConnection(ds);
System.out.println("conn = "+conn);
pstmt = conn.prepareStatement("insert into a1 values(?, ?)");
pstmt.setInt(1, key);
pstmt.setInt(2, value);
return pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw e;
} finally {
// close(conn, pstmt);
close(pstmt);
DataSourceUtils.releaseConnection(conn, ds);
}
}
public void deleteAll() throws Exception {
Connection conn = ds.getConnection();
String sql = "delete from a1";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
close(pstmt);
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
}
A1DaoTest.java
package com.jcy.usedhunter;
import static org.junit.Assert.*;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class A1DaoTest {
@Autowired
A1Dao a1Dao;
@Autowired
DataSource ds;
@Test
public void insertTest() throws Exception {
// TxManager 를 생성
PlatformTransactionManager tm = new DataSourceTransactionManager(ds);
TransactionStatus status = tm.getTransaction(new DefaultTransactionDefinition());
// Tx 시작
try {
a1Dao.deleteAll();
a1Dao.insert(1, 100); // 성공
a1Dao.insert(1, 200); // 실패
tm.commit(status);
} catch (Exception e) {
e.printStackTrace();
tm.rollback(status);
} finally {
}
}
}
conn = com.mysql.cj.jdbc.ConnectionImpl@2b87581
conn = com.mysql.cj.jdbc.ConnectionImpl@2b87581
'Spring > Ch3. Spring DI, AOP' 카테고리의 다른 글
Spring - 서비스 계층의 분리와 @Transactional 2 (0) | 2022.10.01 |
---|---|
Spring - AOP (0) | 2022.09.30 |
Spring - Transaction (0) | 2022.09.29 |
Spring DI - Spring DI 흉내내기 (0) | 2022.09.25 |
Spring - Spring DI(1) (0) | 2022.09.13 |