본문 바로가기
☁︎KOSTA/☂KOSTA-SPRING

[KOSTA] Spring 기반 Cloud 서비스 구현 개발자 양성 (Day 91) - Transaction, Lombok

by 하_센세 2022. 12. 13.

2022.12.13 [TUE]

- Day 91- 

 

수업 주요 목차

  • Transaction
  • Lombok

 

Spring Handler Interceptor : DispatcherServlet이 해당 컨트롤러를 호출하기 전,후에 요청과 응답을 제어하는 역할을 한다. 컨트롤러 영역( presentation layer )의 공통 업무인 인증 체크 로직을 담당  

  • 컨트롤러 실행전 preHandle(request,response,handler)
  • 컨트롤러 실행후 postHandle(request,response,handler)
  • 응답완료 afterCompletion(request,response,handler)

Spring에서 제공하는 HandlerInterceptor 를 implements하여 위와 같은 메서드를 오버라이딩해서 사용한다

 

DispatcherServlet -- HandlerInterceptor -- Handler(Controller)

컨트롤러 영역의 공통관심사항을 일괄처리

 

 

Lombok : 어노테이션 기반으로 자바 코드를 자동 생성하는 라이브러리 

 

👾 자주 사용하는 롬복 어노테이션 👾

@Getter get method 생성 
@Setter  set method 생성
@NoArgsConstructor 파라미터가 없는 기본 생성자를 생성
@AllArgsConstructor 모든 필드 값을 파라미터로 받는 생성자
@RequiredArgsConstructor final field 를 파라미터로 받는 생성자 
@ToString toString method 생성 
@Data @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode 를 한번에 코드로 생성
@Slf4j  logging 선언부 코드를 자동생성
@Builder class에 builder pattern 을 적용해 객체 생성할 수 있게 함
ex ) TestVO vo=TestVO.builder().id("java").name("아이유").money(100).build();

 

더보기

lombok.jar 다운로드 확인

터미널 실행 후 cd /jar파일이 있는 위치로 이동/

java -jar lombok.jar 실행하면 설치 exe가 실행되는데

mac은 eclipse ini 파일을 경로설정(?)해주면 된다.

 

 

 

 

 

Spring AOP Transaction

    

    @Transactional : AOP 기반 선언적 방식의 트랜잭션 처리 

       @Transactional 어노테이션을 적용하면 AOP Proxy(대리인) 객체가 Transaction 제어를 한다  

       해당 서비스 메서드에서 Error 와 RuntimeException(unchecked exception) 계열 Exception 발생하면 자동 rollback

       Spring Data 관련 Exception 은 모두 java.lang.RuntimeException 의 하위 Exception 이다 

       모든 작업이 문제없이 실행되면 commit 

     

    소프트웨어 아키텍쳐 계층( layer )     

  • Presentation Layer ( Controller ) : 어플리케이션의 기능을 웹서비스로 제공
  • Service Layer ( Service ) : 비즈니스 로직을 정의 , 트랜잭션 제어     
  • Persistence Layer ( Repository ) : 데이터 액세스 

 👾 장점 :

  • 계층별로 분리 -> 응집도가 높아짐 -> 중복 감소 , 재사용성 증가
  • 계층 즉 layer 별로 추상화를 적용 ( 캡슐화 or 계층구조화 ) 하면 결합도가 낮아져 유지보수성이 향상 
더보기
  • service layer를 분리하면  presentation layer의 기술 변경에 유연하게 대처할 수 있음 
  • 예를 들면 web page 응답 방식에서 ajax response(rest api) 방식으로 컨트롤러 메커니즘이 변경될 때 service layer 없이 controller 즉 presentation layer에  business logic 이 정의되어 있다면 많은 부분의 작업이 필요하겠지만 비즈니스 로직이 service layer 에 분리되어 응집되어 있다면 단순히 응답 방식을 변경하는 소량의 작업이 하면 됨
  • 또한 service layer로 구분되어 있으면 비즈니스 로직 하나의 다양한 방식의 컨트롤러가 연결되어 재사용될 수 있음

 

DB Transaction : 비즈니스 로직에서 더 이상 나눌 수 없는 하나의 작업단위 ( 여러 세부 작업을 하나로 묶은 실행 단위 ) 

  • 데이터베이스의 상태를 변경하고자 할 때 한번에 수행되어야 하는 연산을 의미 ( 예 -  계좌이체는 출금과 입금의 세부 작업을 하나의 단위로 처리해야 한다 ) 

     

