프로그래밍/- 남굼성의 Spring 교육
서비스 계층 분리 [ @Transaction , AOP ]
즐겁게 하하하
2022. 3. 12. 18:03
728x90
1. 서비스계층의 분리
2. DAO의 각 메서드는 개별 Connetion을 사용 하지만 [ TransactionManager 를 이용할 수 있다. ]
3. AOP를 이용하여 핵심기능, 부가기능 분리하기
4. @Transactional 은 클래스나 인터페이스에도 사용 가능
1. Transaction , AOP
A1Dao.java
@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); // transaction 1개
}
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
}
Junit test
@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);
}
}
2. 두가지 일을 하나의 Transaction 으로 묶기
A1Dao.java
@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(); // transaction 1개
conn = DataSourceUtils.getConnection(ds); // transaction 여러개
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); // transaction 1개
close(pstmt); // transaction 여러개일때
DataSourceUtils.releaseConnection(conn,ds); // transaction 여러개일때
}
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
public void deleteAll() throws Exception{
Connection conn = ds.getConnection();
String sql = "delete from a1";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
close(conn,pstmt);
}
}
Junit test
@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 {
// Tx Manager를 생성 ( 하나의 transaction 으로 묶기 )
PlatformTransactionManager tm = new DataSourceTransactionManager(ds);
TransactionStatus status = tm.getTransaction(new DefaultTransactionDefinition());
// Tx 시작
try {
a1Dao.deleteAll();
a1Dao.insert(1,100);
a1Dao.insert(1,200); // key 중복때문에 roll back 됨
tm.commit(status);
} catch (Exception e) {
e.printStackTrace();
tm.rollback(status);
} finally {
}
}
}
root-context.xml
<?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"
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">
<!-- Root Context: defines shared resources visible to all other web components -->
<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:3306/springbasic?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="back"></property>
<property name="password" value="rhrlekd1+"></property>
</bean>
<!-- com ( component-scan ) -->
<!-- 패키지 안에 있는 클래스 중에서 @Component 붙은것을 찾아서 Bean 으로 등록 -->
<context:component-scan base-package="com.test.ch3" >
<context:exclude-filter type="regex" expression="com.test.ch3.dpCopy*.*"/>
</context:component-scan>
</beans>
결과 : a1Dao.insert(1,100); / a1Dao.insert(1,200); [ key 중복때문에 roll back 됨 ]
3. @Transactional 의 속성
https://hahagogo.tistory.com/166
Transaction, Commit , Rollback
Transaction : 더이상 나눌수 없는 작업의 단위 속성 : ACID 원자성(A) : 나눌수 없는 하나의 작업으로 다뤄져야 일관성(C) : 수행 전 후 가 일관된 상태가 되어야 고립성(I) : 각 트랜젝션은
hahagogo.tistory.com
propagation | Tx의 경계 설정하는 방법 지정 |
isolation | Tx의 isolation level을 지정 |
readOnly | Tx이 데이터를 읽기만 하는 경우 true |
rollbackFor | 지정된 예외가 발생하면 Tx rollback RuntimeException과 Error는 자동 rollback |
noRollbackFor | 지정된 예외가 발생해도 Tx를 rollback 하지 않음 |
timeout | 지정된 시간(초) 내에 Tx 종료 안되면 강제 종료 |
A1Dao
@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(); // transaction 1개
conn = DataSourceUtils.getConnection(ds); // transaction 여러개
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); // transaction 1개
close(pstmt); // transaction 여러개일때
DataSourceUtils.releaseConnection(conn,ds); // transaction 여러개일때
}
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
public void deleteAll() throws Exception{
Connection conn = ds.getConnection();
String sql = "delete from a1";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
close(conn,pstmt);
}
}
TxService
@Transactional(rollbackFor = Exception.class) // Exception Error rollback
@Transactional // RuntimeException , Error만 rollback
@Service
public class TxService {
@Autowired A1Dao a1Dao;
@Autowired B1Dao b1Dao;
public void insertA1WithoutTx() throws Exception {
a1Dao.insert(1,100);
a1Dao.insert(1,200);
}
@Transactional(rollbackFor = Exception.class) // Exception Error rollback
public void insertA1WithTxFail() throws Exception {
a1Dao.insert(1,100);
a1Dao.insert(1,200);
}
@Transactional // RuntimeException , Error만 rollback
public void insertA1WithTxSuccess() throws Exception {
a1Dao.insert(1,100);
a1Dao.insert(2,200);
}
}
TxServiceTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class TxServiceTest {
@Autowired TxService txService;
@Test
public void insertA1WithoutTxTest() throws Exception {
//txService.insertA1WithoutTx();
//txService.insertA1WithTxSuccess();
txService.insertA1WithTxFail();
}
}
728x90