[Slick 3.1.1+Play 2.4.6] Slick에서 MySQL의 DateTime처리하기 + 부분 Column만 Insert하기.

제목 그대로, 한 하루정도 삽질했는데 하도 한글 레퍼런스부터 영문까지도 잘 없어서 포스팅한다.
솔직히 말해 플레이 레퍼런스 없어도 너무 없다. 스칼라 자체는 좀 많은데, 플레이는 자바는 좀 있어도 스칼라 자체는 없으니.. 플레이 하며 프레임워크적인 것은 그렇다 쳐도 대부분은 사실 스칼라 언어의 이해적인 문제이니.. 뭐 implicit나 Future같은 것들 말이다 ㅎㅎ 결국 기본적으로 스칼라를 더 깊게 공부해야 할 필요성을 느낌..

그나저나 내가 너무 앞서서 공부하는 것이 아닌가 싶기도 한데(여기 실리콘벨리에서도 워낙 사용자도 없고 생소해서리) 그래도 스칼라는 정말 풀스택 개발이나 스타트업 환경에서는 정말 좋은 것이 사실이고, 비용 절감에서는 확실히 효율적일 것 같다. 기본적으로 비동기 방식으로 설계된 점이 마음에 들었고, 지금 자바나 여타 언어의 추세를 봐도 스칼라처럼 발전하는 것같이 보이니깐.

서버단으로 보면 Microarchitecture가 대세인 지금, 플레이로 비동기 서버를 여럿 만들어 둔다면 유저의 흐름에 따라 유연하게 대응할 수 있다. ThreadPool방식이 아니니깐, 모든 것이 정말 핵심적으로 순수하게 Micro하게 동작하니깐..

어쨌든, 근 2일간 삽질한 포스팅.

Slick에서 MySQL의 DateTime처리하기

이 부분은 생각보다는 쉬운데, 처음에는 그냥 Timestamp사용해다 하려했는데, 생각처럼 안된다. 그래서 찾다보니 tototoshi님이 만든 slick-joda-mapper 라는게 있더라.

https://github.com/tototoshi/slick-joda-mapper

사용법은 써있긴 한데, build.sbt에 joda라이브러리 설명대로 추가해주고,

https://github.com/tototoshi/slick-joda-mapper/blob/master/src/test/scala/com/github/tototoshi/slick/JodaSupportSpec.scala

위 예제 파일처럼 처리하면 org.joda.time.DateTime 형식을 DAO에서 뭐 MySQL의 경우는

import com.github.tototoshi.slick.MySQLJodaSupport._

이것 한줄로 사용할 수 있다. 안그러면 맵핑 에러가 발생.

 

Slick에서 부분 Column만 Insert하기.

이게 뭔말이냐면, MySQL의 필드 중 Nullable인 경우는 Insert를 안하고자 할때, 어떻게 할 것이냐는 거다.

이걸 정말 몇 시간동안 삽질했는데.. 슬릭 3.1.1 공용문서의 경우에는

http://slick.typesafe.com/doc/3.1.1/queries.html#inserting


coffees.map(c => (c.name, c.supID, c.price)) += ("Colombian_Decaf", 101, 8.99)

뭐 이렇게만 쓰라는데 도통 되야지.. 내가 잘못 설정했나.. 하도 안되길래 몇 번의 삽질 끝에 그냥 소스 공개한다.

해당 내용은, Checklist라는 모델 안의 여러 필드 중, seq_plan, write_date, checked_value 만 값을 insert하는 경우이다.

 

// ChecklistDAO.scala

package dao

import models.Checklist
import play.api.Play
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfig}
import slick.driver.JdbcProfile
import org.joda.time._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

import com.github.tototoshi.slick.MySQLJodaSupport._

/**
 * Created by changmatthew on 2/14/16.
 */
class ChecklistDAO extends HasDatabaseConfig[JdbcProfile]{
private val Checklists = TableQuery[ChecklistsTable]
def insert(seq_plan:Int,write_date:DateTime,checked_value:String): Future[Unit] = {

 val insertAction = DBIO.seq(Checklists.map(c => (c.seq_plan, c.write_date, c.checked_value)).returning(Checklists.map(_.seq)) += ((seq_plan,write_date, checked_value)))

 db.run(insertAction)
}
private class ChecklistsTable(tag: Tag) extends Table[Checklist](tag, "Checklist"){

    def seq = column[Int]("seq", O.PrimaryKey, O.AutoInc)
    def seq_plan = column[Int]("seq_plan")
    def write_date = column[DateTime]("write_date")
    def start_date = column[DateTime]("start_date")
    def end_date = column[DateTime]("end_date")
    def is_checked = column[Int]("is_checked")
    def checked_value = column[String]("checked_value")
    def success_percent = column[Double]("success_percent")
 def * = (seq, seq_plan, write_date, start_date.?, end_date.?, is_checked.?, checked_value.?, success_percent.?) <> ((Checklist.apply _).tupled, Checklist.unapply _)
 }
}
// Checklist.scala
package models
import java.sql.Timestamp

import org.joda.time.DateTime
import play.api.libs.json.Json

case class Checklist(
                      seq:Int,
                      seq_plan:Int,
                      write_date:DateTime,
                      start_date:Option[DateTime],
                      end_date:Option[DateTime],
                      is_checked:Option[Int],
                      checked_value:Option[String],
                      success_percent:Option[Double]
                      )

object Checklist {
  //JSON read/write
  implicit val userFormat = Json.format[Checklist]
}

위와 같이 DAO와 모델이 구성되면,

// ChecklistController.scala

package controllers

import java.text.SimpleDateFormat
import java.util.Calendar

import com.fasterxml.jackson.annotation.JsonValue
import dao.ChecklistDAO
import models.Checklist
import org.joda.time.DateTime
import org.slf4j.{LoggerFactory, Logger}
import play.api.libs.json.{Json, JsResult}
import play.api.libs.json.Json.toJson
import play.api.mvc._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class ChecklistController extends Controller{
  private final val logger: Logger = LoggerFactory.getLogger(classOf[ChecklistController])
  def categoryDao = new CategoryDAO
  def checklistDao = new ChecklistDAO

  // 중략
  def write(seq_plan:Int,write_date:DateTime,checked_value:String) = Action.async{ request =>

   val rslt = checklistDao.insert(seq_plan, write_date, checked_value)
   Future(Ok(toJson(new ResponseParam("OK",100,"",toJson("")))))
  }
}

이렇게 컨트롤러를 구현하고, Routes에서 물려서 처리하면 된다. 핵심은 DBIO.seq내에 넣을 때 새롭게 mapping한 case class를 가지고 += 로 넣어줘야 한다는 것.. 이 외에도 보안로그인을 위해 CSRF/CORS필터처리한 부분이랑 특히 AngularJS와 RESTful로 패킷 동기화 시키는 부분 삽질했었는데, 이부분도 추후 정리해서 올리겠다.