트랜잭션 성질 ACID : 트랜잭션의 안전성을 보장하기 위한 성질 

  • 원자성(Atomicity) : 한 트랜잭션의 세부 연산들이 모두 성공하거나 실패하는 성질(예:이체 작업 중 문제발생시 출금,입금연산 모두 롤백되어야 함)
  • 일관성(Consistency) : 트랜잭션이 성공하면 일관적인 db 상태를 유지하는 성질(예: 무결성 제약이 모든 계좌는 잔고가 있어야 한다면 이를 위반하면 트랜잭션은 중단된다.)
  • 격리성(Isolation) : 트랜잭션으로 처리되는 중간에 외부 간섭은 없어야 한다.
  • 영속성 (Durability) : 성공적으로 완료된(commit) 트랜잭션은 데이터베이스에 영구 보존되어야 하는 성질

    상세한 설명은 https://ko.wikipedia.org/wiki/ACID

 

더보기

🧚‍♂️ 트랜잭션 처리 예: 회원 가입시 포인트 지급이 약속된 업무 business 

  • 회원 정보와 포인트 정보가 모두 정상적으로 등록될 때 commit 
  • 세부 작업 진행 중 문제가 발생( Error 와 RuntimeException계열 Exception )하면 진행되었던 모든 작업은 rollback( 취소하고 원상태로 복귀 ) 되어야 한다.

 

 

    

 

 


 

🔎 Eclipse 실습 내용

 

1. SQL(Self Join) + 예제문제

/*
	Join SQL : 여러개의 (or 하나 이상의) 테이블의 정보를 결합하기 위한 SQL 

	1. INNER JOIN : 테이블 간의 상응하는 컬럼의 정보가 존재할때 조회 : 
	                      ex) 부서와 사원테이블에서 부서번호에 해당하는 사원의 사원정보와 부서정보를 조회할때 사용
	
	                      일반적인 Join 을 의미 
						  여러 테이블을 결합할 때 , 지정한 컬럼의 데이터가 존재할 때 사용하는 join 
						  예 ) 부서 테이블의 부서번호와 사원 테이블의 부서번호가 일치할 때 부서와 사원정보를 join 해서 조회 
						  
	2. OUTER JOIN : 테이블 간의 상응하는 컬럼의 정보가 존재하지 않을 때에도 조회 : 
	                      ex) 부서와 사원테이블 결합시 사원이 존재하지 않는 부서정보까지 모두 조회할때 사용 	
	
	                      여러 테이블 중 한 쪽에서는 데이터가 있고 , 다른 한쪽에는 데이터가 없는 경우
						   데이터가 있는 쪽 테이블의 내용을 모두 출력 
						 예 ) 부서 테이블에는 40번 부서정보가 존재하는 데 
						 	  사원 테이블에는 40번 부서의 사원정보가 존재하지 않을 경우
						 	  사원이 없는 부서 테이블의 부서정보도 모두 조회하기 위해 Outer Join 을 이용 
						 	  
	3. SELF JOIN : 동일한 테이블 상에서의 조인 
						예 ) 사원 테이블의 매니저 컬럼 정보는 또 다른 사원의 사원번호이다.
							 이를 이용해 사원 정보와 그 사원의 관리자인 매니저 정보를 함께 조회할 때 self join 을 사용 
							 
							 
*/

/*
 		1. Inner Join : 테이블 간의 상응하는 컬럼의 정보가 존재하면 조회 
 */
-- 사원은 10,20,30번 부서에만 속해 있음 
SELECT * FROM emp;
SELECT distinct deptno FROM emp;
-- 10,20,30,40 번 부서가 있음 
SELECT* FROM dept;

-- INNER JOIN : 부서 테이블(dept) 과 사원 테이블(emp) 의 정보를 결합 
-- 사원 정보와 그 사원이 속한 부서 정보를 조회 Inner Join : 일치하는 정보가 없는 부서 테이블의 40번 부서 정보는 조회되지 않는다  
--oracle join sql
SELECT e.empno, e.ename, e.job, d.deptno, d.dname, d.loc
FROM emp e, dept d
WHERE e.deptno=d.deptno


-- ANSI SQL ( American National Standards Institute : 미국표준협회 ) : 데이터베이스 표준 SQL -> 특정 벤더에 종속적이지 않는 SQL
SELECT e.empno, e.ename, e.job, d.deptno, d.dname, d.loc
FROM emp e
INNER JOIN  dept d ON e.deptno=d.deptno


