请选择 进入手机版 | 继续访问电脑版
  • 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

andrewoma/kwery: Kwery is an SQL library for Kotlin

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

andrewoma/kwery

开源软件地址(OpenSource Url):

https://github.com/andrewoma/kwery

开源编程语言(OpenSource Language):

Kotlin 98.0%

开源软件介绍(OpenSource Introduction):

Kwery Overview

Kwery is an SQL library for Kotlin.

Kwery consists of three major modules (core, mapper and fetcher) that when combined provide similar functionality to a traditional ORM.

Kwery's manifesto:

  • Your domain model is sacred. No annotations or modifications to your model are required. Immutable models are fully supported.
  • No implicit fetching. Joins and graph fetches are explicit for predictable performance.
  • No magic. No proxies, interceptors, reflection or implicit saves. Explicit functions with sensible defaults control everything.
  • Useful logging. Logged statements are valid SQL with inline parameters for your dialect.

Build Status

Core

The core module is a fairly thin wrapper over JDBC, providing support for named parameters, logging and transactions.

class Actor(val firstName: String, val lastName: String, val lastUpdate: Timestamp)

val session = DefaultSession(connection, HsqlDialect()) // Standard JDBC connection

val sql = "select * from actor where first_name = :first_name"

val actors = session.select(sql, mapOf("first_name" to "Brad")) { row ->
    Actor(row.string("first_name"), row.string("last_name"), row.timestamp("last_update"))
}

Mapper

The mapper module module builds on core to provide typical DAO (Data Access Object) functionality.

As Kwery believes your domain model shouldn't be tainted by mapping annotations, it uses a Table object to define the mapping between rows and objects.

// We'll map to standard immutable classes, grouping name fields into a class
class Name(val firstName: String, val lastName: String)
class Actor(val id: Int, val name: Name, val lastUpdate: LocalDateTime)

// A table object defines the mapping between columns and models
// Conversions default to those defined in the configuration but may be overridden
object actorTable : Table<Actor, Int>("actor"), VersionedWithTimestamp {
    val ActorId    by col(Actor::id, id = true)
    val FirstName  by col(Name::firstName, Actor::name)
    val LastName   by col(Name::lastName, Actor::name)
    val LastUpdate by col(Actor::lastUpdate, version = true)

    override fun idColumns(id: Int) = setOf(ActorId of id)

    override fun create(value: Value<Actor>) = Actor(value of ActorId,
            Name(value of FirstName, value of LastName), value of LastUpdate)
}

// Given a table object, a generic dao is a one-liner, including standard CRUD operations
class ActorDao(session: Session) : AbstractDao<Actor, Int>(session, actorTable, Actor::id)

// Now we can use the DAO
val dao = ActorDao(session)
val inserted = dao.insert(Actor(1, Name("Kate", "Beckinsale"), LocalDateTime.now()))
val actors = dao.findAll()

See FilmDao.kt for a more comprehensive example.

Graph Fetcher

DAOs only fetch data from their linked table by default. To fetch an object graph, using a graph fetcher is the recommended method.

Given a graph specification, the fetcher attempts to fetch the graph in the minimum number of queries possible. It does this by batching together requests for the same type into a single query. As it fetches by ids, it also provides an ideal mechanism to insert a cache layer.

// Given the following domain model
data class Actor(val id: Int, val firstName: String, val lastName: String)

data class Language(val id: Int, val name: String)

data class Film(val id: Int, val language: Language, val actors: Set<Actor>,
                val title: String, val releaseYear: Int)

// Define types with functions describing how to fetch a batch by ids
val language = Type(Language::id, { languageDao.findByIds(it) })
val actor = Type(Actor::id, { actorDao.findByIds(it) })

// For types that reference other types describe how to apply fetched values
val film = Type(Film::id, { filmDao.findByIds(it) }, listOf(
        // 1 to 1
        Property(Film::language, language, { it.language.id }, { f, l -> f.copy(language = l) }),

        // 1 to many requires a function to describe how to fetch the related objects
        CollectionProperty(Film::actors, actor, Film::id,
                { f, a -> f.copy(actors = a.toSet()) },
                { actorDao.findByFilmIds(it) })
))

val fetcher = GraphFetcher(setOf(language, actor, film))

// Extension function to fetch the graph for any List using fetcher defined above
fun <T> Collection<T>.fetch(node: Node) = fetcher.fetch(this, Node(node))

// We can now efficiently fetch various graphs for any list of films
// The following fetches the films with actors and languages in 3 queries
val filmsWithAll = filmDao.findFilmsReleasedAfter(2010).fetch(Node.all)

