나는 Slick 작품과 그에 필요한 것을 이해하려고 노력합니다.
여기에 예가 있습니다.
package models
case class Bar(id: Option[Int] = None, name: String)
object Bars extends Table[Bar]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
// This is the primary key column
def name = column[String]("name")
// Every table needs a * projection with the same type as the table's type parameter
def * = id.? ~ name <>(Bar, Bar.unapply _)
}
누군가의 목적은 무엇 나를 설명 할 수 *
무엇인지, 여기 방법 <>
, 왜 unapply
? 그리고 Projection-method ~
‘는 Projection2
무엇입니까?
답변
[업데이트] – 이해에 대한 설명 추가for
-
*
방법 :이렇게하면 기본 투영 이 반환됩니다 .
‘ 일반적으로 관심있는 모든 열 (또는 계산 된 값) ‘.
테이블에는 여러 필드가있을 수 있습니다. 기본 프로젝션에 대한 하위 집합 만 필요합니다. 기본 프로젝션은 테이블의 유형 매개 변수와 일치해야합니다.
한 번에 하나씩 가져 가자.
<>
물건이 없으면*
:// First take: Only the Table Defintion, no case class: object Bars extends Table[(Int, String)]("bar") { def id = column[Int]("id", O.PrimaryKey, O.AutoInc) def name = column[String]("name") def * = id ~ name // Note: Just a simple projection, not using .? etc } // Note that the case class 'Bar' is not to be found. This is // an example without it (with only the table definition)
이와 같은 테이블 정의 만 있으면 다음과 같은 쿼리를 만들 수 있습니다.
implicit val session: Session = // ... a db session obtained from somewhere // A simple select-all: val result = Query(Bars).list // result is a List[(Int, String)]
(Int, String)
리드 의 기본 프로젝션은List[(Int, String)]
이와 같은 간단한 쿼리 를위한 것 입니다.// SELECT b.name, 1 FROM bars b WHERE b.id = 42; val q = for (b <- Bars if b.id === 42) yield (b.name ~ 1) // yield (b.name, 1) // this is also allowed: // tuples are lifted to the equivalent projection.
유형은
q
무엇입니까? 그것은Query
투영과 함께(String, Int)
입니다. 호출 할 때, 그것은 반환List
의(String, Int)
투사에 따라 튜플.val result: List[(String, Int)] = q.list
이 경우 이해 의
yield
절 에서 원하는 투영을 정의했습니다for
. -
이제
<>
및Bar.unapply
.이것은 Mapped Projections 라는 것을 제공합니다 .
지금까지 우리는 열 (또는 계산 된 값) 의 프로젝션 을 반환하는 쿼리를 Scala에서 얼마나 매끄럽게 표현할 수 있는지 살펴 보았습니다 . 따라서 이러한 쿼리 를 실행할 때 쿼리 의 결과 행 을 Scala 튜플 이라고 생각해야합니다 . 튜플의 유형은 정의 된 프로젝션과 일치합니다 (
for
이전 예제에서와 같이 기본*
프로젝션 에 의한 이해에 의해 ). 이것이 where
is the type of and is the type offield1 ~ field2
의 프로젝션을 반환하는 이유 입니다 .Projection2[A, B]
A
field1
B
field2
q.list.map { case (name, n) => // do something with name:String and n:Int } Queury(Bars).list.map { case (id, name) => // do something with id:Int and name:String }
튜플을 다루고 있는데, 열이 너무 많으면 번거로울 수 있습니다. 우리는 결과
TupleN
를 이름이 지정된 필드가있는 개체가 아니라 일부 개체 로 생각하고 싶습니다 .(id ~ name) // A projection // Assuming you have a Bar case class: case class Bar(id: Int, name: String) // For now, using a plain Int instead // of Option[Int] - for simplicity (id ~ name <> (Bar, Bar.unapply _)) // A MAPPED projection // Which lets you do: Query(Bars).list.map ( b.name ) // instead of // Query(Bars).list.map { case (_, name) => name } // Note that I use list.map instead of mapResult just for explanation's sake.
어떻게 작동합니까?
<>
투영을 취하고Projection2[Int, String]
유형에 매핑 된 투영을 반환합니다Bar
. 두 인수Bar, Bar.unapply _
는이(Int, String)
프로젝션 이 케이스 클래스에 매핑되어야 하는 방법을 매끄럽게 알려줍니다 .이것은 양방향 매핑입니다.
Bar
케이스 클래스 생성자이므로(id: Int, name: String)
에서Bar
. 그리고unapply
당신이 그것을 짐작했다면, 그 반대입니다.어디
unapply
에서 왔습니까? 이것은 어떤 보통의 경우 클래스에 사용할 수있는 표준 스칼라 방법입니다 – 단지 정의가Bar
있는 A에게 제공Bar.unapply
인 추출기 돌아 가야하는 데 사용할 수있는id
및name
이 있음
Bar
으로 지어진 :val bar1 = Bar(1, "one") // later val Bar(id, name) = bar1 // id will be an Int bound to 1, // name a String bound to "one" // Or in pattern matching val bars: List[Bar] = // gotten from somewhere val barNames = bars.map { case Bar(_, name) => name } val x = Bar.unapply(bar1) // x is an Option[(String, Int)]
따라서 기본 프로젝션을 가장 많이 사용할 것으로 예상되는 케이스 클래스에 매핑 할 수 있습니다.
object Bars extends Table[Bar]("bar") { def id = column[Int]("id", O.PrimaryKey, O.AutoInc) def name = column[String]("name") def * = id ~ name <>(Bar, Bar.unapply _) }
또는 쿼리별로 가질 수도 있습니다.
case class Baz(name: String, num: Int) // SELECT b.name, 1 FROM bars b WHERE b.id = 42; val q1 = for (b <- Bars if b.id === 42) yield (b.name ~ 1 <> (Baz, Baz.unapply _))
다음의 타입
q1
A는Query
A의 매핑 에 투영Baz
. 호출 할 때, 그것은 반환List
의Baz
목적 :val result: List[Baz] = q1.list
-
마지막으로, 옆으로는 같은
.?
이벤트의 옵션 리프팅 -하지 않을 수 있습니다 값을 처리하는 스칼라 방법.(id ~ name) // Projection2[Int, String] // this is just for illustration (id.? ~ name) // Projection2[Option[Int], String]
마무리하면 다음과 같은 원래 정의와 잘 작동합니다
Bar
.case class Bar(id: Option[Int] = None, name: String) // SELECT b.id, b.name FROM bars b WHERE b.id = 42; val q0 = for (b <- Bars if b.id === 42) yield (b.id.? ~ b.name <> (Bar, Bar.unapply _)) q0.list // returns a List[Bar]
-
Slick이
for
이해력을 사용하는 방법에 대한 의견에 대한 답변 :어쨌든 모나드는 항상 나타나서 설명의 일부가 될 것을 요구합니다 …
이해력은 컬렉션에만 국한되지 않습니다. 모든 종류의 Monad 에서 사용할 수 있으며 컬렉션은 Scala에서 사용할 수있는 많은 종류의 모나드 유형 중 하나 일뿐입니다.
그러나 컬렉션은 익숙하기 때문에 설명을위한 좋은 출발점이됩니다.
val ns = 1 to 100 toList; // Lists for familiarity val result = for { i <- ns if i*i % 2 == 0 } yield (i*i) // result is a List[Int], List(4, 16, 36, ...)
Scala에서 for comprehension은 메서드 (중첩 된) 메서드 호출에 대한 구문 설탕입니다. 위 코드는 다음과 같습니다.
ns.filter(i => i*i % 2 == 0).map(i => i*i)
기본적으로, 아무것도는
filter
,map
,flatMap
방법 (즉, 모나드 )를 사용할 수 있습니다
for
대신에 이해ns
. 좋은 예는 Option monad 입니다. 다음은 동일한for
명령문이 모나드List
와 모두에서 작동
하는 이전 예제입니다Option
.// (1) val result = for { i <- ns // ns is a List monad i2 <- Some(i*i) // Some(i*i) is Option if i2 % 2 == 0 // filter } yield i2 // Slightly more contrived example: def evenSqr(n: Int) = { // return the square of a number val sqr = n*n // only when the square is even if (sqr % 2 == 0) Some (sqr) else None } // (2) result = for { i <- ns i2 <- evenSqr(i) // i2 may/maynot be defined for i! } yield i2
마지막 예에서 변환은 다음과 같습니다.
// 1st example val result = ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0) // Or for the 2nd example result = ns.flatMap(i => evenSqr(i))
Slick에서 쿼리는 모나 딕 입니다.
map
,flatMap
및filter
메서드 가있는 객체 일뿐 입니다. 따라서for
이해력 (*
방법 설명에 표시됨)은 다음과 같이 해석됩니다.val q = Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1) // Type of q is Query[(String, Int)] val r: List[(String, Int)] = q.list // Actually run the query
볼 수 있듯이
flatMap
,map
및filter
를 생성하는 데 사용Query
의 반복 변형에 의해Query(Bars)
각각의 호출로filter
하고map
. 컬렉션의 경우 이러한 메서드는 실제로 컬렉션을 반복하고 필터링하지만 Slick에서는 SQL을 생성하는 데 사용됩니다. 자세한 내용은 여기를 참조하십시오.
Scala Slick은 어떻게 Scala 코드를 JDBC로 변환합니까?
답변
아무도 대답하지 않았으므로 시작하는 데 도움이 될 수 있습니다. 나는 Slick을 잘 모릅니다.
로부터 슬릭 문서 :
해제 된 삽입 :
모든 테이블에는 기본 프로젝션을 포함하는 * 메서드가 필요합니다. 이것은 쿼리에서 행 (테이블 개체 형식)을 반환 할 때 반환되는 내용을 설명합니다. Slick의 * 투영은 데이터베이스의 투영과 일치하지 않아도됩니다. 새 열 (예 : 계산 된 값 포함)을 추가하거나 원하는대로 일부 열을 생략 할 수 있습니다. * 프로젝션에 해당하는 리프팅되지 않은 유형은 테이블에 유형 매개 변수로 제공됩니다. 단순하고 매핑되지 않은 테이블의 경우 단일 열 유형 또는 열 유형의 튜플이됩니다.
즉, slick은 데이터베이스에서 반환 된 행을 처리하는 방법을 알아야합니다. 정의한 방법은 파서 결합 자 함수를 사용하여 열 정의를 행에서 사용할 수있는 것으로 결합합니다.