The sky is the limit

Vue.js、PHP、Javaなどに関する開発情報を発信しています。時々日常生活に関しても触れます

【エラー】Too many elements for Tuple: xx, allowed: 22【scala】

【エラー】Too many elements for Tuple: xx, allowed: 22【scala】

f:id:duo-taro100:20160218004611p:plain

Scalaでは関数やTupleに渡せる要素が最大で22となっているため、22以上の要素を渡した場合にこのようなエラーが出ます。
Scala 3では「渡せる要素が最大で22」の制限は撤廃されているようです。(未確認)

scalapedia.com

問題となった実装

object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    def * = (
    	id, a, b, c, d, e,f, g, h, 
    	i, j, k, l, m, n, o, p, q,
    	updateUser, updateDate, inputUser, inputDate, activeFlag) <> (TestData.tupled, TestData.unapply)
  }

ここではidとa〜q、そしてupdateUser, updateDate, inputUser, inputDate, activeFlagの計23の要素を渡して、TestData.tupledとしています。
なので前述のエラーが発生します。

解決策

この問題の解決策としてよく提示されているものとしてHListsを使うというものがありました。
underscore.io

以下のような感じにするみたいです。

object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    def * = id :: a :: b :: c :: d :: 省略 :: activeFlag
  }

ですが、この方法では自分はうまく動かなかったので別の方法を探ってみました。
結論としては「shaped」を使う以下の方法で実装しました。
本来CommonDataは別ファイルで定義していますが、ここでは1ファイルで完結できるようにcase classで定義しています。
TestDataは面倒なので省略しました。

case class CommonData
(
  var updateUser: String,
  var updateDate: Timestamp,
  var inputUser: String,
  var inputDate: Timestamp,
  var activeFlag: String
)


object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    private val shapedValue = (
      id, a, b, c, d, e,
      f, g, h,
      i, j, k, l, m, n, o,p, q,
      (updateUser, updateDate, inputUser, inputDate, activeFlag)
    ).shaped

    def * = shapedValue <> ( 
    	{
	      case (
	        id, a, b, c, d, e,
	        f, g, h,
	        i, j, k, l, m, n, o,p, q,
	        commonData
	        ) =>
	        TestData(
	          id, a, b, c, d, e,
	          f, g, h,
	          i, j, k, l, m, n, o,p, q,
	          CommonData.tupled.apply(commonData)
	        )
	    }, { u: TestData =>
	      Some(
	        (
	          u.id,
	          u.a,
	          u.b,
	          u.c,
	          u.d,
	          u.e,
	          u.f,
	          u.g,
	          u.h,
	          u.i,
	          u.j,
	          u.k,
	          u.l,
	          u.m,
	          u.n,
	          u.o,
	          u.p,
	          u.q,
	          CommonData.unapply(u.commonData).get
	        )
	      )
	    }
    )
  }

なんとかできました。