그래서 sqlite4java sqlite 인터페이스 주위에 간단한 래퍼를 작성하고 싶었 기 때문에이 질문을했습니다. 쿼리에서 행 유형이 준비된 문에 지정 될 수있는 다음과 같은 형식의 코드를 사용하려면 (I 비슷한 방법에 따라 전달 된 매개 변수에 유형 검사를 추가 할 예정) :
test("SQLite wrapper test")
{
val db = new SQLiteWrapper()
db.exec("BEGIN")
db.exec("CREATE TABLE test(number INTEGER, value FLOAT, name TEXT)")
val insStatement = db.prepare("INSERT INTO test VALUES(?, ?, ?)", HNil)
insStatement.exec(1, 5.0, "Hello1")
insStatement.exec(2, 6.0, "Hello2")
insStatement.exec(3, 7.0, "Hello3")
insStatement.exec(4, 8.0, "Hello4")
val getStatement = db.prepare("SELECT * from test", Col[Int]::Col[Double]::Col[String]::HNil)
assert(getStatement.step() === true)
assert(_1(getStatement.row) === Some(1))
assert(_2(getStatement.row) === Some(5.0))
assert(_3(getStatement.row) === Some("Hello1"))
getStatement.reset()
db.exec("ROLLBACK")
}
그리고이 가능하도록 , 도움이되는 다양한 제안을 사용하여 나는 아래의 코드를 생각 해냈다. 이것은 스칼라의 제네릭 프로그래밍의 모든 형태에 대한 첫 번째 시도입니다. 나는 단지 1, 2 주 동안 언어를 가지고 놀았습니다. 그래서 이것은 경험있는 스칼라 커뮤니티에 의해 코드가 좋고/좋다고 생각되지 않을 것입니다. 모든 제안/의견 환영 ... ....
import java.io.File
import com.almworks.sqlite4java._
object SqliteWrapper
{
trait TypedCol[T]
{
var v : Option[T] = None
def assign(res : SQLiteStatement, index : Int)
}
sealed trait HList
{
def assign(res : SQLiteStatement, index : Int)
}
final case class HCons[H <: TypedCol[_], T <: HList](var head : H, tail : T) extends HList
{
def ::[T <: TypedCol[_]](v : T) = HCons(v, this)
def assign(res : SQLiteStatement, index : Int)
{
head.assign(res, index)
tail.assign(res, index+1)
}
}
final class HNil extends HList
{
def ::[T <: TypedCol[_]](v : T) = HCons(v, this)
def assign(res : SQLiteStatement, index : Int)
{
}
}
type ::[H <: TypedCol[_], T <: HList] = HCons[H, T]
val HNil = new HNil()
final class IntCol extends TypedCol[Int]
{
def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnInt(index)) }
}
final class DoubleCol extends TypedCol[Double]
{
def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnDouble(index)) }
}
final class StringCol extends TypedCol[String]
{
def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnString(index)) }
}
trait TypedColMaker[T]
{
def build() : TypedCol[T]
}
object TypedColMaker
{
implicit object IntColMaker extends TypedColMaker[Int]
{
def build() : TypedCol[Int] = new IntCol()
}
implicit object DoubleColMaker extends TypedColMaker[Double]
{
def build() : TypedCol[Double] = new DoubleCol()
}
implicit object StringColMaker extends TypedColMaker[String]
{
def build() : TypedCol[String] = new StringCol()
}
}
def Col[T : TypedColMaker]() = implicitly[TypedColMaker[T]].build()
// Hideousness. Improve as Scala metaprogramming ability improves
def _1[H <: TypedCol[_], T <: HList](t : HCons[H, T]) = t.head.v
def _2[H1 <: TypedCol[_], H2 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, T]]) = t.tail.head.v
def _3[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, T]]]) = t.tail.tail.head.v
def _4[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, T]]]]) = t.tail.tail.tail.head.v
def _5[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, T]]]]]) = t.tail.tail.tail.tail.head.v
def _6[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, T]]]]]]) = t.tail.tail.tail.tail.tail.head.v
def _7[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, T]]]]]]]) = t.tail.tail.tail.tail.tail.tail.head.v
def _8[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], H8 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, HCons[H8, T]]]]]]]]) = t.tail.tail.tail.tail.tail.tail.tail.head.v
final class DataWrapper[T <: HList](var row : T)
{
def assign(res : SQLiteStatement) { row.assign(res, 0) }
}
final class SQLiteWrapper(dbFile : File)
{
val conn = new SQLiteConnection(dbFile)
conn.open()
def exec(statement : String)
{
conn.exec(statement)
}
def prepare[T <: HList](query : String, row : T) =
{
new PreparedStatement(query, row)
}
// TODO: Parameterise with tuple type
// make applicable to for comprehensions (implement filter, map, flatMap)
final class PreparedStatement[T <: HList](query : String, var row : T)
{
val statement = conn.prepare(query)
private def bindRec(index : Int, params : List[Any])
{
println("Value " + params.head)
// TODO: Does this need a pattern match?
params.head match
{
case v : Int => statement.bind(index, v)
case v : String => statement.bind(index, v)
case v : Double => statement.bind(index, v)
case _ => throw new ClassCastException("Unsupported type in bind.")
}
if (params.tail != Nil)
{
bindRec(index+1, params.tail)
}
}
def bind(args : Any*)
{
bindRec(1, args.toList)
}
def exec(args : Any*)
{
bindRec(1, args.toList)
step()
reset()
}
def reset()
{
statement.reset()
}
def step() : Boolean =
{
val success = statement.step()
row.assign(statement, 0)
return success
}
}
}
}
내 대답보다 더 정교하게, 엄지 손가락으로! –
이것은 깔끔한 대답입니다. 아마도 내가 간절히 원했던 것처럼 간결하지는 않지만 아마도 전체 효과를 얻으려면 HList가 필요합니다. 둘 다 고려할 것입니다. 감사! –