Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataBase 和Query解耦 #41

Open
jiaojing opened this issue Sep 2, 2019 · 8 comments
Open

DataBase 和Query解耦 #41

jiaojing opened this issue Sep 2, 2019 · 8 comments
Labels

Comments

@jiaojing
Copy link

@jiaojing jiaojing commented Sep 2, 2019

query,最后map的时候会调用global的DataBase执行。是否可以类似:slick
// DB.run(DBIOActions) 这种方式。

项目非常棒,💪

@vincentlauvlwj

This comment has been minimized.

Copy link
Owner

@vincentlauvlwj vincentlauvlwj commented Sep 2, 2019

这是可以的,看看这段代码是不是你想要的效果?

val db = Database.connect(url = "jdbc:h2:mem:ktorm;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")

val names = db {
    Employees
        .select(Employees.name)
        .where { Employees.departmentId eq 1 }
        .orderBy(Employees.salary.desc())
        .map { it.getString(1) }
}

assert(names.size == 2)
assert(names[0] == "vince")
assert(names[1] == "marry")
@jiaojing

This comment has been minimized.

Copy link
Author

@jiaojing jiaojing commented Sep 3, 2019

嗯,我看到了,可以这样写。但是这个db是通过threadlocal的方式传递给map的。如果不用这个db{}包裹代码块,容易有坑(代码也不会提示不可以不包裹),比如在多线程执行的时候。 另外,把query定义为一个SQL构造器,而不直接map执行。用类似这样的方式进行绑定,个人认为代码风格更好些。

db.from(Employees)
    .where { e -> (e.organizationId ne null) and (e.name eq "John Doe") }
    .groupBy { e -> e.name }
    .having { e -> e.id ne null }
    .orderBy { e -> e.name.asc .. e.id.desc }
    .limit { 10 }
    .offset { 10 }
    .select { e -> e.id .. e.name }

或者类似slick这样:

val setup = DBIO.seq(
  // Create the tables, including primary and foreign keys
  (suppliers.schema ++ coffees.schema).create,

  // Insert some suppliers
  suppliers += (101, "Acme, Inc.",      "99 Market Street", "Groundsville", "CA", "95199"),
  suppliers += ( 49, "Superior Coffee", "1 Party Place",    "Mendocino",    "CA", "95460"),
  suppliers += (150, "The High Ground", "100 Coffee Lane",  "Meadows",      "CA", "93966"),
  // Equivalent SQL code:
  // insert into SUPPLIERS(SUP_ID, SUP_NAME, STREET, CITY, STATE, ZIP) values (?,?,?,?,?,?)

  // Insert some coffees (using JDBC's batch insert feature, if supported by the DB)
  coffees ++= Seq(
    ("Colombian",         101, 7.99, 0, 0),
    ("French_Roast",       49, 8.99, 0, 0),
    ("Espresso",          150, 9.99, 0, 0),
    ("Colombian_Decaf",   101, 8.99, 0, 0),
    ("French_Roast_Decaf", 49, 9.99, 0, 0)
  )
  // Equivalent SQL code:
  // insert into COFFEES(COF_NAME, SUP_ID, PRICE, SALES, TOTAL) values (?,?,?,?,?)
)

val setupFuture = db.run(setup)
  1. data class -> entity
  2. object table -> table schema 定义
  3. query表达式构造
  4. db.run

前3步都是和执行环境无关的,抽离之后,后续切换到coroutine 执行,只需要改进第4部分。

@vincentlauvlwj

This comment has been minimized.

Copy link
Owner

@vincentlauvlwj vincentlauvlwj commented Sep 3, 2019

谢谢你的建议,把 query 表达式的构造与执行环境分离确实有很大的意义,我会考虑如何改进

@jiaojing

This comment has been minimized.

Copy link
Author

@jiaojing jiaojing commented Sep 3, 2019

我们刚开始在服务端使用kotlin,评估下来,ktorm是orm这块做的比较好的,也感谢你对项目的付出。

@785172550

This comment has been minimized.

Copy link

@785172550 785172550 commented Oct 20, 2019

@jiaojing 我也在尝试用coroutine来执行sql, 但是感觉需要用异步的DB调用,而不是JDBC这种会阻塞线程的才有效,要不然也只是相当于在一个线程池里面做DB调用(这样同时的并发数,就是线程池数量与数据库连接池数量的两者最小值),我找到了这个数据库driver,
https://github.com/eclipse-vertx/vertx-sql-client
但是现在和ktorm不能结合,因为它不是JDBC协议的,我觉的能将构造sql表达和执行分离,就能用ktorm构造sql和将rowset转换为entity, 然后用vertx-sql-client 来执行数据库调用了,这样ORM框架就彻底和数据库driver层面解耦了

@jiaojing

This comment has been minimized.

Copy link
Author

@jiaojing jiaojing commented Jan 8, 2020

我看了一下这个 rm-global-database-object分支的代码,非常赞。关于coroutine这块:
目前数据库的异步的驱动,也就jasync-sql这一个库,执行SQL以后返回的是CompletableFuture。这个很容易就可以转换成suspend函数。
Ktorm要支持的话,目前这个结构可能还需要修改。
目前Ktorm的query最终会在map,count等iterator的方法调用的时候,lazy 初始化一个iterator,这时会执行数据库查询操作。
image
但是底层用jasync-sql的话,database.executeExpression这个函数签名肯定就是suspend的了,就会传染整个调用链路。从这个作为突破口,可能会比较容易支持coroutine。有一个不成熟的建议是:query构造和db彻底解耦。db.run(query),或者asyncdb.run(query).后者方法签名是suspend的。

@vincentlauvlwj

This comment has been minimized.

Copy link
Owner

@vincentlauvlwj vincentlauvlwj commented Jan 8, 2020

是这样的,异步和同步是两个完全不一样的世界,很多库都没办法做到完美地同时支持两者。

我的想法是,与 Database 对应,增加一个 AsyncDatabase,它们分别支持异步和同步。但是这样的话 database.from(..)database.sequenceOf(..) 就要改,增加 AsyncQueryAsyncEntitySequenceAsyncSequenceGrouping 等一系列的 counterpart,这样一套下来,几乎所有的代码我们都要写两遍。

所以,与其这么做,我觉得还不如另外建一个 ktorm-async 的项目,一个支持异步的专门的 Ktorm 版本。反正代码都要重新写一遍,我不如直接把同步的 Ktorm 和异步的 Ktorm 分为两个项目,它们之间顶多就共享 SqlExpressionSqlFormatter 之类的这些代码,也就是你说的 query 构造部分。

你看看这个思路如何?

@jiaojing

This comment has been minimized.

Copy link
Author

@jiaojing jiaojing commented Jan 13, 2020

嗯,也是一种方案。核心的就是SqlExpresion。然后DB执行他。

我去看了一下slick的方式。他是把query的拼接,数据库的其他操作,都和执行环境全部分离出来。 然后DB.run(DBIOAction)-> future 这种方式把所有最终的执行几乎都放到这一个函数里去了。这个函数返回的是future,也就是底层是同步(那就得自己用线程池转成异步的)或者异步的驱动都可以。所有的terminal 函数,最终调用这个run得到一个future。然后future就可以很简单转成suspend了。

不过这个我感觉不着急,kotlin目前还不成熟。我估计得等1.4以后,看看哪个版本kotlin的 multi-platform做好以后,coroutine的使用场景才会出来吧。目前也就Android下能用。服务端基本上都是spring的生态,基本用不到。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.