Расширение библиотеки Slick
Slick – библиотека для работы с БД на Scala
Арсений Жижелев
1. ВВЕДЕНИЕ SLICK
Небольшое введение в работу с БД с помощью
библиотеки Slick
2
Основные возможности
 Схема БД на Scala
◦ Стандартные и пользовательские типы
◦ Первичные и внешние ключи
◦ Индексы
◦ Ограничения
 Текстовый SQL (sql"SELECT * FROM users")
 DSL, напоминающий коллекции
◦ Select, where
◦ Join (включая авто-join по foreign key)
◦ Update, Delete (отобранных записей)
◦ Insert (включая server-side, bulk insert)
 Поддержка всех основных СУБД
 Расширяемая архитектура
3
Схема
4
class Suppliers(tag: Tag) extends
Table[(Int, String, String)](tag, "SUPPLIERS") {
def id = column[Int]("SUP_ID",
O.PrimaryKey, O.AutoInc)
def name = column[String]("NAME")
def city = column[String]("CITY")
def * = (id, name, city)
}
val suppliers = TableQuery[Suppliers]
http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf
Подключение к БД
5
import scala.slick.driver.H2Driver.simple._
val db = Database.forURL("jdbc:h2:mem:test1",
driver = "org.h2.Driver")
db.withSession { implicit session =>
// suppliers.ddl.create // – создание схемы
// Use the session:
val result = myQuery.run
}
Структура запроса
6
Простые текстовые запросы
7
def personsMatching(pattern: String)(conn: Connection) = {
val st = conn.prepareStatement(
"SELECT id, name FROM person WHERE name LIKE ?")
try {
st.setString(1, pattern)
val rs = st.executeQuery()
try {
val b = new ListBuffer[(Int, String)]
while(rs.next)
b.append((rs.getInt(1), rs.getString(2)))
b.toList
} finally rs.close()
} finally st.close()
}
JDBC
Простые текстовые запросы
8
def personsMatching(pattern: String)(implicit s: Session) =
sql"SELECT id, name FROM person WHERE name LIKE $pattern“
.as[(Int, String)].list
Sql-interpolation
Поддерживаемые СУБД
Slick
 PostgreSQL
 MySQL
 H2
 Hsqldb
 Derby / JavaDB
 SQLite
 Access
Slick Extension$
 Oracle
 DB2
 SQL Server
9
Ссылки
 Stefan Zeiger 2014-09-24 Introduction
to Slick 2.1 and 2.2 / ScalaCamp #7,
Kraków, Poland
(http://slick.typesafe.com/talks/2014-
09-
24_ScalaCamp/Introduction_to_Slick_
2.1_and_2.2.pdf)
 И др. на странице Slick@Typesafe
http://slick.typesafe.com/docs/
10
2. АРХИТЕКТУРА SLICK
Послойное рассмотрение архитектуры Slick
11
Компоненты
Пользовательский уровень
 Запросы
◦ Lifted (DSL для формирования запросов)
◦ Direct (на макросах)
◦ SQL-interpolation - текстовые запросы
 Схема БД: Table и TableQuery
Системный уровень
 AST – синтаксическое дерево будущего запроса
 QueryCompiler – компилятор запросов
Runtime
 Driver
 Session management – подключение к БД
12
2.0. КОНЦЕПЦИЯ
Comprehension
13
SQL ~ functional
Relational Model
 Relation
 Attribute
 Tuple
 Relation Value
 Relation Variable
14
http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf
SQL ~ functional
Relational Model
 Relation
 Attribute
 Tuple
 Relation Value
 Relation Variable
15
http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf
case class Coffee(
name: String,
supplierId: Int,
price: Double
)
val coffees = Set(
Coffee("Colombian", 101, 7.99),
Coffee("French_Roast", 49, 8.99),
Coffee("Espresso", 150, 9.99)
)
Set-comprehension
 Конструктор множества
◦ Исходное множество – генератор
◦ Фильтр
◦ Отображение
16
 712,|2
 xxx
for { x <- 1 to 10
if 2*x+1<7
} yield math.pow(x,2)
SELECT x^2
FROM sequence(1,10)
WHERE 2*x+1<7
2.1. AST
Структура запросов
AST – abstract syntax tree (forest)
17
AST: Node
 Node
◦ SimpleExpression – простой SQL
◦ SimpleFunction
◦ TableNode, TableExpansion – таблица
◦ …
 mix-in’s
◦ DefNode
◦ TypedNode (обычно Rep[T])
◦ SimplyTypedNode (имеет непосредственный тип)
 N-кратные узлы
◦ NullaryNode
◦ UnaryNode
◦ BinaryNode
 Comprehension – полное выражение select, включающее источник,
фильтры, порядок, группировку, список колонок, смещение и размер
страницы
 Insert – выражение insert (update и delete выражаются через
comprehension)
18
AST: Symbol
 Symbol – представляет имена в
запросе
◦ Library – объект, содержащий экземпляры
Symbol для стандартных операторов и
функций
◦ AnonSymbol – уникальное имя
 DefNode – узел, вводящий новое имя
19
AST: Type
 AtomicType (простой тип)
 ProductType (кортеж)
 StructType («запись» с полями)
 CollectionType (тип коллекции, на
основе CanBuild)
 MappedScalaType (содержит
двусторонний конвертер)
 implicit TypedType[T] – почти везде
20
2.2. LIFTED (DSL)
Язык построения запросов на основе Rep[T], for-
comprehension и множества implicit’ов, которые
конструируют AST
21
Rep[T]
 Column[T] – методы расширения для
колонок разных типов
 Query[T] – методы, оперирующие
запросами
◦ for-comprehension (map, flatMap, filter)
◦ join’ы, zip’ы,
◦ sort, group by, order by, union,
◦ take (aka limit), drop (aka offset)
 MappedProjection – client-side
конвертация в пользовательские классы
22
Shape
 Конструируемый запрос
Query[T, RepT, _]
 T и RepT – сильно связаны
implicit Shape[Level, _, T, RepT]
 primitiveShape
 RepShape
◦ optionShape
 ProductNodeShape
◦ TupleShape (tuple1..tuple22 shapes)
◦ MappedProductShape
◦ MappedScalaProductShape
 HListShape
 CaseClassShape
23
Nullable колонки (1):
OptionMapperDSL
 B1 – базовый тип T, P1 – базовый T
