서비스 계층의 분리 - 비지니스 로직의 분리

 

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&amp;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
복사했습니다!