Jam's story

[Spring] 스프링 트랜잭션 격리성 본문

Spring

[Spring] 스프링 트랜잭션 격리성

애플쩀 2022. 7. 19. 15:49

[스프링 전파방식 Propagation]

springTransaction04

method(){}

  1. insert
  2. update

중첩 트랜잭션 처리될 때 전파방식이 의미있다.

  1. 전파방식을 테스트
    • insertAndPointUpOfMember()
      1. 새글쓰기 insert()
      2. 포인트증가

한 일

  1. insert 수정

propagation

  • required 현재 진행중인 트랜잭션이 있으면 해당 트랜잭션을 사용하고 없으면 새로운 트랜잭션을 생성한다.
  • requires_new 항상 새로운 트랜잭션을 시작한다.

 


 

오류메세지

<context:component-scan base-package="newlecture, controllers" />

스프링 트랜잭션 격리 레벨

체크 제약조건 삭제

유일성 제약조건 삭제

 

sql

SELECT *
FROM user_constraints
WHERE table_name ='MEMBER';
WHERE table_name ='NOTICES';

ALTER TABLE MEMBER 
DROP constraint CK_MEMBER_POINT;

ALTER TABLE Notices
DROP constraint UK_NOTICE_TITLE;

 

격리성

서로 동시에 같은자원에 접근하면 트랜잭션 처리를 어떻게 해야하나?

➡️격리레벨(수준)을 지정해줘야한다.

 

 

  1. 트랜잭션 처리과정에 두 개 이상의 트랜잭션이 동시에 공유자원에 접근하게 되면
  2. 예) 멀티스레드 (동기화처리)
    • 임계영역 장금(Lock)
    • 기다려 (wait) - notifyAll()
  3. 트랜잭션의 동시성에 따른 문제점
    • hitUp() 메서드: 조회수를 증가하는 메서드
    • getHit() 메서드: 조회수를 읽어와서 반환하는 메서드
    • 위의 2가지 메서드가 동시에 실행된다고 가정.
  4. 용어
    • Dirty Read 상황: 잘못된 데이터를 읽어간 상황
      • A 트랜잭션 hitUp() 조회수 증가
      • B 트랜잭션 getHit() 조회수 읽어
    • Non-Repeatable Read 상황: 반복적으로 읽었는데 값이 매번 달라지는 경우
    • Phantom Read 상황
      • 여러 개의 행 (레코드) 를 한번에 읽어오는 과정 (작업) 을 반복하는 경우
      • A 트랜잭션이 똑같은작업(Emp테이블에서 연봉 top 10을 읽어오기)을 여러번 하는데 , B트랜잭션이 king=5000삭제 (or 500으로 수정= 가장작은 sal)-> 처음 레코드 값과 나중의 레코드 값이 다르게 된다.
  5. 트랜잭션 격리 레벨
    • DEFAULT: 기본설정 (DBMS 의 격리성 수준)
    • READ_COMMITED: 커밋이 경우에만 READ 실행.
      • Dirty READ 상황 X, non-repeatable read 상황 O
    • READ_UNCOMMITED: 커밋이 되지 않은경우에도 read 실행가능
      • dirty read 상황 O, non-repeatable read 상황
    • REPEATABLE_READ: 특정 행을 잠그고 작업을 진행. 두 상황 모두 일어나지 않는다.
    • SERIALIZABLE: 모든 행 (테이블 전체) 를 잠그고 작업을 진행.
      • 팬텀리드상황 X
  6. hitUp() / getHit() 추가/수정.
  7. CustomerController - noticeDetail() 상세보기 수정

 

 

1️⃣트랜잭션 격리성을 테스트 하기 위한 용도 메소드 추가

	public void hitUp(String seq);
	public int getHit(String seq);

 

2️⃣NLNoticeDao.java

@Override
public void hitUp(String seq) {
	String sql="UPDATE notice"
			+ " SET hit=hit+1"
			+ " WHERE seq=:seq";
	MapSqlParameterSource paramSource=new MapSqlParameterSource();
	paramSource.addValue("seq", seq);
	this.jdbcTemplate.update(sql, paramSource);
}

@Override
public int getHit(String seq) {
	String sql="SELECT hit "
			+ " FROM NOTICE"
			+ " WHERE seq=:seq";
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put("seq", seq);
    
    int hit = this.jdbcTemplate.queryForInt(sql, paramMap);
    
    System.out.printf("<<  getHit.hit = %d\n", hit);
    
    return hit;
}

 

강제로 Dirty Read 상황 발생시키기