или Option[T]
24
object OptionMapperDSL {
type arg[B1, P1] = {
type to[BR, PR] = OptionMapper2[B1, B1, BR, P1, P1, PR]
type toSame = OptionMapper2[B1, B1, B1, P1, P1, P1]
type arg[B2, P2] = {
type to[BR, PR] = OptionMapper2[B1, B2, BR, P1, P2, PR]
type arg[B3, P3] = {
type to[BR, PR] = OptionMapper3[B1, B2, B3, BR, P1, P2, P3, PR]
}
}
}
}
val om = OptionMapperDSL
om#arg[B1,P1]#arg[B2,P2]#to[BR,PR]
Nullable колонки (2)
 Тип результата – Option, если хотя бы
один аргумент – Option. Иначе –
базовый тип
25
trait ColumnExtensionMethods[B1, P1] extends Any
with ExtensionMethods[B1, P1] {
protected[this] def c: Column[P1]
def === [P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]) =
om.column(Library.==, n, e.toNode)
}
Abstract table, Tag
AbstractTable
◦ Объявления колонок, индексов, foreignKey,
primaryKey
◦ def *
 Источник колонок для DDL
 ProvenShape (увязывает колонки в record type,
Shape и конвертер <>)
 Tag – связь с AST (цепочка переходов,
приводящая к текущей таблице)
◦ BaseTag – сама таблица
◦ RefTag – узел AST, имеющий тип таблицы
26
2.3. ДРАЙВЕР
Все возможности Slick представлены через API,
реализуемое драйвером БД
27
Cake pattern для драйвера
 Component
◦ BasicInvokerComponent,
BasicInsertInvokerComponent,
BasicExecutorComponent,
BasicActionComponent
◦ RelationalTableComponent,
RelationalSequenceComponent,
RelationalTypesComponent,
RelationalActionComponent
 Profile (наследуется от профиля + несколько
компонентов)
 Driver (реализует профиль, возможно, путём
подмешивания компонентов с реализацией)
28
Драйвер Postgres
29
trait BasicProfile extends BasicInvokerComponent
with BasicInsertInvokerComponent with BasicExecutorComponent
with BasicActionComponent
trait RelationalProfile extends BasicProfile with RelationalTableComponent
with RelationalSequenceComponent with RelationalTypesComponent
with RelationalActionComponent
trait SqlProfile extends RelationalProfile with SqlExecutorComponent
with SqlTableComponent with SqlActionComponent
trait BasicDriver extends BasicProfile
trait RelationalDriver extends BasicDriver with RelationalProfile
trait SqlDriver extends RelationalDriver with SqlProfile with SqlUtilsComponent
trait JdbcDriver extends SqlDriver with JdbcProfile
with JdbcStatementBuilderComponent with JdbcMappingCompilerComponent
trait PostgresDriver extends JdbcDriver
Драйвер: SimpleQL
 Каждый компонент может перекрыть
SimpleQL и добавить элементы в user-
scope (import …Driver.simple._)
30
trait SomeComponent extends BasicProfile {
override val simple: SimpleQL = new SimpleQL {}
trait SimpleQL extends super.SimpleQL {
type MyType = MyClass
val someVal
def doSomething
}
class MyClass
}
Драйвер: capability
 Возможности драйвера реализуются и
декларируются компонентами
31
object RelationalProfile {
object capabilities {
/** Supports default values in column definitions */
val columnDefaults = Capability("relational.columnDefaults")
…
val all = Set(columnDefaults)
}}
trait ColumnDefaultsComponent {
override protected def computeCapabilities =
super. computeCapabilities ++ capabilities.all
}
2.4. КОМПИЛЯЦИЯ
Компилятор запросов
32
Преобразование запроса
33
Lifted
Embedding
Direct
Embedding
Slick AST
Scala AST
Scala
Compiler
Slick
Macros
Slick AST
Query
Compiler
Result
Executor
DB
Фазы компиляции
(state => state)/rewrite
CleanUp
 inline
 assignUniqueSymbols
 expandTables
 inferTypes
 createResultSetMappings
 forceOuterBinds
FlattenColumns
 expandRefs
 replaceFieldSymbols
 rewritePaths
 relabelUnions
 pruneFields
 assignTypes
SQL Shape (not in memory
driver)
 resolveZipJoins
 convertToComprehensions
 fuseComprehensions
 fixRowNumberOrdering
 hoistClientOps
Generate Code
 codeGen (driver specific)
34
3. РАСШИРЕНИЕ SLICK
Встроенная возможность расширения
функциональности Slick
35
Механизмы расширения
 Базовые возможности
◦ Пользовательские функции в БД
◦ Простые типы-обёртки (MyId)
◦ Пользовательские типы результатов
(MappedProjection <>)
◦ Композитные типы (PgComposite)
 Расширения в драйвере
◦ ColumnOption – метаинформация к колонкам
◦ TableDDLBuilder – создание таблиц
 Расширения в компиляторе
◦ Новые типы узлов, фазы rewrite, генерация
SQL
36
Пользовательские функции
37
trait AgeFunctionTrait extends DatabaseSchema {
val getAgeFName = "get_age"
override val ddl = super.ddl ++ DDL(List(s"""
|CREATE FUNCTION $getAgeFName(date_of_birth DATE)
| RETURN NUMBER
|AS BEGIN
| RETURN
| TRUNC(MONTHS_BETWEEN(SYSDATE, date_of_birth)/12);
|END;
""".stripMargin
), List(),
List(s"DROP FUNCTION $getAgeFName"), List())
val getAge = SimpleFunction.unary[Date, Int](getAgeFName)
}
Пользовательские функции (2)
38
val ageHistogram = for {
(age, q) <- persons
.map(p => (getAge(p.dateOfBirth),
p.id))
.groupBy(_._1)
} yield (age, q.size)
Простые типы-обёртки
39
case class MyID(value: Long) extends MappedTo[Long]
class MyTable(tag: Tag)
extends Table[(MyID, String)](tag, "MY_TABLE")
{
def id = column[MyID]("ID")
def data = column[String]("DATA")
def * = (id, data)
}
Пользовательские типы (<>)
40
trait PersonSchema extends DatabaseSchema {
val personTableName = "person"
case class Person(id:Int,
name:String,
dateOfBirth:Option[Timestamp])
class PersonTable(tag:Tag)
extends Table[Person](tag, personTableName){
def id = column[Int] ("id", O.AutoInc)
def name = column[String]("name")
def dateOfBirth = column[Option[Timestamp]]
("date_of_birth")
def * = (id, name, dateOfBirth) <>
(Person.tupled, Person.unapply)
}
val person = TableQuery[PersonTable]
}
Композитные типы (SlickPg)
41
trait MyPointTrait extends DatabaseSchema {
type Driver <: PgCompositeSupport
val myPointTName = "my_point"
override val ddl = super.ddl ++ DDL(List(
s"CREATE TYPE $myPointTName(x int, y int)"
), List(),
List(s"DROP TYPE $myPointTName"), List())
case class MyPoint(x: Int, y:Int)
implicit val myPointTypeMapper =
createCompositeJdbcType[MyPoint](myPointTName)
}
Slick-pg
Типы
 Array
 Date/Time (включая Joda, ThreeTen)
 Range
 Enum
 Json (включая Play Json)
 HStore,
 LTree
 Text (full text search – tsquery, tsvector)
 PostGis geometry
 Inet/MacAddr
