操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    類型體系是scala中最為復雜的特性,它既是scala強大的原因,也是scala號稱宇宙最復雜語言的直接原因。而且,Scala正在自我革命,新的.0規劃在很大程度上就是要在類型系統上大膽革命(參見:項目)。

    我在最早構建 scala-sql 這個數據庫訪問庫的時候,主要是想把的一些實現方式和早期的esql方式遷移到scala中(參考:ORM是否必要? - 王在祥的回答 - 知乎),但第一個版本在類型化上實現得并不理想,主要存在一下的問題:

    sql""中的插值是動態類型的,而在執行時sql中數據類型,則是通過反射,來決定是使用還是等基礎JDBC操作的。

    case class SQLWithArgs(sql: String, args: Seq[Any]) { ... }

    這種設計導致的問題是:我們可以將任意的值傳給sql,而在執行過程中,則因為類型無法識別,而產生。從而無法享受到編譯期的靜態類型檢查,這在感覺上也不符合scala的風格。

    難以擴展自定義類型的支持。

    在scala-sql中,我們有很多的場景需要支持自定義的數據類型,譬如:

    支持 scala. 類型,而不僅僅是 java.math.類型。

    支持 [Int], []等類型。

    支持 joda.Date 等類型。

    在 scala-sql 1.0中,雖然也支持了一定的擴展類型,但存在很嚴重的問題,其一是:這個擴展能力只能內建在內部,無法讓用戶擴展。其二是采用反射的方式來實現,代碼中大量的case match操作,很不優美。(參考:)

    很蹩腳的 ORM 實現。雖然我有些反感 、JPM這樣的重量級ORM實現,但還是需要一個輕量級的ORM,不處理關系,而只完成簡單層面的字段映射。在scala-sql 1.0中,是通過反射來進行的,這一樣會出現上述的兩個問題。

    在scala-sql 1.0應用了一段時間之后,我對這個庫越來越不滿意,開始在思考如何重構,建立一個更加簡單統一的類型模型,并且能夠支持用戶擴展類型體系。在這個重構的過程中,最終完成了目前的 scala-sql 2.0版本。

    在做這個重構之前,我一直在思考,數據庫支持的類型,諸如int、、date等有什么共性,是否可以用一個統一的類型T來 描述呢?哪些是這個類型的基本操作呢?

    基于此,我們需要這樣一個類型:

    trait T {
    def passIn(stmt: PreparedStatement, index: Int)
    def passOut(rs: ResultSet, index: Int): T
    def passOut(rs: ResultSet, name: String): T
    }

    問題是,我們不可能讓Int、等類型繼承這個接口,Scala的擴展方法也并不能很好的滿足這個場景。而且,即便是我們為Int、擴展了上述的方法,也并不會好用。因為的時候,我們更希望將的值賦給我們的目標變量,而不是調用目標變量的方法來改變它的值。

    這個時候,scala的 Bound 類型就是非常有意義了,我們定義了:

      trait JdbcValueAccessor[T] {
    def passIn(stmt: PreparedStatement, index: Int, value: T)
    def passOut(rs: ResultSet, index: Int): T
    def passOut(rs: ResultSet, name: String): T
    }

    并不是一個值類型,而是一個處理某種值類型T的能力接口,可以這么讀:[]是一個處理值類型的,它可以將傳遞給,也可以從中提取。在這理,[] 就是 的一個能力綁定,為對象賦予了作為的能力。任何時候,我們需要將作為一個處理的時候,我們也需要你提供這個能力對象,完成對應的操作。

    在這里,我們并不需要對、Int進行任何的改造,我們只是將數據庫訪問這種能力提取出來,作為一個,這種能力并不一定只是一個擴展方法,而可能是一個擴展方法集合。

      case class SQLWithArgs(sql: String, args: Seq[JdbcValue[_]]) { ... }

    case class JdbcValue[T: JdbcValueAccessor](value: T) {
    def accessor: JdbcValueAccessor[T] = implicitly[JdbcValueAccessor[T]]
    def passIn(stmt: PreparedStatement, index: Int) = accessor.passIn(stmt, index, value)
    }

    現在的sql插值參數,都是強類型的了,任何不符合的對象都不能作為插值來傳遞,而如果有了,自然,我們知道如何將這個值傳遞給了。

    那么問題來了,Int、并不是一個類型啊?怎么傳遞給sql""插值呢?每次都做一次轉換?如("Hello", sor)"這樣做的話,就非常的不友好了。這時,Scala的隱式轉換就非常實用了。

     ?object JdbcValue {
    ? ?implicit def wrap[T: JdbcValueAccessor](t: T): JdbcValue[T] = JdbcValue(t)
    ? ?implicit def wrap[T: JdbcValueAccessor](t: Option[T]): JdbcValue[Option[T]] = JdbcValue(t)(new JdbcValueAccessor_Option[T])
    ?}

    當我們需要將Int轉換為[Int]的時候,有一下幾個隱式轉換方法是可以派上用場的:

    def wrapT: : [T] = (t)這個寫法,和下面的寫法是完全一致的,是一個語法上的甜品: def wrap(t: T)( value: [T]): [T] = (t, [[T]])

    通過上面的定義,現在我們可以支持將任意的T傳遞給 sql 插值了。前提是我們為之定義了一個 [T] 的上下文綁定。在scala-sql中,我們在..sql這個對象中定義了幾乎所有的內置類型的綁定:

    而要新增一種類型,你只需要參考內置類型,定義一個擴展的 [T]即可,不需要對scala-s ql 庫做任何的修改。

    作為一個擴展的示例,你可以參考 框架中的一個擴展:mysql.:

      case class MySqlBitSet(val mask: Long) {

    def isSet(n: Int) = {
    assert(n >= 0 && n < 64)
    ((mask >> n) & 0x1L) == 1
    }

    override def toString: String = s"b'${mask.toBinaryString}'"

    }

    object MySqlBitSet {

    implicit object jdbcValueAccessor extends JdbcValueAccessor[MySqlBitSet] {

    override def passIn(stmt: PreparedStatement, index: Int, value: MySqlBitSet): Unit =
    stmt.setBytes(index, toByteArray(value.mask))

    override def passOut(rs: ResultSet, index: Int): MySqlBitSet = { ... }

    override def passOut(rs: ResultSet, name: String): MySqlBitSet = { ... }
    }
    }

    從中提取值

    上面的例子,都是介紹如何將 T 作為插值 傳給,而如果需要從 中讀取 T 時sql中數據類型,我們就會實用到 [T].了。

    def rows[T : ResultSetMapper](sql: SQLWithArgs): List[T]  = ... 

    在這里,我們要從sql執行的結果中提取 T 時,需要一個將 轉還為 T 的能力對象,我們稱之為:[T],這個對象是這樣定義的:

    trait ResultSetMapper[T] {
    def from(rs: ResultSet): T
    }

    實際上,有了這個能力對象,rows的實現是非常簡單的,這里就不贅述了。相反,如何為 T 準備一個 [T] 就要復雜的多。

    先看一個簡單的實現:

      implicit object ResultSetMapper_Int extends ResultSetMapper[Int] {
    override def from(rs: ResultSet): Int = rs.getInt(1)
    }

    這個實現是從 中映射一個 Int 值,這適合與 " count(*) from table"這樣的只有一個返回字段的場景。

    而對于多字段的結果集呢,以下是一個示例:

    case class User(name: String, age: Int, classRoom: Int = 1)

    implicit object ResultSetMapper_User extends ResultSetMapper[User] {
    override def from(rs: ResultSet): User = {
    val name = implictly[JdbcValueAccessor[String]].passOut(rs, "name")
    val age = implicitly[JdbcValueAccessor[Int]].passOut(rs, "age")
    val classRoom = implicitly[JdbcValueAccessor[Int]].passOut(rs, "classroom")
    User(name, age, classRoom)
    }
    }

    可以看出來,這個實際上也是基于 的,這樣,就可以支持User中實用任何的字段,只要這個字段有 [T] 的上下文綁定。

    當然,如果,對每一個Bean,都需要編寫這樣的一個 的話,這只能算是矛盾轉移,其代碼的工作量會非常之大,沒有什么實用之處。 不過,這樣的代碼純屬體力勞動,完全可以使用 Scala 的另外一個利器:Macro,讓編譯器自動生成。這也正是 scala-sql 2.0 中所提供的。對所有的Case Class,只要滿足:

    scala-sql 就可以自動的通過 macro 來生成其 。

    在這個意義上,[T] 這個上下文限定統一了 ,,scala-sql 2.0 也算是完美的、統一的支持了數據類型的擴展能力。

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有