// The graph specification can also be built using properties
val filmsWithActors = filmDao.findFilmsReleasedAfter(2010).fetch(Film::actors.node())

DAOs and graph fetching aim to cover 95% of a typical application data retrievals. For the remaining performance critical sections, use specialised methods on the DAOs using partial selects and joins as required.

Example

The example module demonstrates using Kwery to expose a simple model via RESTful web services via Dropwizard.

Transactional

The transactional module adds general purpose transaction interceptors. e.g.

@Transactional open class MyService(val session: Session) {
    open fun foo() {}
}

val session = ManagedThreadLocalSession(dataSource, HsqlDialect())
val service = transactionalFactory.fromClass(MyService(session), MyService::session)
service.foo() // Now calls to service automatically occur within a transaction

See the readme for more information.

Transactional for Jersey

The transactional-jersey module adds transaction annotations for Jersey.

Registering TransactionListener as a Jersey provider allows the transactional attribute to declare resource classes or methods as transactional.

Path("/films")
@Transactional class FilmResource : Resource {
    GET fun find(): List<Film> {
        ...
    }
}

See the readme for more information.

Status

Kwery is unstable. It's currently being developed for a side project, so features are added as required.

Kwery is available in Maven Central

0.17 Compatible with Kotlin 1.1.3-2.

  • Fix #14 - Incorrect parameter positions for collections
  • Lazily set Statement.poolable

0.16 Compatible with Kotlin 1.1.0.

0.15 Compatible with Kotlin 1.0.4.

  • Mapper: Support ThreadLocalSessions in Dao by creating implicit transactions (thanks @brianmadden)

0.14 Compatible with Kotlin 1.0.4.

0.13 Compatible with Kotlin 1.0.3.

0.12 Compatible with Kotlin 1.0.2.

  • Core: QueryBuilder
  • Core: Fix collection binding when not first parameter
  • Mapper: Add Dao.findByIdForUpdate

0.11 Compatible with Kotlin 1.0.2.

  • Core: Fix logging of statements with bound values containing $
  • Core: Add experimental sqlite support
  • Mapper: Support generated keys for MySQL in DAOs

0.10 Compatible with Kotlin 1.0.2.

0.9 Compatible with Kotlin 1.0.0.

  • Mapper: add Table.optionalCol to construct optional types via paths

0.8 Compatible with Kotlin 1.0.0-rc-1036.

  • Mapper: support PreUpdate and PreInsert events (thanks @davemaple)
  • Remove tomcat pool module as Postgres drivers now support prepared statement caching

0.7 Compatible with Kotlin 1.0.0-beta-3595.

  • Add MySQL dialect

0.6 Compatible with Kotlin 1.0.0-beta-1038.

0.5 Compatible with Kotlin M14.

0.4 Compatible with Kotlin M13:

  • Provide a consistent set of defaults and converters for mapping standard types
  • Add defaults and converters for OffsetDateTime and ZonedDateTime

0.3 Compatible with Kotlin M13:

  • Improved docs
  • Simplified transaction listeners
  • Made transactions re-entrant
  • Renamed ThreadLocalSession to ManagedThreadLocalSession and introduced a new ThreadLocalSession for use without interceptors and annotations.

0.2 Compatible with Kotlin M12, adding transactional interceptors.

0.1 Compatible with Kotlin M11.

Building

git clone https://github.com/andrewoma/kwery.git
cd kwery
./gradlew check install

Note: The tests require a local postgres and mysql database named kwery. e.g. On OS X

brew install postgres
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
createdb kwery

brew install mysql
ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
mysql -uroot -e 'create database kwery'
mysql -uroot -e "create user 'kwery'@'localhost' identified by 'kwery'"
mysql -uroot -e "grant all privileges on *.* to 'kwery'@'localhost'"

To open in IntelliJ, just open the build.gradle file and IntelliJ will generate the project automatically.

Roadmap

Core:

  • Support direct execution (currently everything is via a PreparedStatement)
  • Add more robust named parameter replacement (ignore patterns inside comments, strings, etc)

DAO:

  • Documentation

Fetcher:

  • Documentation
  • General review - code seems overly complicated for what it does

Modules:

  • Dropwizard metrics integration
  • Generator - Generate initial Table and domain objects from reading JDBC metadata

Robustness/Performance:

  • Soak test - check for leaking connections/resources over extended usage
  • Profile array based in clauses on large tables

Misc:

  • Better IDE support for highlighting inline SQL. Vote for KT-6610

License

This project is licensed under a MIT license.




鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap