티스토리 뷰
1. 트랜잭션의 종류
- 1 ) 로컬 트랜잭션 ( Local TX )
- : 무조건 Connection 1개로 DML 작업 수행
- 2 ) 전역 트랜잭션 ( Global TX )
- : 동시에 여러 개의 Connection을 사용 ( 1개 이상의 DB )
- + 전역 트랜잭션의 경우, 자동으로 커밋 / 롤백 시켜주는 트랜잭션 관리자(TX Manager)가 WAS 안에 있다.
- + 2단계 커밋 / 롤백을 거치게 된다.
2. Driver SPY 사용법
- 1 ) 정식 라이브러리 명칭 : log4jbc
- 2 ) pom.xml 파일에 dependency(의존성) 추가
- <dependency>
- <groupId>org.bgee.log4jdbc-log4j2</groupId>
- <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
- <version>1.16</version>
- </dependency>
- 3 ) JDBC URL에 단어 하나 추가 : jdbc:log4jdbc:oracle:… (***)
- 4 ) log4j2.xml 설정파일(lombok의 @Log4j2의 설정)에 Driver Spy(log4jdbc) 로깅설정 추가
- <logger name="log4jdbc.log4j2" level="info" additivity="false">
- <MarkerFilter marker="LOG4JDBC_JDBC" onMatch="DENY" onMismatch="NEUTRAL"/>
- <appender-ref ref="Console"/>
- </logger>
- 5 ) 평소대로 JDBC api를 사용한 프로그램 수행하면, JDBC Driver의 상세내역이 출력된다.
3. TDD ( Test – Driven Development )
- 테스트 주도 개발
4. EDD ( Event – Driven Development )
- 이벤트 주도 개발
5. Junit 4.x Test Framework 사용법
- 1 ) pojo 기반의 테스트 클래스 생성
- + (Plain Old Java Object, 상속/구현도 하지 않는 아주 평범한 클래스)
- 2 ) 반드시 매개변수가 없는 기본 생성자를 가지고 있어야 한다.
- 3 ) 아래의 3가지 어노테이션을 이용해서, 테스트 골격 코드를 생성
- + @Before -> public 접근제한자를 가진 메소드로 구현
- + @Test -> public 접근제한자를 가진 메소드로 구현
- + @After -> public 접근제한자를 가진 메소드로 구현
6. Junit 5.x Test Framework 사용법
- 1 ) pojo 기반의 테스트 클래스 생성
- + (Plain Old Java Object, 상속/구현도 하지 않는 아주 평범한 클래스)
- 2 ) 반드시 매개변수가 없는 기본 생성자를 가지고 있어야 한다.
- 3 ) 아래의 다수 어노테이션을 이용해서, 테스트 골격 코드를 생성
- + @TestInstance / @TestMethodOrder : 테스트 클래스 선언부에 붙임
- + @BeforeAll / @AfterAll : 1회성 전역적인 사전처리 메소드에 붙임
- + @BeforeEach / @AfterEach : 매 테스트 메소드 수행 전, "사전처리" 메소드에 붙임
- + @Disabled / @Test / @DisplayName / @Timeout / @Order : 테스트 메소드에 붙이는 어노테이션
- 4 ) 위 테스트 클래스에서 만드는 모든 Lifecyle 메소드나 Test 메소드의 return type은 "void"
[ 1. Driver SPY : jdbcURL 수정 ]
package org.zerock.myapp;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class JDBCSPYExample1 { // JDBC Driver SPY
// Driver Spy를 사용하기 위해서는 jdbcUrl를 수정해야 한다.
static final String jdbcUrl = "jdbc:log4jdbc:oracle:thin:@db0000000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
// Driver Spy에 해당하는 log4jdbc를 추가해줘야 한다.
static final String user = "hr";
static final String pass = "Oracle000000";
public static void main(String[] args) {
String sql = "INSERT INTO departments(department_id, DEPARTMENT_NAME) VALUES(?, ?)";
// 추가하는 sql문
// sql문은 ;을 포함해서는 안된다.
try {
Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
conn.setAutoCommit(false);
// Connection 객체는 기본적으로 Auto Commit을 해준다.
// conn.setAutoCommit(false);로 자동 커밋을 해제시켜줄 수도 있다.
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 900);
pstmt.setString(2, "영화 900");
int affectedLines = 0;
try ( conn; pstmt; ){
affectedLines = pstmt.executeUpdate(); // 영향을 받은 라인의 수를 알려준다.
log.info("+ affectedLines : {}", affectedLines);
// 하나만 생성되었기 때문에 affectedLines 값이 1로 나온다.
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
} // try - with - resources
// 비지니스 로직 수행
} catch (Exception e) {
e.printStackTrace();
} // try - catch
} // end main
} // end class
[ 2. Driver SPY 2 : jdbcURL 수정 ]
package org.zerock.myapp;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.zerock.myapp.domain.Employee;
import lombok.Cleanup;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class JDBCSPYExample2 {
// Driver Spy를 사용하기 위해서는 jdbcUrl를 수정해야 한다.
static final String jdbcUrl = "jdbc:log4jdbc:oracle:thin:@db00000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
// Driver Spy에 해당하는 log4jdbc를 추가해줘야 한다.
static final String user = "hr";
static final String pass = "Oracle00000";
public static void main(String[] args) {
try {
Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
String sql = new StringBuffer().
append("SELECT employee_id, last_name, hire_date, salary, department_id ").
append("FROM employees ").
append("WHERE salary > ? ").
append("ORDER BY salary DESC").toString(); // (****)
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "7000");
ResultSet rs = pstmt.executeQuery(); // 실행
@Cleanup("clear")
List<Employee> employees = new ArrayList<>();
try ( conn; pstmt; rs; ){
while ( rs.next() ) {
String employeeId = rs.getString("EMPLOYEE_ID");
String lastName = rs.getString("last_name");
String hireDate = rs.getString("HIRE_DATE");
String salary = rs.getString("SALARY");
String departmentId = rs.getString("DEPARTMENT_ID");
// 적절한 자료구조를 만들어 저장 - VO(Value Object)
Employee emp = new Employee( employeeId, lastName, hireDate, salary, departmentId );
employees.add(emp); // ArrayList에 원소 추가하기
} // while
} //try -with - resources ( try()안에 지정되어 있는 자원을 블록이 끝나면 자동으로 해제시켜준다. )
employees.forEach( e -> log.info(e) ); // forEach(*****)
} catch(SQLException e) {
e.printStackTrace();
} // try - catch ( try블록 내에서 발생한 예외를 catch에 지정해 주어 예외를 추적하게 한다. )
} // main
} // end class
[ 3. JUnit 4.x대 Frame Work ]
package org.zerock.myapp;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
/**
* JUnit 4.x 테스트 소스 골격 코드를 배우자!
*/
@Log4j2
@NoArgsConstructor
public class AppTest {
//===============================================
// @NoArgsConstructor로 자동으로 생성자 생성
//===============================================
private String jdbcUrl;
private String user;
private String pass;
private Connection conn;
//===============================================
// @Before
public void before() {
log.trace("before() invoked.");
// + 사전처리할 코드 ex. jdbcURL, DB 계정, DB 암호
this.jdbcUrl = "jdbc:log4jdbc:oracle:thin:@db00000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
this.user = "hr";
this.pass = "Oracle000000";
} // before
//===============================================
// @Test(timeout = 100)
public void contextLoads1() throws SQLException {
log.trace("contextLoads1() invoked.");
conn = DriverManager.getConnection(jdbcUrl, user, pass);
} // contextLoads
// + @Test는 timeout 속성을 주어, 블록 내의 코드실행시간을 총 몇 밀리세컨드까지 기다릴지 정할 수 있다.
// + 이때 준비시간은 제외된다.
//===============================================
// @Test
public void contextLoads2() {
log.trace("contextLoads2() invoked.");
} // contextLoads2
//===============================================
// @After
public void tearDown() throws SQLException {
log.trace("tearDown() invoked.");
// + 사후처리할 코드 ex. 자원 해제
// 1번째 방법
assert this.conn != null;
// 2번째 방법
Objects.requireNonNull(this.conn);
// 3번째 방법
// assertNotNull(this.conn);
// 4번째 방법 ( 알려주는 것이 없기에 추천하지 않는다. )
if(this.conn != null)
conn.close();
} // tearDown
//===============================================
// + @Befor / @Test / @After는 public이어야 한다.
// + @Befor / @Test / @After는 매개변수가 없다.
// + @Befor / @After는 사전처리와 사후처리가 없을 경우, 없앨 수 있다.
// + @Test 메소드는 test가 이름 앞에 붙여야 했는데, 현재 버전에서는 붙이지 않아도 가능하다.
// + @Test는 여러개 생성이 가능하다.
// + 4.x 버전에서는 비포와 에프터가 매 테스트 메소드마다 초기화된다.
//===============================================
} // end class
[ 4. JUnit 5.x대 Frame Work ] (****)
package org.zerock.myapp;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
/**
* JUnit 5.x 테스트 소스 골격 코드를 배우자! ( Stub Class )
*/
@Log4j2
@NoArgsConstructor
//==========================================================
@TestInstance(Lifecycle.PER_CLASS)
// @TestInstance는 라이프사이클을 지정해줘야 한다. (***)
//==========================================================
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
// @TestMethodOrder는 테스트 메소드가 수행되는 순서를 지정해 준다. (***)
// 속성으로 value 속성을 지정해 줘야 한다.
//==========================================================
public class AppTest2 {
private String jdbcUrl;
private String user;
private String pass;
private Connection conn;
//==========================================================
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
this.jdbcUrl = "jdbc:log4jdbc:oracle:thin:@db000000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
this.user = "hr";
this.pass = "Oracle0000";
} // beforeAll
// + beforeAll과 eachAll은 전역적인 의미의 사전 / 사후 처리이기 때문에
// + @Test 메소드가 여러개여도 1번씩만 진행이 된다.
//==========================================================
@BeforeEach
void beforeEach() {
log.trace("beforeEach() invoked.");
} // beforeEach
// + beforeEach와 afterEach는 매 테스트 메소드마다 사전 / 사후에 진행이 된다.
//==========================================================
@AfterAll
void afterAll() {
log.trace("afterAll() invoked.");
} // afterAll
@AfterEach
void afterEach() throws SQLException {
log.trace("afterEach() invoked.");
conn.close();
} // afterEach
//==========================================================
// @Disabled
@Test
@Order(2) // 실행되는 순서를 변경할 수 있다.
@DisplayName("<<<< contextLoads1 >>>>") // 실행시킬때 나타나는 이름을 지정해 준다.
@Timeout(value=2000, unit=TimeUnit.MILLISECONDS)
void contextLoads1() throws SQLException {
log.trace("contextLoads1() invoked.");
conn = DriverManager.getConnection(jdbcUrl, user, pass);
assertNotNull(conn);
} // contextLoads1
@Test
@Order(3) // 실행되는 순서를 변경할 수 있다.
@DisplayName("<<<< contextLoads2 >>>>") // 실행시킬때 나타나는 이름을 지정해 준다.
@Timeout(value=2000, unit=TimeUnit.MILLISECONDS)
void contextLoads2() throws SQLException {
log.trace("contextLoads2() invoked.");
conn = DriverManager.getConnection(jdbcUrl, user, pass);
assertNotNull(conn);
} // contextLoads2
//==========================================================
// @Disabled // @Disabled는 비활성화시킨다.
@Test
@Order(1) // 실행되는 순서를 변경할 수 있다.
@DisplayName("<<<< contextLoads3 >>>>") // 실행시킬때 나타나는 이름을 지정해 준다.
@Timeout(value=3000, unit=TimeUnit.MILLISECONDS)
void contextLoads3() throws SQLException {
log.trace("contextLoads3() invoked.");
conn = DriverManager.getConnection(jdbcUrl, user, pass);
assertNotNull(conn);
} // contextLoads3
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 85일차 - Servlet 2 (****) (0) | 2022.06.29 |
---|---|
KH 84일차 - Servlet 생성 (****) (0) | 2022.06.28 |
KH 82일차 - WAS (0) | 2022.06.24 |
KH 81일차 - JDBC 2 (*****) (0) | 2022.06.23 |
KH 80일차 - JDBC 1 ( Target DB에 연결 ) (0) | 2022.06.22 |