본문 바로가기

_Programming/Spring

Spring.AOP(Aspect-Oriented Programming)

AOP

Spring의 3대 요소중 하나로,

Aspect-Oriented Programming

관점지향 프로그래밍을 뜻한다.

 

Spring Transaction 기술로 AOP를 구현한다.

 

예를들어

StartPoint : 카드정보 확인 --> point : 결제정보(금액,장소..등등)

--> point : 정보전송 & return(ok/deny) --> EndPoint : 영수증출력

 

하나하나의 과정이 포인트가 되고 그 과정이 끝나는 부분이 포인트 컷이 된다.

 

point pointCut ----> point pointCut ---> point ponintCut ---> point

이 point 하나를 Aspect(관점)이라한다.

 

point중심으로 실행되도록 관심사로 묶는다.

이제 바로 트랜잭션개념이다.

 

트랜잭션이란 데이터베이스의 상태를 변화시키는 기능을 수행하기 위한 작업의 단위이다.

여기서 데이터베이스의 상태를 변화시킨 다는 것은 SQL문을 이용하여 데이터베이스에 접근하는 것이다.

 

Service에서 이 역할을 수행할 수 있다(여기에서 필요한 다오들을 불러올 수 있다.)

select insert update select의 수행이 같은 메소드 안에서 행하여져야 한다.

1. 이 두개를 사용한다.

//pom.xml
<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

2. DB를 셋팅한다.

다시 셋팅하는 이유는 우리가 처음 pno생성시 tbl_comment과 연결해놧기때문에

그 연결을 끊고 JUNITest를 이용해 product의 comment를 자동으로 넣기 위해서.

drop sequence seq_product_pno;
drop sequence seq_comment_cno;


create sequence seq_product_pno
start with 1
increment by 1
nocycle nocache;


create sequence seq_comment_cno
start with 1
increment by 1
nocycle nocache;


delete from tbl_product;
commit;
truncate table tbl_comment;


alter table tbl_product
add (cmtcnt number(10,0) default 0);

 

결과로 tbl_product와 tbl_comment이 데이터가 초기화 되고 tbl_product에 column으로 cmtcnt만 추가되어 있으면 된다.

3. ProductMapper에서 list부분에 cmtcnt추가.