-- INNER JOIN :      사원 테이블(emp) 과 월급등급 테이블(salgrade) 의 정보를 결합 
					  --     sal				  losal   , 		hisal 	, 	grade 
					  --  사원의 월급			  최저액이상   최고액이하  그에 따른 등급     	 
 -- SMITH 사원은 sal 이 800 이므로 월급등급은 1이다 
 SELECT * FROM salgrade;
 SELECT * FROM emp WHERE ename='SMITH';

-- SMITH 사원의 empno,ename,job,sal,grade 를 INNER JOIN을 이용해 조회 
SELECT e.empno, e.ename, e.job, e.sal, s.grade
FROM emp e,  salgrade s
WHERE e.ename='SMITH' AND e.sal>=s.losal
--WHERE e.ename='SMITH' AND s.grade=(SELECT grade FROM salgrade WHERE losal<=800)
 
-- ANSI SQL 로 Inner JOIN 표현 
SELECT e.empno, e.ename, e.job, e.sal, s.grade
FROM emp e
INNER JOIN salgrade s ON e.sal>=s.losal
WHERE e.ename='SMITH' 

--  SQL 연습 : emp 의 empno가 7521 인 사원의 ename과 dept 의 deptno,dname, salgrade의 grade를 조회해본다 
--oracle
SELECT e.ename, d.deptno, d.dname, s.grade
FROM emp e,  salgrade s, dept d
WHERE e.empno=7521 AND e.sal>=s.losal AND s.hisal>=e.sal
AND e.deptno = d.deptno

-- ANSI SQL 
SELECT e.ename, d.deptno, d.dname, s.grade
FROM emp e
INNER JOIN dept d  ON e.deptno = d.deptno
INNER JOIN salgrade s ON e.sal>=s.losal AND s.hisal>=e.sal
WHERE e.empno=7521

/*
 2. OUTER JOIN : 여러 테이블 중 한 쪽에서는 데이터가 있고 , 다른 한쪽에는 데이터가 없는 경우
						 데이터가 있는 쪽 테이블의 내용을 모두 출력 
						 예 ) 부서 테이블에는 40번 부서정보가 존재하는 데 
						 	  사원 테이블에는 40번 부서의 사원정보가 존재하지 않을 경우
						 	  사원이 없는 부서 테이블의 부서정보도 모두 조회하기 위해 Outer Join 을 이용
 */
-- 부서테이블에는 10,20,30,40번 부서정보가 존재 
SELECT DISTINCT deptno FROM dept;
-- 사원테이블에는 10,20,30번 부서 사원들이 존재 
SELECT DISTINCT deptno FROM emp;
-- inner join 에서는 결과행이 14 : 40번 부서에 해당하는 사원이 존재하지 않으므로 부서테이블의 40번 정보는 조회되지 않는다 
SELECT e.empno, e.ename, d.deptno, d.dname, d.loc
FROM emp e
INNER JOIN  dept d ON e.deptno=d.deptno
--사원이 존재하지 않는 부서 (40번 부서) 정보까지 모두 조회하고자 한다면 Outer Join 이 필요하다.

-- outer join : 조인 즉 결합할 정보가 없는 측 조건에 (+) 기호를 명시 , 부서테이블의 부서정보 40 이 존재 , 사원테이블의 40번 사원정보가 존재 x 
-- outer join 에서는 결과행이 15 : 40번 부서에 해당하는 사원이 존재하지 않아도 부서테이블의 40번 부서정보를 조회한다  
--oracle sql
SELECT e.empno, e.ename, d.deptno, d.dname, d.loc
FROM emp e, dept d
WHERE e.deptno(+)=d.deptno

-- ANSI SQL : LEFT Outer Join -> 왼쪽 테이블을 기준으로 해서 오른쪽 테이블의 정보를 결합 
SELECT e.empno, e.ename, d.deptno, d.dname, d.loc
FROM dept d
LEFT OUTER JOIN emp e ON e.deptno=d.deptno
-- RIGHT OUTER JOIN -> 오른쪽 테이블을 기준으로 결합 
SELECT e.empno, e.ename, d.deptno, d.dname, d.loc
FROM emp e
RIGHT OUTER JOIN dept d ON e.deptno=d.deptno



