Kwery is an SQL library for Kotlin

Related tags

O/R Mapping kwery
Overview

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.

Comments
  • SQLException when using auto_increment in MySQL 5.7

    SQLException when using auto_increment in MySQL 5.7

    This is my first object that utilizes auto increment in MySQL. I've set the DAO to utilize generated keys:

    /**
     * @author [David Maple](mailto:[email protected])
     */
    data class Topic(
            val name: String,
            val dateCreated: LocalDateTime = LocalDateTime.now(),
            val dateModified: LocalDateTime = LocalDateTime.now(),
            val version: Long = 1,
            val id: Int = 0
    )
    
    /**
     * @author [David Maple](mailto:[email protected])
     */
    object topicTable : Table<Topic, Int>("topic", TableConfiguration(
            standardDefaults + timeDefaults,
            standardConverters + timeConverters,
            camelToLowerUnderscore
    )), VersionedWithLong {
        val Id by topicTable.col(Topic::id, id = true)
        val Name by topicTable.col(Topic::name)
        val DateCreated by topicTable.col(Topic::dateCreated)
        val DateModified by topicTable.col(Topic::dateModified)
        val Version by topicTable.col(Topic::version, version = true)
    
        override fun idColumns(id: Int) = setOf(Id of id)
    
        override fun create(value: Value<Topic>) = Topic(
                id = value of Id,
                dateCreated = value of DateCreated,
                dateModified = value of DateModified,
                name = value of Name,
                version = value of Version
        )
    }
    
    /**
     * @author [David Maple](mailto:[email protected])
     */
    class TopicDao(session: Session) : AbstractDao<Topic, Int>(
            session,
            topicTable,
            { it.id },
            "bigint(20) unsigned",
            IdStrategy.Generated,
            0
    ) {
        init {
            addListener(object : Listener {
                override fun onEvent(session: Session, event: Event) {
                    if (event is TransformingEvent && event.new is Topic) {
                        val entity = event.new as Topic
                        if (event is PreInsertEvent) {
                            event.transformed = entity.copy(
                                    dateCreated = LocalDateTime.now(),
                                    dateModified = LocalDateTime.now()
                            )
                        }
                        if (event is PreUpdateEvent && event.new is Topic) {
                            event.transformed = entity.copy(dateModified = LocalDateTime.now())
                        }
                    }
                }
            })
        }
    }
    

    SQL looks like this:

    CREATE TABLE `topic` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(64) DEFAULT NULL,
      `date_created` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
      `date_modified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
      `version` bigint(20) unsigned NOT NULL DEFAULT '1',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=latin1;
    

    As you can see I do have an id column, yet when performing an INSERT via the DAO I get an unexpected SQLException. Logging shows an expected INSERT syntax:

    insert into topic(date_created, date_modified, name, version) 
    values ('2016-05-22 11:07:59.876', '2016-05-22 11:07:59.876', 'Love/Romance', 1);
    

    The stacktrace shows the following:

    Failed to execute TopicDao.insert in 1.921 ms (761521.415 ms)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:885)
    Reason: Column 'id' not found.. TXN: 3
    
        at com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1076)
        at com.mysql.jdbc.ResultSetImpl.getInt(ResultSetImpl.java:2580)
        at com.github.andrewoma.kwery.core.Row.int(Row.kt:47)
        at com.github.andrewoma.kwery.mapper.intConverter$1.invoke(Converters.kt:80)
        at com.github.andrewoma.kwery.mapper.intConverter$1.invoke(Converters.kt:80)
        at com.github.andrewoma.kwery.mapper.Table$rowMapper$1$1.of(Table.kt:300)
        at com.quote.topic.topicTable.create(TopicDao.kt:25)
    

    Any idea what I need to change configuration wise to prevent this?

    opened by davemaple 8
  • Bug when saving a field: String containing '$'

    Bug when saving a field: String containing '$'

    Looks like there's an insidious bug where Kotlin is interpreting a field containing a String with a dollar sign ($) as an actual token for replacement in bind parameters:

    Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 5
        at java.util.regex.Matcher.start(Matcher.java:375)
        at java.util.regex.Matcher.appendReplacement(Matcher.java:880)
        at com.github.andrewoma.kwery.core.DefaultSession.bindParameters(DefaultSession.kt:219)
    

    Filling a String with any text containing '$' and performing an insert/update should recreate the issue.

    A quick study seems to indicate that we could fix this in MysqlDialect::bind().

    opened by davemaple 5
  • Incorrect parameter preparation with lists

    Incorrect parameter preparation with lists

    When using a list parameter with any parameters following it, it gets translated into an incorrect parameter list that throws an exception under PostgreSQL. For example: session.update( "UPDATE sometable SET array_column = ARRAY [:arrayValues] WHERE id = :id", mapOf("arrayValues" to listOf(1), "id" to "2") ) This generates a length 9 parameter list. The first parameter will be "1", the second parameter will be "2", the 9th parameter will be null and thus trigger PostgreSQL to throw org.postgresql.util.PSQLException: No value specified for parameter 9. at org.postgresql.core.v3.SimpleParameterList.checkAllParametersSet(SimpleParameterList.java:257). (Also, why are parameter slots for collections rounded up to a power of 2?)

    opened by ScottPeterJohnson 4
  • Pagination with offset, limit queries?

    Pagination with offset, limit queries?

    Hi Andrew,

    Do you ever use pagination in your queries using offset, limit? I try to avoid large offsets but I continue to find examples where it's helpful. Currently I'm implementing it within a DAO with something like this:

    val name = "findPaginated"
    val sql = sql(name) {
        """
            SELECT
                $columns
            FROM
                ${table.name}
            LIMIT :offset, :limit
        """
    }
    return session.select(
            sql,
            mapOf("offset" to offset, "limit" to limit),
            options(name),
            table.rowMapper()
    );
    

    I'm wondering if this would be something worthy of adding to AbstractDao. Your thoughts?

    opened by davemaple 3
  • org.h2.jdbc.JdbcSQLException: Column

    org.h2.jdbc.JdbcSQLException: Column "id" not found

    I'm always getting this exception when passing IdStrategy.Generated to insert.

    Exception in thread "main" org.h2.jdbc.JdbcSQLException: Column "id" not found [42122-196]
    	at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    	at org.h2.message.DbException.get(DbException.java:179)
    	at org.h2.message.DbException.get(DbException.java:155)
    	at org.h2.jdbc.JdbcResultSet.getColumnIndex(JdbcResultSet.java:3180)
    	at org.h2.jdbc.JdbcResultSet.get(JdbcResultSet.java:3246)
    	at org.h2.jdbc.JdbcResultSet.getInt(JdbcResultSet.java:350)
    	at com.github.andrewoma.kwery.core.Row.int(Row.kt:47)
    	at com.github.andrewoma.kwery.mapper.intConverter$1.invoke(Converters.kt:80)
    	at com.github.andrewoma.kwery.mapper.intConverter$1.invoke(Converters.kt:80)
    	at com.github.andrewoma.kwery.mapper.Table$rowMapper$1$1.of(Table.kt:206)
    	at com.company.thing.ThingTable.create(EventServer.kt:28)
    	at com.company.thing.ThingTable.create(EventServer.kt:23)
    	at com.github.andrewoma.kwery.mapper.Table$rowMapper$1.invoke(Table.kt:204)
    	at com.github.andrewoma.kwery.mapper.Table$rowMapper$1.invoke(Table.kt:39)
    	at com.github.andrewoma.kwery.mapper.AbstractDao$insert$1$1.invoke(AbstractDao.kt:289)
    	at com.github.andrewoma.kwery.mapper.AbstractDao$insert$1$1.invoke(AbstractDao.kt:32)
    	at com.github.andrewoma.kwery.core.DefaultSession$insert$1.invoke(DefaultSession.kt:142)
    	at com.github.andrewoma.kwery.core.DefaultSession$insert$1.invoke(DefaultSession.kt:46)
    	at com.github.andrewoma.kwery.core.DefaultSession.withPreparedStatement(DefaultSession.kt:252)
    	at com.github.andrewoma.kwery.core.DefaultSession.insert(DefaultSession.kt:135)
    	at com.github.andrewoma.kwery.mapper.AbstractDao$insert$1.invoke(AbstractDao.kt:288)
    	at com.github.andrewoma.kwery.mapper.AbstractDao.withTransaction(AbstractDao.kt:100)
    	at com.github.andrewoma.kwery.mapper.AbstractDao.insert(AbstractDao.kt:277)
    	at com.company.thing.main(Main.kt:48)
    
    Process finished with exit code 1
    
    package com.company.thing
    
    import com.github.andrewoma.kwery.core.DefaultSession
    import com.github.andrewoma.kwery.core.Session
    import com.github.andrewoma.kwery.core.dialect.HsqlDialect
    import com.github.andrewoma.kwery.mapper.AbstractDao
    import com.github.andrewoma.kwery.mapper.IdStrategy
    import com.github.andrewoma.kwery.mapper.Table
    import com.github.andrewoma.kwery.mapper.Value
    import java.sql.DriverManager
    
    class Thing(
            var id: Int,
            var name: String
    )
    
    object ThingTable: Table<Thing, Int>("thing") {
        val Id   by col(Thing::id, id = true)
        val Name by col(Thing::name)
    
        override fun create(value: Value<Thing>): Thing {
            return Thing(value of Id, value of Name)
        }
        override fun idColumns(id: Int) = setOf(Id of id)
    }
    
    class ThingDao(session: Session): AbstractDao<Thing, Int>(session, ThingTable, Thing::id)
    
    fun main(args: Array<String>) {
    
        val conn = DriverManager.getConnection("jdbc:h2:mem:test")
        val session = DefaultSession(conn, HsqlDialect())
    
        session.update("""
            create table thing (
                id   integer auto_increment not null primary key,
                name character varying(255) not null,
            )
        """)
    
        val dao = ThingDao(session)
        dao.insert(Thing(0, "foo"), IdStrategy.Generated)
    }
    
    opened by icholy 2
  • Basic Join Support

    Basic Join Support

    Hi there,

    I added some basic support for Joins.

    I know that they can be added with the select() fun already, but for my own use cases I would prefer something slightly more elaborate. Have a look and tell me if you are interested in adding this kind of functionality to kwery. Otherwise I will probably start my own project consisting solely of the QueryBuilder.

    Here is an example of the syntax:

    query {
        select("select p.PERS_ID, p.PERS_NAME, pp.POSITION from PERSON p")
    
        joinGroup {
            innerJoin("PERS_POS") { on("$it.PERS_ID = p.PERS_ID") }
            leftJoin("LOCATION") { on("$it.LOC_ID = p.LOC_ID") } and { "$it.LOC_ID IS NULL" }
        }
    
    }
    

    Which yields the following sql (formatted):

    select p.PERS_ID, p.PERS_NAME, pp.POSITION from PERSON p
     inner join PERS_POS pp on pp.PERS_ID = p.PERS_ID 
      left join LOCATION l on l.LOC_ID = p.LOC_ID and l.LOC_ID IS NULL
    

    The Join automatically derives a handler for the joined table. For example if you join the table Actor, it will output join Actor a. This derived handler is then accessible in the on() and and() functions as it.

    opened by AndreasVolkmann 2
  • Columns can be added after table instance construction which makes the `idColumns` and friends return possibly wrong collections

    Columns can be added after table instance construction which makes the `idColumns` and friends return possibly wrong collections

        @Test
        fun `test adding more columns after table instance construction`() {
            val usersTable = UsersTable()
            val dataColumnsBeforeModification = usersTable.dataColumns
    
            @Suppress("UNCHECKED_CAST")
            val emailColumn = Column(
                    User::email, null,
                    standardConverters[String::class.java] as Converter<String?>,
                    "email", false, false, true, false
            )
    
            usersTable.addColumn(emailColumn)
            val dataColumnsAfterModification = usersTable.dataColumns
    
            assert(dataColumnsBeforeModification != dataColumnsAfterModification)
        }
    
    
        private data class User(val id: String, val name: String, val age: Int) {
            val email: String? = null
        }
    
    
        private class UsersTable : Table<User, UUID>("users") {
    
            val id by col(User::id, id = true)
            val userName by col(User::name)
            val age by col(User::age)
    
            override fun create(value: Value<User>): User {
                return User(value of id, value of userName, value of age)
            }
    
            override fun idColumns(id: UUID): Set<Pair<Column<User, *>, *>> {
                return setOf(this.id to id)
            }
        }
    
    opened by jayrave 2
  • Needs support for CallableStatements

    Needs support for CallableStatements

    I noticed that I needed to call an Oracle Stored Procedure (would be nice to have a dialect for Oracle) and this framework had no support for doing it. I had to resort to obtaining the native connection and writing java style code to make it happen.

    Would be really nice if you added support for CallableStatements so that they work roughly the same way as your select/update statements do. I also didn't see it on the roadmap either.

    opened by sepatel 2
  • No magic. No proxies, interceptors or ....

    No magic. No proxies, interceptors or ....

    Hi you mentioned the kwery don't use Proxies or reflection ... but you use Proxies in transactional project. There isn't a better solution to implement this feature ?

    Thanks.

    opened by mohsenk 2
  • Question about modeling 1-N relationship with mapper

    Question about modeling 1-N relationship with mapper

    I wanted to model simple domain with 1-N relationship. I have this query working in core:

    select * from scores s join users u where s.created_at > '${date.toString("yyyy-MM-dd")}' order by s.score desc limit 20

    But don't know how to model it with mapper. With Exposed it's as simple as this:

    object Users : Table() {
        val id    = integer("id").primaryKey()
        val login = text("login")
    }
    
    object Scores : Table() {
        val score       = integer("score")
        val createdAt   = date("created_at")
        val userId      = integer("user_id") references Users.id
    }
    
       override fun findAll(): List<Score> {
            return (Scores innerJoin Users).select { Scores.createdAt.greater(DateTime.now().minusDays(2)) }
                    .orderBy(Scores.score, false)
                    .limit(20)
                    .map { r -> Score(r[Scores.score], r[Users.login]) }
    }
    

    I got something like this but, obviously, login is set to empty string:

    class User(val id: Int, val login: String = "")
    
    class PersistentScore(val id: Int, val user: User, score: Int) : Score(score, user.login)
    
    object userConverter : SimpleConverter<User>(
            { row, c -> User(row.int(c)) },
            User::id
    )
    
    val tableConfig = TableConfiguration(
            defaults = standardDefaults + reifiedValue(User(0)),
            converters = standardConverters + reifiedConverter(userConverter)
    )
    
    object userTable: Table<User, Int>("users", tableConfig) {
        val Id          by col(User::id, id = true)
        val LoginVal    by col(User::login)
    
        override fun create(value: Value<User>): User =
            User(value of Id, value of LoginVal)
    
        override fun idColumns(id: Int) = setOf(Id of id)
    }
    
    object scoreTable : Table<PersistentScore, Int>("scores", tableConfig) {
        val Id          by col(PersistentScore::id, id = true)
        val ScoreVal    by col(PersistentScore::score, name = "score")
        val UserId      by col(PersistentScore::user)
    
        override fun create(value: Value<PersistentScore>): PersistentScore =
                PersistentScore(value of Id, value of UserId, value of ScoreVal)
    
        override fun idColumns(id: Int) = setOf(Id of id)
    }
       fun findScores(): List<PersistentScore> {
            val name = "findScores"
            val sql = sql(name) { "select * from ${table.name} where created_at > :date order by score desc limit 10" }
            return session.select(sql, mapOf("date" to DateTime.now().minusDays(2).toString("yyyy-MM-dd")), options(name), table.rowMapper())
        }
    

    Any help?

    opened by pjagielski 2
  • Contended Table initialization?

    Contended Table initialization?

    While Table instance is not initialized, columns are getting added. If any other thread sees a Table instance after that and try to initialize it, writes into columns made by other thread may not be visible.

    Unfortunately, I don't know any easy solutions to address the issue.

    opened by Miha-x64 1
  • 'GENERATED_KEY' stuck

    'GENERATED_KEY' stuck

    Hi, Andrew.

    Thank you for your awesome job!

    I've been stuck a little with generated keys using core "insert" function with MySQL, because 'GENERATED_KEY' should be specified instead of key column name. I.e.

    val (count, key) = session.insert("insert blablabla", mapOf("field" to "value" ...)) { it.long("GENERATED_KEY") }

    Can you add a small remark about this into documentation? Thank you. Sorry for my English.

    opened by gigacoderr 0
  • Release Version 0.18

    Release Version 0.18

    Are there any plans for a 0.18 release? There are a couple of bugfixes on master missing from version 0.17.. It would have been nice to see those fixes released soon 👍

    opened by abrabah 0
  • How to escape a colon ':' character in a core module query?

    How to escape a colon ':' character in a core module query?

    Is there any possibility to use a colon ':' character in a query? It seems to be always used as a query parameter. For instance there is no way to use a string 'HH:mm' or PostgreSQL casting '3'::int.

    opened by rjaros 1
Owner
Andrew O'Malley
Andrew O'Malley
requery - modern SQL based query & persistence for Java / Kotlin / Android

A light but powerful object mapping and SQL generator for Java/Kotlin/Android with RxJava and Java 8 support. Easily map to or create databases, perfo

requery 3.1k Dec 29, 2022
Upsert DSL extension for Exposed, Kotlin SQL framework

Exposed Upsert Upsert DSL extension for Exposed, Kotlin SQL framework. Project bases on various solutions provided by community in the official "Expos

Dzikoysk 23 Oct 6, 2022
Upsert DSL extension for Exposed, Kotlin SQL framework

Exposed Upsert Upsert DSL extension for Exposed, Kotlin SQL framework. Project bases on various solutions provided by community in the official "Expos

Reposilite Playground 23 Oct 6, 2022
sql-delight example, a plugin by Square which is pure kotlin and it is useful in kmm

Sql-Delight-Example01 Developed by Mahdi Razzaghi Ghaleh first example of sql-delight What is SqlDelight? Kotlin Multiplatform is one of the most inte

rq_mehdi 0 Jan 24, 2022
A lightweight wrapper around SQLiteOpenHelper which introduces reactive stream semantics to SQL operations.

SQL Brite A lightweight wrapper around SupportSQLiteOpenHelper and ContentResolver which introduces reactive stream semantics to queries. Deprecated T

Square 4.6k Jan 5, 2023
A simple NoSQL client for Android. Meant as a document store using key/value pairs and some rudimentary querying. Useful for avoiding the hassle of SQL code.

SimpleNoSQL A simple NoSQL client for Android. If you ever wanted to just save some data but didn't really want to worry about where it was going to b

Colin Miller 389 Sep 25, 2022
A Java/Kotlin library for Android platform, to manage bean's persistence in SQLite, SharedPreferences, JSON, XML, Properties, Yaml, CBOR.

Thanks to JetBrains for support Kripton Persistence Library project! Kripton Persistence Library Kripton is a java library, for Android platform, that

xcesco 117 Nov 11, 2022
A blazing fast, powerful, and very simple ORM android database library that writes database code for you.

README DBFlow is fast, efficient, and feature-rich Kotlin database library built on SQLite for Android. DBFlow utilizes annotation processing to gener

Andrew Grosner 4.9k Dec 30, 2022
A blazing fast, powerful, and very simple ORM android database library that writes database code for you.

README DBFlow is fast, efficient, and feature-rich Kotlin database library built on SQLite for Android. DBFlow utilizes annotation processing to gener

Andrew Grosner 4.9k Dec 30, 2022
An Android library that makes developers use SQLite database extremely easy.

LitePal for Android 中文文档 LitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most o

Lin Guo 7.9k Dec 31, 2022
SquiDB is a SQLite database library for Android and iOS

Most ongoing development is currently taking place on the dev_4.0 branch. Click here to see the latest changes and try out the 4.0 beta. Introducing S

Yahoo 1.3k Dec 26, 2022
Sprinkles is a boiler-plate-reduction-library for dealing with databases in android applications

Sprinkles Sprinkles is a boiler-plate-reduction-library for dealing with databases in android applications. Some would call it a kind of ORM but I don

Emil Sjölander 781 Nov 28, 2022
LiteGo is a Java-based asynchronous concurrency library. It has a smart executor, which can be freely set the maximum number of concurrent at same time , and the number of threads in waiting queue. It can also set waiting policies and overload strategies.

LiteGo:「迷你」的Android异步并发类库 LiteGo是一款基于Java语言的「异步并发类库」,它的核心是一枚「迷你」并发器,它可以自由地设置同一时段的最大「并发」数量,等待「排队」线程数量,还可以设置「排队策略」和「超载策略」。 LiteGo可以直接投入Runnable、Callable

马天宇 189 Nov 10, 2022
An Android library that makes developers use SQLite database extremely easy.

LitePal for Android 中文文档 LitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most o

Lin Guo 7.9k Jan 4, 2023
Samples demonstrating the usage of Realm-Kotlin SDK

Realm-Kotlin Samples This repository contains a set of projects to help you learn about using Realm-Kotlin SDK Each sample demonstrates different use

Realm 52 Dec 31, 2022
Collection of Kotlin APIs/tools to make using Realm Mobile database easier

Compass Kotlin API and tools to make working with Realm easier Components Compass is designed to make working with Realm easier through collection of

Arunkumar 16 Oct 4, 2022
RecordMe - Record your voice application with kotlin

RecordMe A simple voice recording app. Made Using : Kotlin, Navigation Component

Steve Waweru 2 Apr 28, 2022
Starter code for Android Kotlin Fundamentals Codelab 6.1 Room

TrackMySleepQuality - Starter Code Starter code for Android Kotlin Fundamentals Codelab 6.1 Room Introduction TrackMySleepQuality is an app for record

YamanAswal 0 Jan 15, 2022
BookSearchApp - Book Search App With Kotlin

BookSearchApp IT Book Search App Search IT books with keyword and view informati

null 1 Feb 7, 2022