ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 7. 마지막 세션
    Kotlin/실전! 코틀린과 스프링 부트로 ... 개발 2023. 8. 8. 20:29

    추가 - 테스트와 영속성 컨텍스트

    - 테스트 코드와 LazyInitializationException

        ㆍ테스트 코드에서 A 엔티티를 불러오고 B객체에 접근을 하면

             영속성 컨텍스트가 종료되어 버려서 지연 로딩 객체를 초기화할 수 없게 될 경우

     

    - 테스트 코드에서 영속성 컨텍스트를 쓰고 싶은 상황이 분명 있음!

     

    @Transactional
    fun saveUserAndLoanTwoBooks() {
        val newUser = User("A", 123)
        val books = bookRepository.saveAll(listOf(Book("책1", COMPUTER), Book("책2", COMPUTER)))
        books.forEach { book -> newUser.loanBook(book) }    // UserLoanHistory 2개 생길겁니다!
        userRepository.save(newUser)
    }
    @SpringBootTest
    class TempTest @Autowired constructor(
        private val userService: UserService,
        private val userRepository: UserRepository,
    ) {
    
        @Test
        fun `유저 1명과 책 2권을 저장하고 대출한다`() {
            // when
            userService.saveUserAndLoanTwoBooks()
    
            // then
            val users = userRepository.findAll()
            assertThat(users).hasSize(1)
            assertThat(users[0].userLoanHistories).hasSize(2)
        }
    }

     

    [방법1] @Transactional

    - 테스트 코드에 @Transactional 을 붙인다.

    @Transactional
    @Test
    fun `유저 1명과 책 2권을 저장하고 대출한다`() {
        // when
        userService.saveUserAndLoanTwoBooks()
    
        // then
        val users = userRepository.findAll()
        assertThat(users).hasSize(1)
        assertThat(users[0].userLoanHistories).hasSize(2)
    }

    - 장점

        ㆍ간결하다! 롤백도 된다!

        ㆍ트랜잭션별로 테스트를 격리할 수 있어, 병렬 테스트도 가능하다!

    - 단점

        ㆍ테스트 내성이 떨어진다.

     

    * 트랜잭션널을 사용하지 않는다면?! (데이터 삭제 자동화)

    @SpringBootTest
    class CleaningSpringBootTest {
        @Autowired
        private lateinit var repositories: List<JpaRepository<*, *>>
    
        @AfterEach
        fun clean() {
            val currentMillis = System.currentTimeMillis()
            repositories.forEach { it.deleteAll() }
            println("소요 시간 : ${System.currentTimeMillis() - currentMillis}")
        }
    }

    - CleaningSpringBootTest를 상속받게 하면 자동으로 데이터들을 삭제해준다.

     

     

    [방법2]  N쪽의 Repository를 활용한다.

    @Test
    fun `유저 1명과 책 2권을 저장하고 대출한다`() {
        // when
        userService.saveUserAndLoanTwoBooks()
    
        // then
        val users = userRepository.findAll()
        assertThat(users).hasSize(1)
    
        val histories = userLoanHistoryRepository.findAll()
        assertThat(histories).hasSize(2)
        assertThat(histories[0].user.id).isEqualTo(users[0].id)
    }

     

    [방법 3] fetch join을 사용해 미리 불러온다

    @Test
    fun `유저 1명과 책 2권을 저장하고 대출한다`() {
        // when
        userService.saveUserAndLoanTwoBooks()
    
        // then
        val users = userRepository.findAllWithHistories()
        assertThat(users).hasSize(1)
        assertThat(users[0].userLoanHistories).hasSize(2)
    }

    - fetch join은 1개의 N에 대해서만 사용할 수 있다.

     

    [방법 4] TxHelper를 이용한다

    @Component
    class TxHelper {
    
        @Transactional
        fun exec(block: () -> Unit) {
            block()
        }
    }
    // then
    txHelper.exec {
        val users = userRepository.findAllWithHistories()
        assertThat(users).hasSize(1)
        assertThat(users[0].userLoanHistories).hasSize(2)
    }

     

     


    추가 - 코프링과 플러그인

    1. 스프링 컴포넌트와 spring 플러그인

    id 'org.jetbrains.kotlin.plugin.spring' version '1.6.21'

    - @Component, @Transactional 이 붙어있는 함수나 클래스를 자동으로 열어준다.

     

    왜 오픈되어야 하는가?

    @Service
    class AService {
        
        @Transactional
        fun saveEntity() {
        	// 로직
        }
    }

    - Service는 왜 Open 되어야 하는가!

        ㆍ프록시라는 것 때문!

    - 스프링이 시작될 때 프록시 객체가 AService를 상속받고 오버라이드한 함수가 생긴다.

        ㆍ그렇기에 오픈 되어야 한다.

     

    2. JPA 객체와 기본 생성자

    id 'org.jetbrains.kotlin.plugin.jpa' version '1.6.21'

    - 엔티티객체, 매퍼슈퍼클래스, 임베디드 객체에 기본 생성자를 만들어주는 역할

     

    3. JPA 객체와 open

    id 'org.jetbrains.kotlin.plugin.allopen' version '1.6.21'
    
    allOpen {
        annotation("javax.persistence.Entity")
        annotation("javax.persistence.MappedSuperclass")
        annotation("javax.persistence.Embeddable")
    }

    - 프록시 때문에 오픈해준다.

    댓글

Designed by Tistory.