콘텐츠로 이동

2023 05 23

2023-05-23

Slick Transaction

참고: https://effectivesquid.tistory.com/entry/Slick%EC%9D%84-%ED%86%B5%ED%95%9C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%A0%81%EC%9A%A9 참고: https://scala-slick.org/doc/3.3.1/database.html#the-cake-pattern

  • Cake Pattern
    1. slick은 DBIOAction을 통해 쿼리를 실행 (보통은 type alias된 DBIO 사용)
    2. future는 DBIOAction으로 바꾸고 싶다면 DBIO.from(...) 통해 간단하게 바꿀 수 있음
    3. for comprehension을 활용해 여러 DBIOAction을 트랜잭션으로 묶자
  • transactionally 를 통해 감싸주자
    val dbAction = (
        for {
            user <- userTable.doSomething
            address <- addressTable.doSomething
            contact <- contractTable.doSomething
        } yield()
    ).transactionally
    
    val resultFuture = db run dbAction
    

Slick DBIO Actions

참고: https://scala-slick.org/doc/3.0.0/dbio.html#transactions-and-pinned-sessions

  • 개요
    • 아래와 같은 DB 접근 행위는 DBIOAction의 인스턴스로 구성됨
      • myQuery.result
      • myTable.schema.create
      • myTable += item
    • DBIO Action은 여러개의 몇가지 컴비네이터로 조합할 수 있음
      • 순서에 맞게 항상 수행될 것이며,
      • 하나의 데이터베이스 세션을 사용할 것이다
  • DBIO 실행하는 법

    1. Materialized

      • run을 활용해 결과값 도출
        val q = for (c <- coffees) yield c.name
        val a = q.result
        val f: Future[Seq[String]] = db.run(a)
        
        f.onSuccess { case s => println(s"Result: $s") }
        
    2. Streaming

      • Collection-valued queries
      • Pub/Sub 구조로 수행될 수 있음
        • Publisher: Reactive Streams
        • Consumer: Akka Streams
          val q = for (c <- coffees) yield c.name
          val a = q.result
          val p: DatabasePublisher[String] = db.stream(a)
          
          p.foreach { s => pringln(s"Element: $s") }
          
    3. Pinned Sessions

      • DBIOAction인데 여러가지 작은 액션들의 조합이라면...
        • 슬릭은 커넥션 풀로부터 세션을 얻고,
        • db 계산이 아니라면,
        • 세션을 쓱 놔준다
      • withPinnedSession등을 사용해 사용중인 세션을 계속해서 non-db 계산에서도 사용할 수 있다
    4. Transactions

      • DBIOAction이 한번에 되거나, 한번에 안되거나
      • 트랜잭션으로 묶인 개별적인 DBIOAction에 대해서는 에러 복구에 대한 측면을 구현해두면 안된다
      • 실제 데이터베이스 트랜잭션은 가장 바깥쪽 트랜잭션에 대해서만 생성되며, 커밋/롤백 된다

transactionally

참고: https://github.com/slick/slick/issues/1425

  • 개요
    • multiple databases operation을 다루기 위함
    • DBIO 타입에 있다는데 (사실 변환하는 듯)
  • Implicit Conversions
    • JdbcActionComponent
      • Slick에서 database action을 수행하기 위함
      • 이걸 통해 transactionally 등의 메서드를 DBIO가 쓸 수 있도록 지원해줌
    • jdbcActionExtensionMethods
      • jdbcActionExtensionMethods를 통해 DBIO를 추가적인 메서드를 쓸 수 있도록 지원해준다
      • 이를 통해 transactionally를 쓸 수 있는 것
        implicit def jdbcActionExtensionMethods[E <: Effect, R, S <: NoStream](a: DBIOAction[R, S, E]): JdbcActionExtensionMethods[E, R, S] =
          new JdbcActionExtensionMethods[E, R, S](a)
        
  • 그니까...
    • DBIO --jdbcActionExtensionMethods--> JdbcActionComponent로 변환해줌
    • db.run 할 때 transactionally 메서드를 가진 DBIO를 만나면, transactionally flag를 트랜잭션 수행
  • OK 그러니까...
    • 결국에 Service에서 이 "transactionally"를 쓸라면 JdbcProfile을 Scope에서 쓸 수 있도록 import를 해와야해
      • 변환할 때 db.execute를 가져오는 과정에서 protected로 정의되어 있는 친구들은 클래스 레벨에서 임포트가 안 될거야
      • 필요한 클래스 내부에서 import 문으로 가져와보자