<select id="list" parameterType="Criterion" resultType="ProductVO">
    <![CDATA[        
    select pno,title,writer,readcount,modd8,imgfile,cmtcnt
    from (select /*+INDEX_DESC(tbl_product pk_product)*/ 
          rownum as rn, pno,title,writer,readcount,modd8,imgfile,cmtcnt
          from tbl_product
          where pno > 0
    ]]>

4. VO객체에서 list부분에 int cmtcnt를 추가.

//ProductVO.java
//list용
public ProductVO(String title, String content, String writer, int price, String imgfile, int cmtcnt) {
  this.title = title;
  this.content = content;
  this.writer = writer;
  this.price = price;
  this.imgfile = imgfile;
  this.cmtcnt = cmtcnt;
}

5. Comment 생성

src/test/java/com/myweb/java에 먼저 test로 Product를 자동 생성해준다.

그 후 에 댓글을 한 Product당 랜덤갯수만큼 Comment를 생성한다.

@Test public void insertProductDummy() { 
  for (int i = 0; i < 255; i++) {
    ProductVO pvo = new ProductVO(); pvo.setTitle(i + "번째 상품명");
    pvo.setWriter("admin@admin.com"); pvo.setContent(i+"번째 상품 상세 정보");
    pvo.setPrice(i+10000); pvo.setImgfile("NONE"); pdao.insertProduct(pvo); 
  } 
}

 

생성후 DBeaver에서 조회해보면

SELECT pno, COUNT(cno) FROM TBL_COMMENT 
GROUP BY PNO 
HAVING pno > 0
ORDER BY pno DESC;

255개의 상품과 각각에 해당하는 랜덤숫자갯수의 댓글이 생성된 것을 알 수 있다.

6. root-context.xml에 트랜잭션 적용

root-context.xml에서 아래 Namespaces선택하고 aop, tx를 체크하면

Sourc에 아래 두 가지가 추가 된 것을 알 수 있다.

xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"

 

autoproxy : point마다 데이터들이 이동할 때 자동으로 저장소를 생성해주는 개념을 생성해줌(결과값의 실패 성공)

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

여기서 ref의 dataSource가 맨 위 히카리의 dataSource이다.

db에 트랜잭션을 검.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven/>

 7. 댓글 갯수 표시

product list.jsp에 댓글 갯수가 표시 되는 화면단을 만든다.

역시나 Bootstrap에 Badges에서 맘에 드는 것을 컨택. 

<td>${pvo.writer } <span class="badge badge-warning">${pvo.cmtcnt }</span> </td>

 여기서 그냥 이대로 실행하면 만든 저 ${pvo.cmtcnt} 안에 아무것도 들어와있지 않음을 알 수 있다.

아직 tbp_product에 cmtcnt값고 tbl_comment에 count(cno) 값을 넣어주기 않았기때문에

따라서 DBeaver에서 넣어준다.

UPDATE tbl_product
SET cmtcnt = (
SELECT COUNT(cno) FROM tbl_comment
WHERE TBL_COMMENT.PNO = TBL_PRODUCT.PNO 
GROUP BY pno 
HAVING pno > 0);

8. Transaction 이용한 조회수 올리기

detail.jsp에서 조회수 올리기 - Transaction개념.

 

ProductCtrl의 detail부분에서 getProduct(pno)를 실어 보냈으므로 따라가보면

@GetMapping({ "/detail", "/modify" })
public void detail() {
  model.addAttribute("pvo", psv.getProduct(pno));
}

Service부분에는 getProduct로 pno을 받고

--> ServiceImpl에서는 조회수를 올려주는 놈(updateReadCount)을 만들어줘야한다.

//ProductServiec.java
public ProductVO getProduct(int pno);
//ProductServiceImpl.java
@Override
public ProductVO getProduct(int pno) {
  pdao.updateReadCount(pno);
  return pdao.selectProduct(pno);
}

 

빨간 에러를 쭉쭉 따라가면 다음은

dao단에 updateReadCount에 대한 부분을 빌드해주고

"upRd"의 이름으로 pno을 가지고 Mapper로 이동한다.

//ProductDAO.java
public void updateReadCount(int pno);
//ProductDAOImpl.java
@Override
public int updateReadCount(int pno) {
  return sql.update(ns+"upRd", pno);
}

 

Mapper에 upRd의 이름으로 pno을 받고 조회수를 올려주는 update sql문을 작성한다.

<update id="upRd" parameterType="java.lang.Integer">
  update tbl_product set readcount = readcount+1
  where pno =#{pno}
</update>

 

이제 다시 ProductServiceImpl로 돌아가보면 update 되었으면 그 때 selectProduct가 돌아가고 상품디테일을 불러오게 되는데

여기서 Transaction개념이 적용된다.

 

Transaction을 적용하려면 @Transactional하고 advice를 해줘야 하는데

많은 종류의 advice가 있지만 그 중 commit한걸 읽어오라는 advice인 Isolation.READ_COMMITTED를 사용한다.

//ProductServiceImpl.java
@Transactional(isolation = Isolation.READ_COMMITTED)
@Override
public ProductVO getProduct(int pno) {
  pdao.updateReadCount(pno);
  return pdao.selectProduct(pno);
}

 

여기서 Transaction개념을 적용해보자면

getProduct전체가 Transaction이 적용되는 부분이고 그 안에 있는

updateReadCount와 selectProduct가 각각 point가 되고

advice부분이 이 point를 나누는 point cut이 된다.

 

먼저 updateReadCount가 실행되고 그 결과 값을

autoproxy에 저장을 하고 있다가

selectProduct의 결과 값도 성공으로 떨어지면

getProduct전체가 성공으로 되고

둘 중 하나라도 실패가 되면 getProduct 전체가 실패가 되게 된다.

 

위 point중 하나라도 실패한다면 getProduct전체가 실패로 끝나게 된다.

comment remove과정 (리팩토링 과정과 비교하기!!)

결국의 목적은 댓글 삭제시 댓글 갯수를 다운시켜주는건데

그럴려면 상품의 pno값을 가져와야 한다.

 

CommentServiceImpl에서 DB의 pno을 가져오려면 해당 메소드를 생성해줘야한다.

//commentServiceImpl
@Transactional
@Override
public int remove(int cno) {
  //내가 cno를 던지면 해당 pno를 주는 메서드생성
  int pno = cdao.selectPno(cno);  
  return cdao.deleteComment(cno);
}
//CommentDAO
public int selectPno(int cno);
//CommentDAOImpl
Override
public int selectPno(int cno) {
  return sql.selectOne(ns+"findPno", cno);
}
//commentServiceImpl
@Override
public int remove(int cno) {
  int pno = cdao.selectPno(cno);
  pdao.updateCmtCntDown(pno);
  return cdao.deleteComment(cno);
}

 

 

 

 

------------------------------------------------------------------------------------------------------------------------

 

 

 

 

'_Programming > Spring' 카테고리의 다른 글

Spring.TeamProject (Cpos)  (0) 2020.07.13
Spring.Security_basic  (0) 2020.07.07
Spring.Comment  (0) 2020.07.02
Spring.Search(검색기능)  (0) 2020.07.02
Spring.페이징(Paging)  (0) 2020.07.01