Возможности Postgres
 CompositeType
 Наследование
42
Расширение драйвера (1)
43
import slick.driver.PostgresDriver
import com.github.tminglei.slickpg._
trait MyPostgresDriver extends PostgresDriver
with PgCompositeSupport {
override lazy val Implicit = new Implicits {}
override val simple = new SimpleQL {}
trait Implicits extends super.Implicits
with MyImplicits
trait SimpleQL extends super.SimpleQL
with Implicits
with MyQL
trait MyQL {
def myFavouriteMethod
}
}
object MyPostgresDriver extends MyPostgresDriver
Расширение драйвера (2)
44
trait MyColumnOptComponent { driver : JdbcPostgresDriver =>
trait ColumnOptions extends super.ColumnOptions {
def MyMetaData (data: String) =
MyColumnOptions.MyMetaData(data)
}
override val columnOptions: ColumnOptions =
new ColumnOptions {}
}
object MyColumnOptions {
case class MyMetaData(data:String)
extends ColumnOption[Nothing] //Для колонок любого типа
}
// abstract class ColumnOption[+T]
Расширение драйвера (3)
45
trait MyTableComponent { driver : JdbcPostgresDriver =>
override def createTableDDLBuilder(table: Table[_]) =
new TableDDLBuilder(table)
override def createColumnDDLBuilder(column: FieldSymbol,
table: Table[_]) = new ColumnDDLBuilder(column)
class TableDDLBuilder(table: Table[_])
extends super.TableDDLBuilder(table) {
override def createPhase1 =
super.createPhase1.mkString("").
replaceAll("CREATE TABLE", "CREATE TEMPORARY TABLE")
}
}
object MyPostgresDriver extends MyPostgresDriver
with MyTableComponent
Генерация SQL (ILIKE)
46
trait ILikeComponent { driver : JdbcPostgresDriver =>
override def createQueryBuilder(n: Node,
state: CompilerState) = new QueryBuilder(n, state)
class QueryBuilder(tree: Node, state: CompilerState)
extends super. QueryBuilder(tree, state) {
override def expr(n: Node,
skipParens: Boolean = false) = n match {
case Library.ILike(l, r) => b"($l ilike $r)“
case _ => super.expr(n, skipParens)
}
}
}
final class ILikeStringColumnExtensionMethods[P1](val c: Column[P1])
extends AnyVal with ExtensionMethods[String, P1] {
def ilike[P2, R](e: Column[P2], esc: Char = 'u0000')(implicit om:
o#arg[String, P2]#to[Boolean, R]) =
if(esc == 'u0000')
om.column(Library.ILike, n, e.toNode)
else
om.column(Library.ILike, n, e.toNode, LiteralNode(esc))
}
object Library {
val ILike = new FunctionSymbol(“ILike")
}
Ключевые особенности Slick
 Functional Relational Mapping
 Целостная архитектура
 Модульность и расширяемость
 Новый строго типизированный язык
запросов (по-видимому,
эквивалентный по выразительности
SQL)
47
http://slick.typesafe.com/
https://github.com/slick/slick/
Welcome
Slick
http://slick.typesafe.com/
https://github.com/slick/slick/
SynapseGrid
https://github.com/Primetalk/SynapseGrid/
'ru.primetalk:synapse-grid-core_2.10:1.3.5'
Open source - BSD
Жижелев Арсений
zhizhelev@primetalk.ru
48https://github.com/Primetalk/SynapseGrid/

Расширение библиотеки Slick

  • 1.
    Расширение библиотеки Slick Slick– библиотека для работы с БД на Scala Арсений Жижелев
  • 2.
    1. ВВЕДЕНИЕ SLICK Небольшоевведение в работу с БД с помощью библиотеки Slick 2
  • 3.
    Основные возможности  СхемаБД на Scala ◦ Стандартные и пользовательские типы ◦ Первичные и внешние ключи ◦ Индексы ◦ Ограничения  Текстовый SQL (sql"SELECT * FROM users")  DSL, напоминающий коллекции ◦ Select, where ◦ Join (включая авто-join по foreign key) ◦ Update, Delete (отобранных записей) ◦ Insert (включая server-side, bulk insert)  Поддержка всех основных СУБД  Расширяемая архитектура 3
  • 4.
    Схема 4 class Suppliers(tag: Tag)extends Table[(Int, String, String)](tag, "SUPPLIERS") { def id = column[Int]("SUP_ID", O.PrimaryKey, O.AutoInc) def name = column[String]("NAME") def city = column[String]("CITY") def * = (id, name, city) } val suppliers = TableQuery[Suppliers] http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf
  • 5.
    Подключение к БД 5 importscala.slick.driver.H2Driver.simple._ val db = Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") db.withSession { implicit session => // suppliers.ddl.create // – создание схемы // Use the session: val result = myQuery.run }
  • 6.
  • 7.
    Простые текстовые запросы 7 defpersonsMatching(pattern: String)(conn: Connection) = { val st = conn.prepareStatement( "SELECT id, name FROM person WHERE name LIKE ?") try { st.setString(1, pattern) val rs = st.executeQuery() try { val b = new ListBuffer[(Int, String)] while(rs.next) b.append((rs.getInt(1), rs.getString(2))) b.toList } finally rs.close() } finally st.close() } JDBC
  • 8.
    Простые текстовые запросы 8 defpersonsMatching(pattern: String)(implicit s: Session) = sql"SELECT id, name FROM person WHERE name LIKE $pattern“ .as[(Int, String)].list Sql-interpolation
  • 9.
    Поддерживаемые СУБД Slick  PostgreSQL MySQL  H2  Hsqldb  Derby / JavaDB  SQLite  Access Slick Extension$  Oracle  DB2  SQL Server 9
  • 10.
    Ссылки  Stefan Zeiger2014-09-24 Introduction to Slick 2.1 and 2.2 / ScalaCamp #7, Kraków, Poland (http://slick.typesafe.com/talks/2014- 09- 24_ScalaCamp/Introduction_to_Slick_ 2.1_and_2.2.pdf)  И др. на странице Slick@Typesafe http://slick.typesafe.com/docs/ 10
  • 11.
    2. АРХИТЕКТУРА SLICK Послойноерассмотрение архитектуры Slick 11
  • 12.
    Компоненты Пользовательский уровень  Запросы ◦Lifted (DSL для формирования запросов) ◦ Direct (на макросах) ◦ SQL-interpolation - текстовые запросы  Схема БД: Table и TableQuery Системный уровень  AST – синтаксическое дерево будущего запроса  QueryCompiler – компилятор запросов Runtime  Driver  Session management – подключение к БД 12
  • 13.
  • 14.
    SQL ~ functional RelationalModel  Relation  Attribute  Tuple  Relation Value  Relation Variable 14 http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf
  • 15.
    SQL ~ functional RelationalModel  Relation  Attribute  Tuple  Relation Value  Relation Variable 15 http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf case class Coffee( name: String, supplierId: Int, price: Double ) val coffees = Set( Coffee("Colombian", 101, 7.99), Coffee("French_Roast", 49, 8.99), Coffee("Espresso", 150, 9.99) )
  • 16.
    Set-comprehension  Конструктор множества ◦Исходное множество – генератор ◦ Фильтр ◦ Отображение 16  712,|2  xxx for { x <- 1 to 10 if 2*x+1<7 } yield math.pow(x,2) SELECT x^2 FROM sequence(1,10) WHERE 2*x+1<7
  • 17.
    2.1. AST Структура запросов AST– abstract syntax tree (forest) 17
  • 18.
    AST: Node  Node ◦SimpleExpression – простой SQL ◦ SimpleFunction ◦ TableNode, TableExpansion – таблица ◦ …  mix-in’s ◦ DefNode ◦ TypedNode (обычно Rep[T]) ◦ SimplyTypedNode (имеет непосредственный тип)  N-кратные узлы ◦ NullaryNode ◦ UnaryNode ◦ BinaryNode  Comprehension – полное выражение select, включающее источник, фильтры, порядок, группировку, список колонок, смещение и размер страницы  Insert – выражение insert (update и delete выражаются через comprehension) 18
  • 19.
    AST: Symbol  Symbol– представляет имена в запросе ◦ Library – объект, содержащий экземпляры Symbol для стандартных операторов и функций ◦ AnonSymbol – уникальное имя  DefNode – узел, вводящий новое имя 19
  • 20.
    AST: Type  AtomicType(простой тип)  ProductType (кортеж)  StructType («запись» с полями)  CollectionType (тип коллекции, на основе CanBuild)  MappedScalaType (содержит двусторонний конвертер)  implicit TypedType[T] – почти везде 20
  • 21.
    2.2. LIFTED (DSL) Языкпостроения запросов на основе Rep[T], for- comprehension и множества implicit’ов, которые конструируют AST 21
  • 22.
    Rep[T]  Column[T] –методы расширения для колонок разных типов  Query[T] – методы, оперирующие запросами ◦ for-comprehension (map, flatMap, filter) ◦ join’ы, zip’ы, ◦ sort, group by, order by, union, ◦ take (aka limit), drop (aka offset)  MappedProjection – client-side конвертация в пользовательские классы 22
  • 23.
    Shape  Конструируемый запрос Query[T,RepT, _]  T и RepT – сильно связаны implicit Shape[Level, _, T, RepT]  primitiveShape  RepShape ◦ optionShape  ProductNodeShape ◦ TupleShape (tuple1..tuple22 shapes) ◦ MappedProductShape ◦ MappedScalaProductShape  HListShape  CaseClassShape 23
  • 24.
    Nullable колонки (1): OptionMapperDSL B1 – базовый тип T, P1 – базовый T или Option[T] 24 object OptionMapperDSL { type arg[B1, P1] = { type to[BR, PR] = OptionMapper2[B1, B1, BR, P1, P1, PR] type toSame = OptionMapper2[B1, B1, B1, P1, P1, P1] type arg[B2, P2] = { type to[BR, PR] = OptionMapper2[B1, B2, BR, P1, P2, PR] type arg[B3, P3] = { type to[BR, PR] = OptionMapper3[B1, B2, B3, BR, P1, P2, P3, PR] } } } } val om = OptionMapperDSL om#arg[B1,P1]#arg[B2,P2]#to[BR,PR]
  • 25.
    Nullable колонки (2) Тип результата – Option, если хотя бы один аргумент – Option. Иначе – базовый тип 25 trait ColumnExtensionMethods[B1, P1] extends Any with ExtensionMethods[B1, P1] { protected[this] def c: Column[P1] def === [P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]) = om.column(Library.==, n, e.toNode) }
  • 26.
    Abstract table, Tag AbstractTable ◦Объявления колонок, индексов, foreignKey, primaryKey ◦ def *  Источник колонок для DDL  ProvenShape (увязывает колонки в record type, Shape и конвертер <>)  Tag – связь с AST (цепочка переходов, приводящая к текущей таблице) ◦ BaseTag – сама таблица ◦ RefTag – узел AST, имеющий тип таблицы 26
  • 27.
    2.3. ДРАЙВЕР Все возможностиSlick представлены через API, реализуемое драйвером БД 27
  • 28.
    Cake pattern длядрайвера  Component ◦ BasicInvokerComponent, BasicInsertInvokerComponent, BasicExecutorComponent, BasicActionComponent ◦ RelationalTableComponent, RelationalSequenceComponent, RelationalTypesComponent, RelationalActionComponent  Profile (наследуется от профиля + несколько компонентов)  Driver (реализует профиль, возможно, путём подмешивания компонентов с реализацией) 28
  • 29.
    Драйвер Postgres 29 trait BasicProfileextends BasicInvokerComponent with BasicInsertInvokerComponent with BasicExecutorComponent with BasicActionComponent trait RelationalProfile extends BasicProfile with RelationalTableComponent with RelationalSequenceComponent with RelationalTypesComponent with RelationalActionComponent trait SqlProfile extends RelationalProfile with SqlExecutorComponent with SqlTableComponent with SqlActionComponent trait BasicDriver extends BasicProfile trait RelationalDriver extends BasicDriver with RelationalProfile trait SqlDriver extends RelationalDriver with SqlProfile with SqlUtilsComponent trait JdbcDriver extends SqlDriver with JdbcProfile with JdbcStatementBuilderComponent with JdbcMappingCompilerComponent trait PostgresDriver extends JdbcDriver
  • 30.
    Драйвер: SimpleQL  Каждыйкомпонент может перекрыть SimpleQL и добавить элементы в user- scope (import …Driver.simple._) 30 trait SomeComponent extends BasicProfile { override val simple: SimpleQL = new SimpleQL {} trait SimpleQL extends super.SimpleQL { type MyType = MyClass val someVal def doSomething } class MyClass }
  • 31.
    Драйвер: capability  Возможностидрайвера реализуются и декларируются компонентами 31 object RelationalProfile { object capabilities { /** Supports default values in column definitions */ val columnDefaults = Capability("relational.columnDefaults") … val all = Set(columnDefaults) }} trait ColumnDefaultsComponent { override protected def computeCapabilities = super. computeCapabilities ++ capabilities.all }
  • 32.
  • 33.
    Преобразование запроса 33 Lifted Embedding Direct Embedding Slick AST ScalaAST Scala Compiler Slick Macros Slick AST Query Compiler Result Executor DB
  • 34.
    Фазы компиляции (state =>state)/rewrite CleanUp  inline  assignUniqueSymbols  expandTables  inferTypes  createResultSetMappings  forceOuterBinds FlattenColumns  expandRefs  replaceFieldSymbols  rewritePaths  relabelUnions  pruneFields  assignTypes SQL Shape (not in memory driver)  resolveZipJoins  convertToComprehensions  fuseComprehensions  fixRowNumberOrdering  hoistClientOps Generate Code  codeGen (driver specific) 34
  • 35.
    3. РАСШИРЕНИЕ SLICK Встроеннаявозможность расширения функциональности Slick 35
  • 36.
    Механизмы расширения  Базовыевозможности ◦ Пользовательские функции в БД ◦ Простые типы-обёртки (MyId) ◦ Пользовательские типы результатов (MappedProjection <>) ◦ Композитные типы (PgComposite)  Расширения в драйвере ◦ ColumnOption – метаинформация к колонкам ◦ TableDDLBuilder – создание таблиц  Расширения в компиляторе ◦ Новые типы узлов, фазы rewrite, генерация SQL 36
  • 37.
    Пользовательские функции 37 trait AgeFunctionTraitextends DatabaseSchema { val getAgeFName = "get_age" override val ddl = super.ddl ++ DDL(List(s""" |CREATE FUNCTION $getAgeFName(date_of_birth DATE) | RETURN NUMBER |AS BEGIN | RETURN | TRUNC(MONTHS_BETWEEN(SYSDATE, date_of_birth)/12); |END; """.stripMargin ), List(), List(s"DROP FUNCTION $getAgeFName"), List()) val getAge = SimpleFunction.unary[Date, Int](getAgeFName) }
  • 38.
    Пользовательские функции (2) 38 valageHistogram = for { (age, q) <- persons .map(p => (getAge(p.dateOfBirth), p.id)) .groupBy(_._1) } yield (age, q.size)
  • 39.
    Простые типы-обёртки 39 case classMyID(value: Long) extends MappedTo[Long] class MyTable(tag: Tag) extends Table[(MyID, String)](tag, "MY_TABLE") { def id = column[MyID]("ID") def data = column[String]("DATA") def * = (id, data) }
  • 40.
    Пользовательские типы (<>) 40 traitPersonSchema extends DatabaseSchema { val personTableName = "person" case class Person(id:Int, name:String, dateOfBirth:Option[Timestamp]) class PersonTable(tag:Tag) extends Table[Person](tag, personTableName){ def id = column[Int] ("id", O.AutoInc) def name = column[String]("name") def dateOfBirth = column[Option[Timestamp]] ("date_of_birth") def * = (id, name, dateOfBirth) <> (Person.tupled, Person.unapply) } val person = TableQuery[PersonTable] }
  • 41.
    Композитные типы (SlickPg) 41 traitMyPointTrait extends DatabaseSchema { type Driver <: PgCompositeSupport val myPointTName = "my_point" override val ddl = super.ddl ++ DDL(List( s"CREATE TYPE $myPointTName(x int, y int)" ), List(), List(s"DROP TYPE $myPointTName"), List()) case class MyPoint(x: Int, y:Int) implicit val myPointTypeMapper = createCompositeJdbcType[MyPoint](myPointTName) }
  • 42.
    Slick-pg Типы  Array  Date/Time(включая Joda, ThreeTen)  Range  Enum  Json (включая Play Json)  HStore,  LTree  Text (full text search – tsquery, tsvector)  PostGis geometry  Inet/MacAddr Возможности Postgres  CompositeType  Наследование 42
  • 43.
    Расширение драйвера (1) 43 importslick.driver.PostgresDriver import com.github.tminglei.slickpg._ trait MyPostgresDriver extends PostgresDriver with PgCompositeSupport { override lazy val Implicit = new Implicits {} override val simple = new SimpleQL {} trait Implicits extends super.Implicits with MyImplicits trait SimpleQL extends super.SimpleQL with Implicits with MyQL trait MyQL { def myFavouriteMethod } } object MyPostgresDriver extends MyPostgresDriver
  • 44.
    Расширение драйвера (2) 44 traitMyColumnOptComponent { driver : JdbcPostgresDriver => trait ColumnOptions extends super.ColumnOptions { def MyMetaData (data: String) = MyColumnOptions.MyMetaData(data) } override val columnOptions: ColumnOptions = new ColumnOptions {} } object MyColumnOptions { case class MyMetaData(data:String) extends ColumnOption[Nothing] //Для колонок любого типа } // abstract class ColumnOption[+T]
  • 45.
    Расширение драйвера (3) 45 traitMyTableComponent { driver : JdbcPostgresDriver => override def createTableDDLBuilder(table: Table[_]) = new TableDDLBuilder(table) override def createColumnDDLBuilder(column: FieldSymbol, table: Table[_]) = new ColumnDDLBuilder(column) class TableDDLBuilder(table: Table[_]) extends super.TableDDLBuilder(table) { override def createPhase1 = super.createPhase1.mkString(""). replaceAll("CREATE TABLE", "CREATE TEMPORARY TABLE") } } object MyPostgresDriver extends MyPostgresDriver with MyTableComponent
  • 46.
    Генерация SQL (ILIKE) 46 traitILikeComponent { driver : JdbcPostgresDriver => override def createQueryBuilder(n: Node, state: CompilerState) = new QueryBuilder(n, state) class QueryBuilder(tree: Node, state: CompilerState) extends super. QueryBuilder(tree, state) { override def expr(n: Node, skipParens: Boolean = false) = n match { case Library.ILike(l, r) => b"($l ilike $r)“ case _ => super.expr(n, skipParens) } } } final class ILikeStringColumnExtensionMethods[P1](val c: Column[P1]) extends AnyVal with ExtensionMethods[String, P1] { def ilike[P2, R](e: Column[P2], esc: Char = 'u0000')(implicit om: o#arg[String, P2]#to[Boolean, R]) = if(esc == 'u0000') om.column(Library.ILike, n, e.toNode) else om.column(Library.ILike, n, e.toNode, LiteralNode(esc)) } object Library { val ILike = new FunctionSymbol(“ILike") }
  • 47.
    Ключевые особенности Slick Functional Relational Mapping  Целостная архитектура  Модульность и расширяемость  Новый строго типизированный язык запросов (по-видимому, эквивалентный по выразительности SQL) 47 http://slick.typesafe.com/ https://github.com/slick/slick/
  • 48.

Editor's Notes

  • #2 Титульный лист, содержание.2. Введение для тех, кто вообще не в курсе. * Несколько слайдов (2+) с примерами запросов. * Пример текстового запроса SQL. [ ] уточнить, как реализованы текстовые запросы. * краткий перечень возможностей Самым продвинутым в настоящее время, на мой взгляд, является Slick (http://slick.typesafe.com/doc/2.1.0/introduction.html). [ ] добавить перечень механизмов работы Описание схемы1. Описание простой таблицы с 3 колонками (id, name, ageOption) class UserTable(tag:Tag) extends Table[(Long, String, Option[Int])] (tag, "user"){ def id = column[Long]("id", O.AutoInc) def name = column[String]("name", O.DBType("text")) def ageOpt = column[Option[Int]]("age") def * = (id, name, ageOpt)}val users = TableQuery[UserTable]2. Запросы2.1 "SELECT * FROM user" val allUsers = users.run2.2. "SELECT name FROM user WHERE id=?" def getUserNameQuery(id:Column[Long]) = users.filter(_.id===id).map(_.name) val getUserName = Compiled(getUserNameQuery)3. Структура запроса(можно вставить картинку из презентации1. Хорошая презентация по архитектуре Slick http://www.slideshare.net/jaxLondonConference/scaling-scala-to-the-database-stefan-zeiger-typesafe см. )4. Как работает магия отложенного выполнения запроса.Все операторы - перегружены, все простые типы данных поддерживаются в виде Rep[T].По мере выполнения кода формирования запроса строится типизированный Rep-объект, содержащий внутри неполное AST-дерево, а снаружи выглядящий как коллекция.Выполняются не сами вычисления, а выполняются их заместители, которые вместо вычислений строят дерево.5. Вставка данных. users += (1, "Vasya", Some(1))6. Вставка с автоинкрементом Id - игнорируется для AutoInc- колонок! Это позволяет при вставке использовать тот же тип строки, что и при чтении.Кроме того, поддерживается конструкция returning7. Вставка данных из другой таблицы на стороне сервера БД. users.insert
  • #3 Для Scala существует несколько механизмов работы с базами данных (Anorm, scalikeJdbc, jooq, ... ) со своими pros’ами и cons’ами. При реализации систем, в которых БД играет ключевую роль, Slick – вполне подходящая технология. Если требуется прочитать пару таблиц, то лучше работают sql-based подходы (в Slick’е тоже есть sql-interpolation).
  • #4 Основные возможности Slick: * запросы пишутся на Scala-DSL (lifted) все запросы - строго типизированные запросы можно комбинировать поддерживает все основные СУБД * lifted
  • #5 Примеры взяты из презентации Stefan Zeiger’а 2014-09-24 Introduction to Slick 2.1 and 2.2 / ScalaCamp #7, Kraków, Poland Video - coming soon Slides (PDF) Event
  • #7 Stefan Zeiger 2014-09-24 Introduction to Slick 2.1 and 2.2 / ScalaCamp #7, Kraków, Poland Video - coming soon Slides (PDF) Event
  • #11 http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf Stefan Zeiger 2014-09-24 Introduction to Slick 2.1 and 2.2 / ScalaCamp #7, Kraków, Poland Video - coming soon Slides (PDF) Event
  • #12 Stefan Zeiger – ещё одна презентация по Slick’у – 2013-09-25 Scaling Scala to the Database / JavaOne 2013, San Francisco, CA, USA by Stefan Zeiger and Jan Christopher Vogt Video Live Code Example Slides (PDF) Slides (Powerpoint) Event Website
  • #13 http://slick.typesafe.com/talks/2014-09-24_ScalaCamp/Introduction_to_Slick_2.1_and_2.2.pdf Stefan Zeiger 2014-09-24 Introduction to Slick 2.1 and 2.2 / ScalaCamp #7, Kraków, Poland Video - coming soon Slides (PDF) Event
  • #14 Stefan Zeiger – ещё одна презентация по Slick’у – 2013-09-25 Scaling Scala to the Database / JavaOne 2013, San Francisco, CA, USA by Stefan Zeiger and Jan Christopher Vogt Video Live Code Example Slides (PDF) Slides (Powerpoint) Event Website
  • #16 case class Coffee( name: String, supplierId: Int, price: Double ) val coffees = Set( Coffee("Colombian", 101, 7.99), Coffee("French_Roast", 49, 8.99), Coffee("Espresso", 150, 9.99) )
  • #18 2. AST - синтаксическое дерево Синтаксическое дерево состоит из узлов Node, символов Symbol, типов Type Node образуют иерархию trait'ов, листами являются case class'ы, с которыми в дальнейшем выполняется PatternMatching (при компиляции). Node'ы как раз и представляют формируемое постепенно дерево запроса (AST). Стандартный набор символов/узлов собирается в библиотеку Library - простые val - экземпляры (см. scala.slick.ast.Library). В PgSlick аналогичным образом набирается своя библиотека символов. Type - отдельная иерархия, описывающая все возможные типы данных. Типы определяются в ходе работы компилятора запросов (по-видимому, фаза typer)
  • #19 Все Node’ы поддерживают rebuild – для выполнения операции rewrite в ходе компиляции. TableExpansion – ссылка на таблицу (TableNode) и на колонки, которые у неё есть.
  • #21 CollectionType (коллекция элементов указанного типа)
  • #22 3. lifted и Rep - ассортимент компонентов, похожих на коллекции Scala, для формирования AST. Построение AST осуществляется с использованием классов пакета lifted. Все объекты, используемые на этапе построения дерева, унаследованы от типа Rep и могут быть непосредственно сконвертированы в Node (часть AST-дерева). К объектам Rep привязываются extension-методы, позволяющие конструировать более сложные конструкции (ColumnExtensionMethods). Любой Node может быть превращён в Column с помощью метода Column.forNode(n:Node). В частности, конструирование простых выражений осуществляется двойным преобразованием - вначале из колонок берутся их узлы, применяется функция (строится AST, содержащее узел-функцию и дочерние узлы - аргументы), а затем конструируется колонка, символизирующая результат выполнения функции. Column extends ColumnLowPriority { def forNode[T : TypedType](n: Node): Column[T] = new Column[T] { def toNode = n } Строгая типизация достигается за счёт implicit TypedType[T] везде, где используется пользовательский тип.
  • #24 Shape При конструировании запросов рука об руку идут два типа - тип значения и lifted-тип. Чтобы увязать эти два типа, используется семейство наследников Shape. Степень жёсткости связи регулируется ShapeLevel от NestedShapeLevel до ColumnsShapeLevel в зависимости от допустимых возможностей в запросах. Самый слабый тип - NestedShapeLevel (допустимы коллекции), затем - FlatShapeLevel (без коллекций), ColumnsShapeLevel - допустимы только колонки (как аргументы откомпилированного запроса). Shape'ы особым образом определены для прозрачных контейнеров - tuple, HList. Это связано с необходимостью двоякого представления составных/сконструированных типов - как типы значений (например, (T1, T2)), так и тип lifted (packed) - (Column[T1], Column[T2]). А так как возможно появление и не lifted-типов среди lifted, то допускаются Mixed - типы (T1, Column[T2]). Для простых типов Shape'ы определены сразу же с помощью конвертации. Для контейнеров - путём конструирования из элементов. Shape умеет выполнять конвертацию
  • #25 OptionMapperDSL Весьма любопытный механизм для работы с Optional-типами. Поддерживаются бинарные и тернарные операции над колонками. При этом поддерживаются все 2^n варианта option/не option. Требуемый тип OptionMapper'а формируется с помощью красивого type-construction dsl: object OptionMapperDSL { type arg[B1, P1] = { type to[BR, PR] = OptionMapper2[B1, B1, BR, P1, P1, PR] type toSame = OptionMapper2[B1, B1, B1, P1, P1, P1] type arg[B2, P2] = { type to[BR, PR] = OptionMapper2[B1, B2, BR, P1, P2, PR] type arg[B3, P3] = { type to[BR, PR] = OptionMapper3[B1, B2, B3, BR, P1, P2, P3, PR] } } } } OptionMapperDSL позволяет указать по два типа каждого аргумента для функции одного, двух или трёх аргументов, а затем два типа результата функции. Два типа каждого аргумента используются для элегантного представления Option-типов (см.ниже). Рассмотрим, как используется такой type-construction DSL: trait ColumnExtensionMethods[B1, P1] extends Any with ExtensionMethods[B1, P1] { protected[this] def c: Column[P1] def === [P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]) = om.column(Library.==, n, e.toNode) } Для колонки, имеющей тип P1, с помощью implicit'ов создаётся объект, к которому подмешан ColumnExtensionMethods. У этого объекта можно вызвать === с аргументом - другой колонкой. Для представления типа исходной колонки используется сразу два type-аргумета - B1 (B-base) и P1. Если колонка - обычного, не option-типа, то B1 =:= P1. Если же P1 является Option[_], то B1 - вложенный в Option тип, т.е. B1 - это результат обратной функции Option: P1 = Option[B1]. Таким образом, с помощью типа B1 мы выберем подходящий экземпляр OptionMapper'а (и сможем воспользоваться, например, его свойствами в ходе Runtime-обработки.) Если хотя бы один из аргументов - Optional, то всё выражение будет иметь тип PR (Option[Result]), если же все элементы - не Option, то будет выбран plain OptionMapper и Конечной целью OptionMapperDSL является отражение логики Null-propagation в SQL. Если какая-то колонка в выражении - Optional, то и всё выражение будет иметь тип Option. Если все - обязательные, тогда и колонка будет иметь простой тип.
  • #26 OptionMapperDSL Весьма любопытный механизм для работы с Optional-типами. Поддерживаются бинарные и тернарные операции над колонками. При этом поддерживаются все 2^n варианта option/не option. Требуемый тип OptionMapper'а формируется с помощью красивого type-construction dsl: object OptionMapperDSL { type arg[B1, P1] = { type to[BR, PR] = OptionMapper2[B1, B1, BR, P1, P1, PR] type toSame = OptionMapper2[B1, B1, B1, P1, P1, P1] type arg[B2, P2] = { type to[BR, PR] = OptionMapper2[B1, B2, BR, P1, P2, PR] type arg[B3, P3] = { type to[BR, PR] = OptionMapper3[B1, B2, B3, BR, P1, P2, P3, PR] } } } } OptionMapperDSL позволяет указать по два типа каждого аргумента для функции одного, двух или трёх аргументов, а затем два типа результата функции. Два типа каждого аргумента используются для элегантного представления Option-типов (см.ниже). Рассмотрим, как используется такой type-construction DSL: trait ColumnExtensionMethods[B1, P1] extends Any with ExtensionMethods[B1, P1] { protected[this] def c: Column[P1] def === [P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]) = om.column(Library.==, n, e.toNode) } Для колонки, имеющей тип P1, с помощью implicit'ов создаётся объект, к которому подмешан ColumnExtensionMethods. У этого объекта можно вызвать === с аргументом - другой колонкой. Для представления типа исходной колонки используется сразу два type-аргумета - B1 (B-base) и P1. Если колонка - обычного, не option-типа, то B1 =:= P1. Если же P1 является Option[_], то B1 - вложенный в Option тип, т.е. B1 - это результат обратной функции Option: P1 = Option[B1]. Таким образом, с помощью типа B1 мы выберем подходящий экземпляр OptionMapper'а (и сможем воспользоваться, например, его свойствами в ходе Runtime-обработки.) Если хотя бы один из аргументов - Optional, то всё выражение будет иметь тип PR (Option[Result]), если же все элементы - не Option, то будет выбран plain OptionMapper и Конечной целью OptionMapperDSL является отражение логики Null-propagation в SQL. Если какая-то колонка в выражении - Optional, то и всё выражение будет иметь тип Option. Если все - обязательные, тогда и колонка будет иметь простой тип.
  • #27 AbstractTable и Tag "Таблица" используется как namespace для колонок. И строит expression от таблицы до колонок. Узел таблицы предоставляется внешним выражением, которое строится (запрос).
  • #28 Stefan Zeiger – ещё одна презентация по Slick’у – 2013-09-25 Scaling Scala to the Database / JavaOne 2013, San Francisco, CA, USA by Stefan Zeiger and Jan Christopher Vogt Video Live Code Example Slides (PDF) Slides (Powerpoint) Event Website
  • #29 Драйвер 1. Конструирование драйвера путём объединения поддержки отдельных возможностей. (Profile & Component) "Cake pattern в полный рост" * драйвер собирается из trait'ов. Интерфейс собирается путём перекрытия одного и того же класса - SimpleQL * механизм трейтов хорошо задействован в PgSlick - адаптер для Postgres-возможностей. трейт ***Profile - наследуется от ***Component (JdbcTypesComponent или Каждый Component - объявляет в целом независимый набор возможностей в виде функций, типов, классов, трейтов. Profile - объединяет несколько компонентов. По сути является целостным интерфейсом драйвера. Именно profile'ы задают внешний вид драйвера. Существуют следующие профайлы: * BasicProfile * RelationalProfile * SqlProfile * JdbcProfile * MemoryQueryingProfile * MemoryProfile * DistributedProfile Драйвер реализует профайл и иногда ещё какой-нибудь компонент. Каждый профайл собирается из компонентов. Прямо внутри профайла перекрываются классы/методы так, чтобы использовать возможности компонентов. Часто этот приём используется для внутренних задач sqlBuilder'ы что позволяет внедриться в любой элемент системы. (hook)
  • #30 trait BasicProfile extends BasicInvokerComponent with BasicInsertInvokerComponent with BasicExecutorComponent with BasicActionComponent trait RelationalProfile extends BasicProfile with RelationalTableComponent with RelationalSequenceComponent with RelationalTypesComponent with RelationalActionComponent trait SqlProfile extends RelationalProfile with SqlExecutorComponent with SqlTableComponent with SqlActionComponent trait BasicDriver extends BasicProfile trait RelationalDriver extends BasicDriver with RelationalProfile trait SqlDriver extends RelationalDriver with SqlProfile with SqlUtilsComponent trait JdbcDriver extends SqlDriver with JdbcProfile with JdbcStatementBuilderComponent with JdbcMappingCompilerComponent trait PostgresDriver extends JdbcDriver
  • #31 Типичный пример - сборка SimpleQL- интерфейса. В каждом профайле переопределяется экземпляр simple: val simple: SimpleQL = new SimpleQL {} а также перекрывается класс SimpleQL (наследуясь от родителя): trait SimpleQL extends super.SimpleQL { } пользователь делает простой импорт: import SomeDriver.simple._ и автоматически получает все типы и методы, объявленные в конкретном драйвере. Причём наследуются все возможности родительского драйвера. Подключение новых типов ColumnOption (для сохранения специфической для драйвера информации в определении колонки). Можно посмотреть пример в SqlTableComponent. Объявляется trait ColumnOptions extends super.ColumnOptions , который содержит новую опцию. Переопределяется val columnOptions, чтобы создавался новый тип. А в предке определён val O: driver.columnOption.type. Тем самым объект O, доступный пользователю, будет содержать новый элемент.
  • #32 Capability Возможности драйвера представлены коллекцией объектов типа Capability. Экземпляры этого класса объявляются в companion-объекте. object RelationalProfile { object capabilities { /** Supports default values in column definitions */ val columnDefaults = Capability("relational.columnDefaults") В каждом Component'е перекрывается метод override protected def computeCapabilities (и, естественно, вызывается super). Тем самым, объединение трейтов автоматически приводит к объединению списка возможностей.
  • #33 Stefan Zeiger – ещё одна презентация по Slick’у – 2013-09-25 Scaling Scala to the Database / JavaOne 2013, San Francisco, CA, USA by Stefan Zeiger and Jan Christopher Vogt Video Live Code Example Slides (PDF) Slides (Powerpoint) Event Website
  • #35 Все фазы компиляции унифицированы, работают по принципу rewrite
  • #36 1. Подключение новых типов ColumnOption (для сохранения специфической для драйвера информации в определении колонки). Можно посмотреть пример в SqlTableComponent. Объявляется trait ColumnOptions extends super.ColumnOptions , который содержит новую опцию. Переопределяется val columnOptions, чтобы создавался новый тип. А в предке определён val O: driver.columnOption.type. Тем самым объект O, доступный пользователю, будет содержать новый элемент. Аналогично SimpleQL Comment(comment: String) extends ColumnOption[Nothing] supportsInheritance = Capability("postgres.supportsInheritance") class JdbcTypes extends super.JdbcTypes { override val stringJdbcType = new StringJdbcType class StringJdbcType extends super.StringJdbcType { override def createDDLInvoker(ddl: SchemaDescription) = new DDLInvoker(ddl) class DDLInvoker(ddl: DDL) extends super.DDLInvoker(ddl) Свои методы в SimpleQL trait DatabaseMetaHelpersQL { def isTableExists Наследование SlickPg inheritance Свои методы для создания колонок (col, colOpt) Составной тип в Postgres объявление составного типа CREATE TYPE my_type AS (a INT, b INT) объявление scala-типа case class MyType(a:Int, b:Int) реализация наследника BaseTypedType[T], в частности, JdbcType[T] Создание и вызов custom-функции в Postgres. 5. Примеры расширения возможностей Slick. Стандартные пути расширения 1. Объявление схемы данных * tableConstraints - можно перекрыть у таблицы и вернуть ограничения (вызвав super). 1. Своя функция в БД 2. Свой составной тип данных (case class), имеющий идентичный Postgres-тип в БД 3. Маппинг строки таблицы в свой тип данных. MappedProjection 4. * Использование своих классов напрямую (Shape) Примеры расширения возможностей Slick. 1. Приведение типа ::my_type (уже есть Library.Cast, asColumnOfType 2.
  • #37 http://slick.typesafe.com/doc/2.1.0/userdefined.html SimpleFunction.unary[Date, Int]("day_of_week") Сложные функции – через plain sql пользовательские типы записей (Record, Shape)
  • #38 val q1 = for { (age, q) <- persons.map(s => (getAge(s.dateOfBirth), s.id)).groupBy(_._1) } yield (age, q.map(_._2).size)
  • #39 val q1 = for { (age, q) <- persons.map(s => (getAge(s.dateOfBirth), s.id)).groupBy(_._1) } yield (age, q.map(_._2).size)
  • #43 Перечислить возможности Slick-pg по расширению Slick’а custom row type (mapped projection) [9:57:37] Arseniy Zhizhelev: PgEnumSupport [10:07:59] Arseniy Zhizhelev: PgRange [10:08:09] Arseniy Zhizhelev: Json support, Play Json support Date/Time Enum Range Hstore LTree JSON Inet/MacAddr text Search postgis Geometry
  • #44 import slick.driver.PostgresDriver import com.github.tminglei.slickpg._ trait MyPostgresDriver extends PostgresDriver with PgArraySupport with PgDateSupport with PgRangeSupport with PgHStoreSupport with PgPlayJsonSupport with PgSearchSupport with PgPostGISSupport { override val pgjson = "jsonb" //to keep back compatibility, pgjson's value was "json" by default override lazy val Implicit = new ImplicitsPlus {} override val simple = new SimpleQLPlus {} ////// trait ImplicitsPlus extends Implicits with ArrayImplicits with DateTimeImplicits with RangeImplicits with HStoreImplicits with JsonImplicits with SearchImplicits with PostGISImplicits trait SimpleQLPlus extends SimpleQL with ImplicitsPlus with SearchAssistants with PostGISAssistants } object MyPostgresDriver extends MyPostgresDriver
  • #45 abstract class ColumnOption[+T]
  • #49 Нужны пользователи Нужны разработчики