/*
 	3. SELF JOIN : 동일한 테이블 상에서의 조인 
 					   동일한 테이블이지만 개념적으로 다른 정보를 결합 
						예 ) 사원 테이블의 매니저 컬럼 정보는 또 다른 사원의 사원번호이다.
							 이를 이용해 사원 정보와 그 사원의 관리자인 매니저 정보를 함께 조회할 때 self join 을 사용  
 */
	SELECT * FROM emp;
	-- 7902 사원번호의 매니저 번호 7566 이다 , 7566 은 또 다른 사원의 사원번호이다 -> 사원의 매니저 번호란 관리자의 사원번호를 의미한다  
	SELECT empno,ename,mgr FROM emp WHERE empno=7902;
	SELECT empno,ename,mgr FROM emp WHERE empno=7566;
    
	-- 7902 empno 의 ename과 mgr , 매니저명(또 다른 사원의 ename) 을 조회하고자 한다 : 이 때 self join을 이용 
	--oracle sql
	SELECT e.ename, m.ename AS mgrEname
	FROM emp e, emp m
	WHERE e.empNo=7902 AND e.mgr=m.empNo


	--ANSI
	SELECT e.ename, m.ename AS mgrEname
	FROM emp e
	JOIN emp m ON e.mgr=m.empNo
	WHERE e.empNo=7902

	-- 전체 사원을 대상으로 SELF JOIN 해서 사원의 e.empno,e.ename,e.mgr,m.ename as 매니저명을 모두 조회해본다 
	--oracle sql
	SELECT e.ename, m.ename AS mgrEname
	FROM emp e, emp m
	WHERE e.mgr=m.empno
	
	--ANSI
	SELECT e.ename, m.ename AS mgrEname
	FROM emp e
	INNER JOIN emp m ON e.mgr=m.empno
	
	
    -- 위 self join sql을 실행하면 13명의 사원이 조회된다 
    -- 총사원수는 14명 , 이유는 mgr이 없는 king 사원이 제외되었기 때문에 13명이 조회된다 -> self join 이 inner join 으로 적용되었기 때문 
    -- mgr 즉 매니저가 존재하지 않는 사원까지 모두 조회하고자 한다면 Outer Join을 적용하면 된다 
    -- Outer Join 연산기호 (+) 는 존재하지 않는 정보의 조건에 명시 : king 사원정보는 존재하고 매니저 정보가 존재하지 않으므로 매니저 측에 (+) 을 명시 
	SELECT e.ename, m.ename AS mgrEname
	FROM emp e, emp m
	WHERE e.mgr=m.empno(+)
	
    -- ANSI SQL로 표현 : table1 LEFT OUTER JOIN table2   : 왼쪽의 table1을 기준으로 table2 정보를 결합 , table 1의 모든 정보가 조회 
    SELECT e.ename, m.ename AS mgrEname
	FROM emp e
	LEFT OUTER JOIN emp m ON e.mgr=m.empno
	
	-- JOIN SQL 연습 
	-- empno 가 7369인 사원의 사원명ename,부서명dname,월급등급grade,매니저명mgr의 ename 을 조회 
	-- table : emp , dept , salgrade 
	SELECT e.ename, d.dname, s.grade , m.ename
	FROM emp e, dept d, emp m, salgrade s
	WHERE e.deptno=d.deptno
	AND e.mgr=m.empno
	AND e.sal>=s.losal 
	AND s.hisal>=e.sal
	AND e.empno=7369

	-- ANSI SQL 
	SELECT e.ename, d.dname, s.grade , m.ename
	FROM emp e
	INNER JOIN dept d ON e.deptno=d.deptno
	INNER JOIN emp m ON e.mgr=m.empno
	INNER JOIN salgrade s ON e.sal>=s.losal AND s.hisal>=e.sal
	WHERE e.empno=7369
	
	
	
	-- 답 : SMITH , RESERCH , 1, FORD  
	
	-- JOIN SQL 연습 
	-- 전체 사원의 사원명ename,부서명dname,월급등급grade,매니저명mgr의 ename 을 조회하되 매니저가 없는 사원까지 모두 조회한다  
	-- table : emp , dept , salgrade 
	
	-- outer join 연산기호 (+) 는 데이터가 없는 측의 조인조건에 기술 
    SELECT e.ename,d.dname,s.grade,m.ename
	FROM emp e, dept d, salgrade s, emp m
	WHERE e.mgr=m.empno(+) 
	AND e.sal>=s.losal 
	AND s.hisal>=e.sal
	AND e.deptno=d.deptno

	-- ANSI SQL 
	SELECT e.ename,d.dname,s.grade,m.ename
	FROM emp e
	JOIN dept d ON e.deptno=d.deptno
	LEFT OUTER JOIN emp m ON e.mgr=m.empno
	INNER JOIN salgrade s ON e.sal>=s.losal AND s.hisal>=e.sal
	
	
	
/******************************************/
--  join 연습을 위한 테이블 SQL 

DROP TABLE DEPT;

-- 부서 테이블 
CREATE TABLE DEPT(
DEPTNO NUMBER PRIMARY KEY,
DNAME VARCHAR2(14) ,
LOC VARCHAR2(13) 
) ;

