avpavlov,
A>В Скала намного больше мета-информации доступно, и то к ЛИНКу даже близко не приблизились. Правда макросы сейчас пилят, вот на макросах уже можно будет ЛИНК сделать.
Твои сведения либо сильно искажены, либо устарели. Typesafe Slick полностью повторяет Linq, а в версии 2.0 идëт намного дальше в сторону NoSql баз данных.
Макросы позволяют более эффективно генерировать запросы и выдавать более человеческие ошибки (т.н. direct query Api), функционал же доступен уже сейчас через lifted api.
LCR>Твои сведения либо сильно искажены, либо устарели. Typesafe Slick полностью повторяет Linq,
Либо ты смотришь через розовые очки
Сравним
LIN2SQL — обрати внимание, компилятор подтянул имена CompanyName и Phone в кортеж, поэтому нам не надо помнить, что там первое, что второе
var q =
from c in db.Customers
where c.City == "London"
select new { c.CompanyName, c.Phone };
foreach(var c in q)
Console.WriteLine("{0}, {1}", c.CompanyName, c.Phone);
Slick — старые добрые _1 и _2
// Perform a join to retrieve coffee names and supplier names for
// all coffees costing less than $9.00
println("Manual join:")
val q2 = for {
c <- coffees if c.price < 9.0
s <- suppliers if s.id === c.supID
} yield (c.name, s.name)
for(t <- q2) println(" " + t._1 + " supplied by " + t._2)
Ну и синтаксис у Линка куда ближе к реальному СКЛ, чем у Слика
avpavlov,
LCR>>Твои сведения либо сильно искажены, либо устарели. Typesafe Slick полностью повторяет Linq, A>Либо ты смотришь через розовые очки
Возможно.
A>Сравним A>LIN2SQL — обрати внимание, компилятор подтянул имена CompanyName и Phone в кортеж, поэтому нам не надо помнить, что там первое, что второе
Во-первых. Компилятор C# конечно молодец, однако всё что здесь делается — это используется анонимный тип с именованными полями.
Аналог в Скала тоже можно нарисовать при желании:
case class Supply(coffeeName: String, supplierName: String)
// Perform a join to retrieve coffee names and supplier names for
// all coffees costing less than $9.00
println("Manual join:")
val q2 = for {
c <- coffees if c.price < 9.0
s <- suppliers if s.id === c.supID
} yield Supply(c.name, s.name)
for(t <- q2) println(" " + t.coffeeName + " supplied by " + t.supplierName)
Во-вторых, речь шла о захвате AST (в терминологии Expression Tree) и генерации по нему SQL инструкций. И утверждалось, что де Скала даже близко не стоит. Ответ — стоит, и даже умеет кое-что большее.
A>Ну и синтаксис у Линка куда ближе к реальному СКЛ, чем у Слика
Это необязательное требование Linq, более того спорное — Linq-выражения выглядят чуждо, интеллисенс на них работает криво и многим это не нравится. Исключительно синтаксический сахар.
В Скала такой сахар отсутствует (точнее никому не потребовалось его изобретать), но с другой стороны, в Скала не потребовались и спец. костыли для поддержки нового синтаксиса, а вся работа делается на привычном и поддерживаемом синтаксисе.
Здравствуйте, lazy-cjow-rhrr, Вы писали:
A>>Ну и синтаксис у Линка куда ближе к реальному СКЛ, чем у Слика LCR>Это необязательное требование Linq, более того спорное — Linq-выражения выглядят чуждо, интеллисенс на них работает криво и многим это не нравится. Исключительно синтаксический сахар. LCR>В Скала такой сахар отсутствует (точнее никому не потребовалось его изобретать), но с другой стороны, в Скала не потребовались и спец. костыли для поддержки нового синтаксиса, а вся работа делается на привычном и поддерживаемом синтаксисе.
Slick идёт нафиг не в последнюю очередь как раз из-за синтаксиса: переформулировать простейший group by на for comprehensions — да ну его нафиг. А уж если что посложнее приспичит...
Здравствуйте, lazy-cjow-rhrr, Вы писали:
LCR>Во-первых. Компилятор C# конечно молодец, однако всё что здесь делается — это используется анонимный тип с именованными полями. LCR>Аналог в Скала тоже можно нарисовать при желании:
Не, это не аналог. Это рисование нового класса для каждого кортежа. Линк делает тоже самое, но за сценой и не заставляет программиста об этом думать.
LCR>
LCR> case class Supply(coffeeName: String, supplierName: String)
LCR> } yield Supply(c.name, s.address)
LCR> for(t <- q2) println(" " + t.coffeeName + " supplied by " + t.supplierName)
LCR>
См. выделенное. Я исправил, а Скала и не заметила, а ЛИНК даст компайл-тайм еррор и заставит меня убедиться, что логика приложения не нарушена
LCR>Во-вторых, речь шла о захвате AST (в терминологии Expression Tree) и генерации по нему SQL инструкций. И утверждалось, что де Скала даже близко не стоит. Ответ — стоит, и даже умеет кое-что большее.
Нет, утверждалось что без макросов не стоит. С макросами стоит, но это ещё мало где с умом прикручено.
LCR>Это необязательное требование Linq, более того спорное — Linq-выражения выглядят чуждо, интеллисенс на них работает криво и многим это не нравится. Исключительно синтаксический сахар.
avpavlov,
LCR>>Во-первых. Компилятор C# конечно молодец, однако всё что здесь делается — это используется анонимный тип с именованными полями. LCR>>Аналог в Скала тоже можно нарисовать при желании:
A>Не, это не аналог. Это рисование нового класса для каждого кортежа. Линк делает тоже самое, но за сценой и не заставляет программиста об этом думать.
Классы моделей в любом случае нужны, и их можно (и нужно) здесь использовать. Анонимные классы в C# имеют помимо указанных тобой достоинств и ряд недостатков и ограничений, так что предпочтительнее использовать нормальные классы.
LCR>> case class Supply(coffeeName: String, supplierName: String)
LCR>> } yield Supply(c.name, s.address)
LCR>> for(t <- q2) println(" " + t.coffeeName + " supplied by " + t.supplierName)
A>См. выделенное. Я исправил, а Скала и не заметила, а ЛИНК даст компайл-тайм еррор и заставит меня убедиться, что логика приложения не нарушена
Что-то, аргументация несколько однобокая и неубедительная. Попробуй сейчас не заметить:
Есть определённое преимущество у C# в случае (2). По мне, так весьма частный случай, когда нам не нужно использовать модели (почему-то). Обычно всё-таки создавать модели нужно.
LCR>>Во-вторых, речь шла о захвате AST (в терминологии Expression Tree) и генерации по нему SQL инструкций. И утверждалось, что де Скала даже близко не стоит. Ответ — стоит, и даже умеет кое-что большее.
A>Нет, утверждалось что без макросов не стоит. С макросами стоит, но это ещё мало где с умом прикручено.
Синтаксический сахар необязателен. Что-то ещё?
A>А мне нравится синтаксис SQL
На здоровье. Мне — нравятся HOF.
ЧТД. Требуется вводить coding rules и следить, чтобы их все соблюдали, вместо перекладывания на компилятор.
LCR>Совершенно аналогично ты и в конструктор анонимного типа можешь передать неправильное значение (случай 1). Скажешь, не было? LCR>new {CoffeeName = c.Name, SupplierName = s.Address} // 1
ЧТД. Если использование ЛИНКА намеренно ухудшить, то получится как в Скале
LCR>Синтаксический сахар необязателен. Что-то ещё?
Если синтаксический сахар необязателен, то вообще вся эта тема не нужна и непонятно зачем ты в неё влез. Работу с базой делали ещё на Коболе 50 лет назад и не жужжали.
A>>А мне нравится синтаксис SQL LCR>На здоровье. Мне — нравятся HOF.
Вот и разработчикам библиотек для Скала нравится. Из-за этого так и будем иметь на выходе УГ, пока за разработку не возьмётся человек, которому нравится SQL
dimgel,
D>Slick идёт нафиг не в последнюю очередь как раз из-за синтаксиса: переформулировать простейший group by на for comprehensions — да ну его нафиг. А уж если что посложнее приспичит...
А что не так? Работаешь с таблицами как с массивами и в ус не дуешь — интеллисенс и типобезопасность в комплекте
session.withTransaction {
// seek for the jobs that have specified status and earlier than lowerBound and select the oldest one
val jobsOpt = Query(schema.Jobs)
.filter(_.status === status)
.filter(_.isDeleted === Active.value)
.filter(_.fireTime <= lowerBound)
// .sortBy(_.fireTime.desc) -- newer jobs are higher
.sortBy(_.fireTime).firstOption
if (jobsOpt.isDefined) {
val job: Job = jobsOpt.get
val jobId = job.id.get
logger.debug(s"Job (id = $jobId) is selected to acquire")
// try to aquire it
val n = schema.Jobs.filter(_.id === jobId).update(job.copy(status = Status.next(status)))
if (n > 0) {
logger.debug(s"Job (id = $jobId) is acquired for processing")
selectByWithDeps(jobId)
} else {
logger.debug(s"Job (id = $jobId) is failed to acquire")
None
}
} else None
}
Если конечно у человека в прошлом опыт DBA, то тут да, будут сложности. Ну в этом случае ждите макросов, чё.
Здравствуйте, lazy-cjow-rhrr, Вы писали:
A>>А мне нравится синтаксис SQL LCR>На здоровье. Мне — нравятся HOF.
Вы оба не правы, а вот мне нравится, когда поменьше разных синтаксисов, так что если SQL — необсуждаемая данность, которой частенько приходится пользоваться для ковыряния в потрохах базы, то забивать себе башку ещё одним диалектом, через зад в этот SQL компилирующимся — довольно глупо.
avpavlov, Вы писали:
A>Если синтаксический сахар необязателен, то вообще вся эта тема не нужна и непонятно зачем ты в неё влез. Работу с базой делали ещё на Коболе 50 лет назад и не жужжали.
Ценность и польза Linq вообще не в синтаксическом сахаре. И Кобол тут непричём.
Здравствуйте, lazy-cjow-rhrr, Вы писали:
LCR>dimgel,
D>>Slick идёт нафиг не в последнюю очередь как раз из-за синтаксиса: переформулировать простейший group by на for comprehensions — да ну его нафиг. А уж если что посложнее приспичит...
LCR>А что не так? Работаешь с таблицами как с массивами и в ус не дуешь — интеллисенс и типобезопасность в комплекте
Не так то, что мне частенько приходилось нарываться на ситуации, когда два логически эквивалентных запроса, сформулированных через join и через вложенный подзапрос, дают ОООЧЕНЬ разную эффективность. Мне также приходилось заниматься оптимизацией постгреса на огромной базе, разбивая сложные запросы на сотню маленьких медведей с использованием временных промежуточных таблиц, потому что эта дрянь убей бог не хотела давать мне нужный план. И по итогам всего этого суммарно-многолетнего секса я предпочитаю иметь ПОЛНЫЙ контроль над видом генерируемого SQL, что естественным образом достигается только если статически типизированный язык запросов имеет близкий к SQL синтаксис, а не является очередным <матерно>paradigm mismatch bridge</матерно>.
dimgel,
LCR>>А что не так? Работаешь с таблицами как с массивами и в ус не дуешь — интеллисенс и типобезопасность в комплекте
D>Не так то, что мне частенько приходилось нарываться на ситуации, когда два логически эквивалентных запроса, сформулированных через join и через вложенный подзапрос, дают ОООЧЕНЬ разную эффективность. Мне также приходилось заниматься оптимизацией постгреса на огромной базе, разбивая сложные запросы на сотню маленьких медведей с использованием временных промежуточных таблиц, потому что эта дрянь убей бог не хотела давать мне нужный план. И по итогам всего этого суммарно-многолетнего секса я предпочитаю иметь ПОЛНЫЙ контроль над видом генерируемого SQL, что естественным образом достигается только если статически типизированный язык запросов имеет близкий к SQL синтаксис, а не является очередным <матерно>paradigm mismatch bridge</матерно>.
Цена абстракции-с. Зато типобезопасность, блэкджек и шлюхи, а для большой и чистой любви есть другие места.
Понятненько, спасибо за коммент, я такие вещи особенно люблю (в смысле вербализованный опыт коллег) Конечно, в твоём случае придётся использовать либо прямое исполнение SQL (в Slick тоже есть, но типобезопасность накрывается медным тазом), либо какой-нибудь примитивный враппер типа Anorm (или myBatis). Чтобы иметь полный контроль над сиквелем, генерируемом думателем (что Linq, что Slick), нужно частенько погружаться в кодяру и держать руку на пульсе логов. По виду Customers.First().CustomerOrders.SelectMany(item => item.Orders) невозможно догадаться, что эта строчка порождает кучу запросов вместо одного. Только либо знать, либо увидеть собственными глазами.
Я по своей воле тщательно избегал чересчур плотного погружения в SQL, дебри планов, повадки оптимизаторов и прочей хрени. Но однажды эта участь таки меня настигла — я примерно на 4.5 года вынужден был основательно потра...подружиться с мускулем и мелкософтовским сервером. Это был безусловно полезный и нужный опыт, я много подчерпнул для себя полезного. Но он (опыт) отложил отпечаток — с тех пор я стараюсь держать DAO слой как можно проще, пусть даже ценой какого-то ухудшения перфоманса в некритических местах. Потому что жизнь одна, а субдей много, и в каждой ещё по выводку тараканов. А ежели DAO простой как три копейки, то и возможностей что Linq, что Slick хватает с избытком. Лично я Slick, за вычетом ряда раздражающих неудобств, полностью доволен.
Здравствуйте, lazy-cjow-rhrr, Вы писали:
LCR>Цена абстракции-с. Зато типобезопасность,
Типобезопасность тут ортогональна, её и squeryl обеспечивает, который гораздо ближе к SQL, чем Slick. И точно такой же синтаксис можно на макросах обеспечить.
LCR>блэкджек и шлюхи,
Да вот что-то не видать. Сложный запрос запаришься формулировать. Разве что если работать с базой как с плоским хранилищем, то да, наверное стандартными scala-циклами удобнее.
LCR>А ежели DAO простой как три копейки, то и возможностей что Linq, что Slick хватает с избытком.
Во-во, ну а я привык в духе anemic — оптимизированная загрузка данных достаточно развесистыми запросами.
Аноним,
А>Наткнулся тут на статью по этому поводу. А>Кто что думает по этому поводу? А>А там есть упоминание вот этого.
Внезапно появилась новая библиотека для Scala, которая сильно использует макросовскую магию и реализует типобезопасные SQL-и, которые выглядят прямо как родные:
object Product {
def create(name: String, price: Long)(implicit s: DBSession = AutoSession): Long = {
sql"insert into products values (${name}, ${price})"// don't worry, it prevents SQL injection!
.updateAndReturnGeneratedKey.apply() // returns auto-incremeneted id
}
def findById(id: Long)(implicit s: DBSession = AutoSession): Option[Product] = {
sql"select id, name, price, created_at from products where id = ${id}"
.map { rs => Product(rs) }.single.apply()
}
}
Product.findById(123) // borrows connection from pool and gives it back after execution
LCR>Внезапно появилась новая библиотека для Scala, которая сильно использует макросовскую магию и реализует типобезопасные SQL-и, которые выглядят прямо как родные:
Да, а вот магия
withSQL {
select
.from(Programmer as p)
.leftJoin(Company as c).on(p.companyId, c.id)
.where.eq(p.isDeleted, false)
.orderBy(p.createdAt)
.limit(10)
.offset(0)
}.map(Programmer(p, c)).list.apply()
Здравствуйте, lazy-cjow-rhrr, Вы писали:
LCR>>Внезапно появилась новая библиотека для Scala, которая сильно использует макросовскую магию и реализует типобезопасные SQL-и, которые выглядят прямо как родные: LCR>Да, а вот магия
LCR>>Внезапно появилась новая библиотека для Scala, которая сильно использует макросовскую магию и реализует типобезопасные SQL-и, которые выглядят прямо как родные:
LCR>Да, а вот магия LCR>
Ну не знаю. Мне, кажется, макросы [должны быть] способны на большее. В примеру выше почти всё можно было и без макросов сделать. Только алиасы да проперти бинов. Причём в отсутствие макросов от пропертей можно было бы отказаться и сделать из них дескрипторы колонок, тогда только алиасы бы нереализованными остались.
Здравствуйте, dimgel, Вы писали:
D>Здравствуйте, lazy-cjow-rhrr, Вы писали:
LCR>>>Внезапно появилась новая библиотека для Scala, которая сильно использует макросовскую магию и реализует типобезопасные SQL-и, которые выглядят прямо как родные: LCR>>Да, а вот магия
D>Блин, опередили, окаянные.
Если ты хотел что-то написать, то пиши. Они ещё никого не опередили
Здравствуйте, avpavlov, Вы писали:
D>>Блин, опередили, окаянные.
A>Если ты хотел что-то написать, то пиши. Они ещё никого не опередили
Пишу и буду писать. Чужим фреймворкам в таких тонких вопросах доверять нельзя, это я уже понял. К тому же вряд ли у них есть поддержка prepared statements и компиляции императивного кода в хранимки.