CustomerController-noticeDetail()

  @RequestMapping("noticeDetail.htm")
   public String noticeDetail(
         Model model
         , HttpSession session
         , String seq //?seq=1
         ) throws Exception {

	   //스레드 2개 생성해서 처리하기 
	   //조회수를 읽어오는 스레드  
	   //0,5초 쉬기때문에 그다음메소드가 먼저 실행돈다. 
	   new Thread(new Runnable() {
		   @Override
		   public void run() {
			   try {
				Thread.sleep(500);
			} catch (Exception e) {
				e.printStackTrace();
			}
			   noticeDao.getHit(seq); //0.5초 후에 조회수를 읽어가는 코딩 
		   }//run
	   }, "getHit_Thread").start();
	   
	   //조회수를 증가시키는 스레드 
	   new Thread(new Runnable() {
		   @Override
		   public void run() {}//run
	   }, "hitup_Thread").start();
	
	  //조회수 증가  
	   this.noticeDao.hitUp(seq);
	   
       Notice notice=this.noticeDao.getNotice(seq);

       model.addAttribute("notice", notice);
       
       return "noticeDetail.jsp";
   }

 

 

더티리드 만들기

NLNoticeDao.java


@Override
//@Transactional(isolation=Isolation.READ_COMMITTED)
@Transactional(isolation=Isolation.READ_UNCOMMITTED) //더티리드 
public int getHit(String seq) {
	String sql="SELECT hit "
			+ " FROM NOTICE"
			+ " WHERE seq=:seq";
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put("seq", seq);
    
    int hit = this.jdbcTemplate.queryForInt(sql, paramMap);
    
    System.out.printf("<<  getHit.hit = %d\n", hit);
    
    return hit;
}

 

 


오후시간

springTiles 프로젝트 생성

 

1.격리수준- 설정코딩수정

CustomerController.noticeDetali()수정

NLNoticeDao.hitUp() 수정

 

2.스프링타일즈

1)페이지 모듈화(집중화)

페이지 마다 공통적인 영역을 모듈화해서 공통적으로 참조할 수 있도록 하는 방법

2)JSP 프로젝트 -@include 지시자, <jsp:include>액션태그

 layout폴더

ㄴTOP

ㄴBottom

ㄴAside

 

 

다운로드

http://archive.apache.org/dist/tiles/v2.2.2/

 

압축풀어서 라이브러리 추가하기

 

 

webapp 폴더      
                    ㄴ inc 폴더  - 모든 웹 페이지에서 공통적인 영역
                             ㄴ header.jsp
                             ㄴ footer.jsp
                             ㄴ layout.jsp
                             
                     ㄴ. joinus 폴더
                              ㄴ inc 폴더
                                     ㄴ visual.jsp
                                     ㄴ aside.jsp
                                     ㄴ layout.jsp
                                             
                     ㄴ.  customer 폴더         
                             ㄴ inc 폴더  - 모든 웹 페이지에서 공통적인 영역
                                   ㄴ visual.jsp
                                   ㄴ aside.jsp
                                   ㄴ layout.jsp   

 

 

tiles-defs.xml

타일붙이듯이 타일처럼 붙이겟다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
<!--   <definition name="myapp.homepage" template="/layouts/classic.jsp">
    <put-attribute name="title" value="Tiles tutorial homepage" />
    <put-attribute name="header" value="/tiles/banner.jsp" />
    <put-attribute name="menu" value="/tiles/common_menu.jsp" />
    <put-attribute name="body" value="/tiles/home_body.jsp" />
    <put-attribute name="footer" value="/tiles/credits.jsp" />
  </definition> -->
  <definition>
  </definition>
</tiles-definitions>

 

컨트롤러

마지막에 수정 return "costomer.notice"; //스프링 타일즈 뷰리절브

	@RequestMapping("notice.htm")
	public String notices(
			// ?page=1&field=title&query=hh
			@RequestParam( value = "page", defaultValue = "1" )  int page
			, @RequestParam( value = "field", defaultValue = "title" )  String field
			, @RequestParam( value = "query", defaultValue = "" )  String query
			, Model model
			) throws Exception { 

		List<Notice> list=  this.noticeDao.getNotices( page, field, query);
		model.addAttribute("list", list);  
		model.addAttribute("test", "Hello, Spring MVC World!");

		return "costomer.notice"; //스프링 타일즈 뷰리절브

	} // notices

 

dispatcher-servlet.xml

스프링타일즈 사용하기 위해 필요한 빈 객체

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
         xmlns:aop="http://www.springframework.org/schema/aop" 
         xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop-3.0.xsd
               http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd">         
    <!-- dispatcher-servlet.xml -->
   <context:component-scan base-package="newlecture, controllers"></context:component-scan>

   <!-- p439 -->
   <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
      <property name="maxUploadSize" value="-1"></property>
   </bean>
   
   <!-- p524 @transactional 어노테이션 -->
   <tx:annotation-driven 
      transaction-manager="transactionManager"
      mode="proxy"
      proxy-target-class="false"
   />
   
   <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
       <property name="definitions" value="/WEB-INF/tiles-defs.xml"></property>
   </bean>
   <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
      <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"></property>
   </bean>

   <!-- ViewResolver 등록 -->
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
</beans>

 

'Spring' 카테고리의 다른 글

[Spring] 스프링시큐리티 - 로그인  (0) 2022.07.21
[Spring] 스프링 레거시 프로젝트 4.0버전  (0) 2022.07.20
[Spring] 트랜잭션  (0) 2022.07.18
[Spring] NamedParameterJdbcTemplate  (0) 2022.07.18
[Spring] JDBC  (0) 2022.07.17
Comments