DROP TABLE EMP;

-- 사원 테이블 
-- foreign key ( 부서 정보를 참조) 
CREATE TABLE EMP(
 EMPNO NUMBER  PRIMARY KEY, -- 사원번호
 ENAME VARCHAR2(10),--사원명
 JOB VARCHAR2(9),-- 직종
 MGR NUMBER, -- 매니저 사원 번호 
 HIREDATE DATE, -- 입사일 
 SAL NUMBER, -- 월급
 COMM NUMBER, -- 커미션  
 DEPTNO NUMBER,
 CONSTRAINT fk_deptno2 FOREIGN KEY(deptno) REFERENCES DEPT(deptno)
);

INSERT INTO DEPT VALUES
 (10,'ACCOUNTING','NEW YORK');
INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS');
INSERT INTO DEPT VALUES
 (30,'SALES','CHICAGO');
INSERT INTO DEPT VALUES
 (40,'OPERATIONS','BOSTON');
 
INSERT INTO EMP VALUES
(7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
INSERT INTO EMP VALUES
(7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
INSERT INTO EMP VALUES
(7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
INSERT INTO EMP VALUES
(7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
INSERT INTO EMP VALUES
(7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
INSERT INTO EMP VALUES
(7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
INSERT INTO EMP VALUES
(7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
INSERT INTO EMP VALUES
(7788,'SCOTT','ANALYST',7566,to_date('13-7-1987','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO EMP VALUES
(7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
INSERT INTO EMP VALUES
(7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
INSERT INTO EMP VALUES
(7876,'ADAMS','CLERK',7788,to_date('13-7-1987','dd-mm-yyyy'),1100,NULL,20);
INSERT INTO EMP VALUES
(7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
INSERT INTO EMP VALUES
(7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO EMP VALUES
(7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);

COMMIT

SELECT count(*) FROM dept; -- 4
SELECT count(*) FROM emp; -- 14

SELECT * FROM emp;

DROP TABLE salgrade;
CREATE TABLE salgrade(
     grade number PRIMARY KEY,
     losal number,
     hisal number
 )
INSERT INTO salgrade(grade,losal,hisal) VALUES(1,700,1200); 
INSERT INTO salgrade(grade,losal,hisal) VALUES(2,1201,1400);  
INSERT INTO salgrade(grade,losal,hisal) VALUES(3,1401,2000);  
INSERT INTO salgrade(grade,losal,hisal) VALUES(4,2001,3000);   
INSERT INTO salgrade(grade,losal,hisal) VALUES(5,3001,9999); 

COMMIT

SELECT COUNT(*) FROM salgrade;

SELECT * FROM salgrade;
SELECT * FROM emp;
    
SELECT count(*) FROM emp WHERE deptno=30;

SELECT ename FROM emp;

 

 

2. Lombok Test (프로젝트 새로 만들 때 Lombok dependencies 추가)

  • VO1
package org.kosta.myproject;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class MyLombokVO {
	private String id;
	private String name;
	private int price;
}
  • VO2
package org.kosta.myproject;

import lombok.Data;

@Data
//@Data : @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode 를 한번에 코드로 생성 
public class MyLombokVO2 {
	private String id;
	private String name;
	private int price;
}
  • VO3
package org.kosta.myproject;

import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class MyLombokVO3 {
	private String id;
	private String name;
	private int price;
	private String maker;
	private int point;
}
  • Test
package org.kosta.myproject;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MyLombokTest {

	@Test
	void contextLoads() {
		MyLombokVO myLombokVO=new MyLombokVO();
		myLombokVO.setId("java");
		System.out.println(myLombokVO.getId());
		System.out.println(myLombokVO);
	}
	
	@Test
	void myLombok2Test() {
		MyLombokVO2 myLombokVO=new MyLombokVO2();
		myLombokVO.setId("java");
		System.out.println(myLombokVO.getId());
		System.out.println(myLombokVO);
	}
	
	
	@Test
	void builderTest() {
		MyLombokVO3 mlb=MyLombokVO3.builder().id("java").name("아이유").price(100).maker("edam").point(500).build(); // 변수가 아주 많을 때
		System.out.println(mlb);
	}

}

java
MyLombokVO2(id=java, name=null, price=0)

java
MyLombokVO2(id=java, name=null, price=0)


MyLombokVO3(id=java, name=아이유, price=100, maker=edam, point=500)

 

 

 

 

 

 

//오늘의 숙제

layout복습+면접준비 따로 하기