Giter Site home page Giter Site logo

exposed's Introduction

Exposed


JetBrains team project Kotlinlang Slack Channel TC Build status Maven Central GitHub License

Welcome to Exposed, an ORM framework for Kotlin.

Exposed is a lightweight SQL library on top of JDBC driver for Kotlin language. Exposed has two flavors of database access: typesafe SQL wrapping DSL and lightweight Data Access Objects (DAO).

With Exposed, you have two ways for database access: wrapping DSL and a lightweight DAO. Our official mascot is the cuttlefish, which is well-known for its outstanding mimicry ability that enables it to blend seamlessly into any environment. Similar to our mascot, Exposed can be used to mimic a variety of database engines and help you build applications without dependencies on any specific database engine and switch between them with very little or no changes.

Supported Databases

  • H2 (versions 2.x; 1.x version is deprecated and will be removed in future releases)
  • MariaDB
  • MySQL
  • Oracle
  • Postgres (Also, PostgreSQL using the pgjdbc-ng JDBC driver)
  • SQL Server
  • SQLite

Dependencies

Maven Central configuration

Releases of Exposed are available in the Maven Central repository. You can declare this repository in your build script as follows:

Gradle Groovy and Kotlin DSL

Warning: You might need to set your Kotlin JVM target to 8, and when using Spring to 17, in order for this to work properly:

repositories {
    // Versions after 0.30.1
    // Versions before 0.30.1 is unavailable for now
    mavenCentral()
}

Maven

The Maven Central repository is enabled by default for Maven users.

Exposed modules

Exposed consists of the following modules:

  • exposed-core - base module, which contains both DSL api along with mapping
  • exposed-crypt - provides additional column types to store encrypted data in DB and encode/decode it on client-side
  • exposed-dao - DAO api
  • exposed-java-time - date-time extensions based on Java8 Time API
  • exposed-jdbc - transport level implementation based on Java JDBC API
  • exposed-jodatime - date-time extensions based on JodaTime library
  • exposed-json - JSON and JSONB data type extensions
  • exposed-kotlin-datetime - date-time extensions based on kotlinx-datetime
  • exposed-money - extensions to support MonetaryAmount from "javax.money:money-api"
  • exposed-spring-boot-starter - a starter for Spring Boot to utilize Exposed as the ORM instead of Hibernate
<dependencies>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-core</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-crypt</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-dao</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-java-time</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-jdbc</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-jodatime</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-json</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-kotlin-datetime</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-money</artifactId>
        <version>0.49.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-spring-boot-starter</artifactId>
        <version>0.49.0</version>
    </dependency>
</dependencies>

Gradle Groovy

dependencies {
    implementation 'org.jetbrains.exposed:exposed-core:0.49.0'
    implementation 'org.jetbrains.exposed:exposed-crypt:0.49.0'
    implementation 'org.jetbrains.exposed:exposed-dao:0.49.0'
    implementation 'org.jetbrains.exposed:exposed-jdbc:0.49.0'
    
    implementation 'org.jetbrains.exposed:exposed-jodatime:0.49.0'
    // or
    implementation 'org.jetbrains.exposed:exposed-java-time:0.49.0'
    // or
    implementation 'org.jetbrains.exposed:exposed-kotlin-datetime:0.49.0'
    
    implementation 'org.jetbrains.exposed:exposed-json:0.49.0'
    implementation 'org.jetbrains.exposed:exposed-money:0.49.0'
    implementation 'org.jetbrains.exposed:exposed-spring-boot-starter:0.49.0'
}

Gradle Kotlin DSL

In build.gradle.kts:

val exposedVersion: String by project
dependencies {
    implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-crypt:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
    
    implementation("org.jetbrains.exposed:exposed-jodatime:$exposedVersion")
    // or
    implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
    // or
    implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion")
    
    implementation("org.jetbrains.exposed:exposed-json:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-money:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-spring-boot-starter:$exposedVersion")
}

and in gradle.properties

exposedVersion=0.49.0

Samples

Check out the samples for a quick start.

Links

Currently, Exposed is available for maven/gradle builds. Check the Maven Central and read Getting Started to get an insight on setting up Exposed.

For more information visit the links below:

Filing issues

Please note that we are moving away from GitHub Issues for reporting of bugs and features. Please log any new requests on YouTrack. You must be logged in to view and log issues, otherwise you will be met with a 404.

Community

Do you have questions? Feel free to request an invitation for the kotlinlang slack and join the project conversation at our #exposed channel.

Pull requests

We actively welcome your pull requests. However, linking your work to an existing issue is preferred.

  • Fork the repo and create your branch from main.
  • Name your branch something that is descriptive to the work you are doing. i.e. adds-new-thing.
  • If you've added code that should be tested, add tests and ensure the test suite passes.
  • Make sure you address any lint warnings.
  • If you make the existing code better, please let us know in your PR description.

Examples

SQL DSL

import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
import org.jetbrains.exposed.sql.transactions.transaction

object Users : Table() {
    val id: Column<String> = varchar("id", 10)
    val name: Column<String> = varchar("name", length = 50)
    val cityId: Column<Int?> = (integer("city_id") references Cities.id).nullable()

    override val primaryKey = PrimaryKey(id, name = "PK_User_ID") // name is optional here
}

object Cities : Table() {
    val id: Column<Int> = integer("id").autoIncrement()
    val name: Column<String> = varchar("name", 50)

    override val primaryKey = PrimaryKey(id, name = "PK_Cities_ID")
}

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "root", password = "")

    transaction {
        addLogger(StdOutSqlLogger)

        SchemaUtils.create(Cities, Users)

        val saintPetersburgId = Cities.insert {
            it[name] = "St. Petersburg"
        } get Cities.id

        val munichId = Cities.insert {
            it[name] = "Munich"
        } get Cities.id

        val pragueId = Cities.insert {
            it.update(name, stringLiteral("   Prague   ").trim().substring(1, 2))
        }[Cities.id]

        val pragueName = Cities.selectAll().where { Cities.id eq pragueId }.single()[Cities.name]
        println("pragueName = $pragueName")

        Users.insert {
            it[id] = "andrey"
            it[name] = "Andrey"
            it[Users.cityId] = saintPetersburgId
        }

        Users.insert {
            it[id] = "sergey"
            it[name] = "Sergey"
            it[Users.cityId] = munichId
        }

        Users.insert {
            it[id] = "eugene"
            it[name] = "Eugene"
            it[Users.cityId] = munichId
        }

        Users.insert {
            it[id] = "alex"
            it[name] = "Alex"
            it[Users.cityId] = null
        }

        Users.insert {
            it[id] = "smth"
            it[name] = "Something"
            it[Users.cityId] = null
        }

        Users.update({ Users.id eq "alex" }) {
            it[name] = "Alexey"
        }

        Users.deleteWhere{ Users.name like "%thing" }

        println("All cities:")

        for (city in Cities.selectAll()) {
            println("${city[Cities.id]}: ${city[Cities.name]}")
        }

        println("Manual join:")
        
        (Users innerJoin Cities)
            .select(Users.name, Cities.name)
            .where {
                (Users.id.eq("andrey") or Users.name.eq("Sergey")) and
                    Users.id.eq("sergey") and Users.cityId.eq(Cities.id)
            }.forEach { 
                println("${it[Users.name]} lives in ${it[Cities.name]}") 
            }

        println("Join with foreign key:")

        (Users innerJoin Cities)
            .select(Users.name, Users.cityId, Cities.name)
            .where { Cities.name.eq("St. Petersburg") or Users.cityId.isNull() }
            .forEach { 
                if (it[Users.cityId] != null) { 
                    println("${it[Users.name]} lives in ${it[Cities.name]}") 
                } 
                else { 
                    println("${it[Users.name]} lives nowhere") 
                } 
            }

        println("Functions and group by:")

        ((Cities innerJoin Users)
            .select(Cities.name, Users.id.count())
            .groupBy(Cities.name)
            ).forEach {
                val cityName = it[Cities.name]
                val userCount = it[Users.id.count()]

                if (userCount > 0) {
                    println("$userCount user(s) live(s) in $cityName")
                } else {
                    println("Nobody lives in $cityName")
                }
            }

        SchemaUtils.drop(Users, Cities)
    }
}

Generated SQL:

    SQL: CREATE TABLE IF NOT EXISTS Cities (id INT AUTO_INCREMENT, name VARCHAR(50) NOT NULL, CONSTRAINT PK_Cities_ID PRIMARY KEY (id))
    SQL: CREATE TABLE IF NOT EXISTS Users (id VARCHAR(10), name VARCHAR(50) NOT NULL, city_id INT NULL, CONSTRAINT PK_User_ID PRIMARY KEY (id), CONSTRAINT FK_Users_city_id__ID FOREIGN KEY (city_id) REFERENCES Cities(id) ON DELETE RESTRICT ON UPDATE RESTRICT)
    SQL: INSERT INTO Cities (name) VALUES ('St. Petersburg')
    SQL: INSERT INTO Cities (name) VALUES ('Munich')
    SQL: INSERT INTO Cities (name) VALUES (SUBSTRING(TRIM('   Prague   '), 1, 2))
    SQL: SELECT Cities.id, Cities.name FROM Cities WHERE Cities.id = 3
    pragueName = Pr
    SQL: INSERT INTO Users (id, name, city_id) VALUES ('andrey', 'Andrey', 1)
    SQL: INSERT INTO Users (id, name, city_id) VALUES ('sergey', 'Sergey', 2)
    SQL: INSERT INTO Users (id, name, city_id) VALUES ('eugene', 'Eugene', 2)
    SQL: INSERT INTO Users (id, name, city_id) VALUES ('alex', 'Alex', NULL)
    SQL: INSERT INTO Users (id, name, city_id) VALUES ('smth', 'Something', NULL)
    SQL: UPDATE Users SET name='Alexey' WHERE Users.id = 'alex'
    SQL: DELETE FROM Users WHERE Users.name LIKE '%thing'
    All cities:
    SQL: SELECT Cities.id, Cities.name FROM Cities
    1: St. Petersburg
    2: Munich
    3: Pr
    Manual join:
    SQL: SELECT Users.name, Cities.name FROM Users INNER JOIN Cities ON Cities.id = Users.city_id WHERE ((Users.id = 'andrey') or (Users.name = 'Sergey')) and (Users.id = 'sergey') and (Users.city_id = Cities.id)
    Sergey lives in Munich
    Join with foreign key:
    SQL: SELECT Users.name, Users.city_id, Cities.name FROM Users INNER JOIN Cities ON Cities.id = Users.city_id WHERE (Cities.name = 'St. Petersburg') or (Users.city_id IS NULL)
    Andrey lives in St. Petersburg
    Functions and group by:
    SQL: SELECT Cities.name, COUNT(Users.id) FROM Cities INNER JOIN Users ON Cities.id = Users.city_id GROUP BY Cities.name
    1 user(s) live(s) in St. Petersburg
    2 user(s) live(s) in Munich
    SQL: DROP TABLE IF EXISTS Users
    SQL: DROP TABLE IF EXISTS Cities

DAO

import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

object Users : IntIdTable() {
    val name = varchar("name", 50).index()
    val city = reference("city", Cities)
    val age = integer("age")
}

object Cities: IntIdTable() {
    val name = varchar("name", 50)
}

class User(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<User>(Users)

    var name by Users.name
    var city by City referencedOn Users.city
    var age by Users.age
}

class City(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<City>(Cities)

    var name by Cities.name
    val users by User referrersOn Users.city
}

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "root", password = "")

    transaction {
        addLogger(StdOutSqlLogger)

        SchemaUtils.create(Cities, Users)

        val stPete = City.new {
            name = "St. Petersburg"
        }

        val munich = City.new {
            name = "Munich"
        }

        User.new {
            name = "a"
            city = stPete
            age = 5
        }

        User.new {
            name = "b"
            city = stPete
            age = 27
        }

        User.new {
            name = "c"
            city = munich
            age = 42
        }

        println("Cities: ${City.all().joinToString { it.name }}")
        println("Users in ${stPete.name}: ${stPete.users.joinToString { it.name }}")
        println("Adults: ${User.find { Users.age greaterEq 18 }.joinToString { it.name }}")
    }
}

Generated SQL:

    SQL: CREATE TABLE IF NOT EXISTS Cities (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL)
    SQL: CREATE TABLE IF NOT EXISTS Users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, city INT NOT NULL, age INT NOT NULL, CONSTRAINT FK_Users_city__ID FOREIGN KEY (city) REFERENCES Cities(id) ON DELETE RESTRICT ON UPDATE RESTRICT)
    SQL: CREATE INDEX Users_name ON Users (name)
    SQL: INSERT INTO Cities (name) VALUES ('St. Petersburg')
    SQL: INSERT INTO Cities (name) VALUES ('Munich')
    SQL: SELECT Cities.id, Cities.name FROM Cities
    Cities: St. Petersburg, Munich
    SQL: INSERT INTO Users (name, city, age) VALUES ('a', 1, 5)
    SQL: INSERT INTO Users (name, city, age) VALUES ('b', 1, 27)
    SQL: INSERT INTO Users (name, city, age) VALUES ('c', 2, 42)
    SQL: SELECT Users.id, Users.name, Users.city, Users.age FROM Users WHERE Users.city = 1
    Users in St. Petersburg: a, b
    SQL: SELECT Users.id, Users.name, Users.city, Users.age FROM Users WHERE Users.age >= 18
    Adults: b, c

Contributing

Please see the contribution guide before contributing.

By contributing to the Exposed project, you agree that your contributions will be licensed under Apache License, Version 2.0.

exposed's People

Contributors

alexeysoshin avatar alilozano avatar bog-walk avatar cheptsov avatar denlink avatar dependabot[bot] avatar doyaaaaaken avatar e5l avatar fulloforange avatar guredd avatar hfazai avatar hfhbd avatar hhariri avatar imuraveika avatar jnfeinstein avatar joc-a avatar kushalp avatar lure avatar maxr1998 avatar naftalmm avatar orangy avatar oshai avatar sackcastellon avatar sergeyqx avatar shafirov avatar spand avatar t-kameyama avatar tapac avatar valentinkip avatar yukukotani avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exposed's Issues

README.md seems to be out of date

The README.md suggests

Database.connect(...)
transaction {
    create(table)
}

But create() is not available in Transaction.

Please update the README.md.

jcenter version need updated

Exposed's latest tag is 0.6.6 in GitHub, but 0.6.5 in jcenter;

And I can not use jcenter download Exposed directly, I have to do this:

repositories {
    mavenCentral()
    maven { url "https://dl.bintray.com/kotlin/exposed" } // for kotlin exposed only not jcenter() here.
}

Gradle 2.14.1

IdTable should allow other types of ID's

We use GUID Id's generated by the application, but cannot use DAO because of this, without making it another value. Some databases also support auto generated GUID's on the server-side. Neither can work with IdTable as it is now. Therefore cannot use DAO's

Could I merge these 2 fun into one to reduce duplicate code

The problem is caused by InsertStatement and UpdateStatment have no common parent

fun assgin(it: InsertStatement, o: JsonObject) {
val cols = it.table.columns
for (col in cols) {
var type = col.columnType.sqlType()
val idx = type.indexOfAny(listOf(" ", "("))
type = if (idx > 0) type.substring(0, idx) else type
if (o.containsKey(col.name)) {
if (col.name == "hashedPassword") {
val p = o.getString("hashedPassword")
o.put("hashedPassword", createHashString(p))
}
when (type) {
"INT" -> it.set(col as Column, o.getInteger(col.name))
"VARCHAR" -> it.set(col as Column, o.getString(col.name))
"DATE" -> it.set(col as Column, formateDate(o.getString(col.name)))
"DECIMAL" -> it.set(col as Column, o.getDouble(col.name))
}
}
}
}

fun assgin(it: UpdateStatement, o: JsonObject) {
    val cols = it.targetsSet.columns
    for (col in cols) {
        var type = col.columnType.sqlType()
        val idx = type.indexOfAny(listOf(" ", "("))
        type = if (idx > 0) type.substring(0, idx) else type
        if (o.containsKey(col.name)) {
            if (col.name == "hashedPassword") {
                val p = o.getString("hashedPassword")
                o.put("hashedPassword", createHashString(p))
            }
            when (type) {
                "INT" -> it.set<Int>(col as Column<Int>, o.getInteger(col.name))
                "VARCHAR" -> it.set<String>(col as Column<String>, o.getString(col.name))
                "DATE" -> it.set<DateTime>(col as Column<DateTime>, formateDate(o.getString(col.name)))
                "DECIMAL" -> it.set<Double>(col as Column<Double>, o.getDouble(col.name))
            }
        }
    }
}

support for SQL ORDER BY clauses

Please add support for ORDER BY clauses. If there already is support and I'm not seeing it, please add an example of how to add an ORDER BY clause to a Entity.find query. Thanks.

Add MS SQL Server Dialect

I'm trying to perform a limit query against a Microsoft SQL Server database, but I'm getting a "No dialect registered for sqlserver" error. Exposed could use a SQLServerDialect class.

How to handle exception on unique constrain?

object PropName : Table("tblProperty") {
val id = integer("id").autoIncrement().primaryKey()
val name = varchar("name", 50).uniqueIndex()
}
propNameId = PropName.insert {
it[name] = aname
} get PropName.id

I want a logic
if name exists
return id
else
do insert and return id
is there any simple way to write code?

Full text search opportunity

We did not found any functionality for making full text search queries or configurating.
Also it would be useful to make simple string queries via dsl objects.
Do you have any ideas or workarounds?
We use Postgres.

Mysql 5.7: 'SELECT list is not in GROUP BY clause' in `createMissingTablesAndColumns()`

With Mysql 5.7 last part of createMissingTablesAndColumns() fails with this error:

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'information_schema.s.NON_UNIQUE' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
	at com.mysql.jdbc.Util.getInstance(Util.java:408)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:943)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3970)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3906)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2677)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2503)
	at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1369)
	at org.jetbrains.exposed.sql.vendors.MysqlDialect.existingIndices(Mysql.kt:87)
	at org.jetbrains.exposed.sql.QueriesKt.checkExcessiveIndices(Queries.kt:114)
	at org.jetbrains.exposed.sql.QueriesKt.checkMappingConsistence(Queries.kt:90)
	at org.jetbrains.exposed.sql.SchemaUtils$createMissingTablesAndColumns$$inlined$with$lambda$1.invoke(SchemaUtils.kt:130)
	at org.jetbrains.exposed.sql.SchemaUtils$createMissingTablesAndColumns$$inlined$with$lambda$1.invoke(SchemaUtils.kt:8)
	at org.jetbrains.exposed.sql.SchemaUtils.withDataBaseLock(SchemaUtils.kt:149)
	at org.jetbrains.exposed.sql.SchemaUtils.createMissingTablesAndColumns(SchemaUtils.kt:108)

this is because by default 5.7 ships with sql_mode=only_full_group_by

OptionalReferrersOn is not availible

version - 7.4
Entity.kt:
infix fun referrersOn(column: Column<EntityID>) = referrersOn(column, false)

fun referrersOn(column: Column<EntityID<ID>>, cache: Boolean) = Referrers(column, this, cache)

fun optionalReferrersOn(column: Column<EntityID<ID>?>, cache: Boolean = false) = OptionalReferrers(column, this, cache)

OptionalReferrersOn is realized, but not availible

DAO inner objects cause closed ResultSet error

Scenario:

            val objs = PlannedActivity.all().forEach { act ->
                activs.append("<b>"+act.id+"</b>: "+
                    act.members.joinToString { memb -> memb.user.psnName + " (@" + memb.user.telegramName + ")" }+
                    " going to " + act.activity.name + " " + act.activity.mode +
                    " at <b>" + formatStartTime(act.start) + "</b>\n")
            }

members and activity are external fields causing another query:

object PlannedActivities : IntIdTable() {
    val authorId = reference("author_id", Users)
    val activityId = reference("activity_id", Activities)
}

class PlannedActivity(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<PlannedActivity>(PlannedActivities)

    var author by User referencedOn PlannedActivities.authorId
    var activity by Activity referencedOn PlannedActivities.activityId

    val members by PlannedActivityMember referrersOn PlannedActivityMembers.plannedActivityId
}

object PlannedActivityMembers : IntIdTable() {
    val plannedActivityId = reference("planned_activity_id", PlannedActivities)
    val userId = reference("user_id", Users)

    init {
        index(true, plannedActivityId, userId)
    }
}

SQL queries:

SQL: SELECT PlannedActivities.id, PlannedActivities.author_id, PlannedActivities.activity_id, PlannedActivities.details, PlannedActivities."start" FROM PlannedActivities
SQL: SELECT PlannedActivityMembers.id, PlannedActivityMembers.planned_activity_id, PlannedActivityMembers.user_id, PlannedActivityMembers.added FROM PlannedActivityMembers WHERE PlannedActivityMembers.planned_activity_id = 1
SQL: SELECT Users.id, Users.telegram_name, Users.telegram_id, Users.psn_name, Users.email, Users.psn_clan, Users.created_at, Users.updated_at, Users.deleted_at, Users.tokens, Users.pending_activation_code FROM Users WHERE Users.id = 2

The above query causes error with pgsql jdbc driver:

org.postgresql.util.PSQLException: This ResultSet is closed.
        at org.postgresql.jdbc.PgResultSet.checkClosed(PgResultSet.java:2740)
        at org.postgresql.jdbc.PgResultSet.next(PgResultSet.java:1817)
        at org.jetbrains.exposed.sql.Query$ResultIterator.hasNext(Query.kt:211)
        at org.jetbrains.exposed.sql.IterableExKt$mapLazy$1$iterator$1.hasNext(IterableEx.kt:107)
        at kotlin.collections.CollectionsKt___CollectionsKt.joinTo(_Collections.kt:1922)
        at kotlin.collections.CollectionsKt___CollectionsKt.joinToString(_Collections.kt:1943)
        at kotlin.collections.CollectionsKt___CollectionsKt.joinToString$default(_Collections.kt:1942)
        at org.aeriagloris.telegram.commands.ListCommand$execute$1.invoke(ListCommand.kt:24)
        at org.aeriagloris.telegram.commands.ListCommand$execute$1.invoke(ListCommand.kt:14)
        at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:69)
        at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:57)
        at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:48)

Any advice on a workaround?

Field Comment, table comment set by mapping support feature request

varchar("id", 10).primaryKey().comment("Identifier")
Our team very like your project, and we think that this ORM is the most laconic and simple for java (kotlin).
We use it with CodeFirst approach and would be nice to have opportunity to add db comments via mappings.
Thank you for your GREAT work!

Deploy to Maven Central

It is currently difficult to use Exposed in real projects since there is no dependency available on Maven Central or other well known repository, leading to this kind of anti-pattern.

Could you please deploy exposed.jar in Maven Central?

inList filtering causes error with PostgreSQL

The following code

EventField.deleteWhere({ EventField.id.inList(fieldIdList) }) // fieldList is a List<Int>

causes the following error with PostgreSQL:

Can't infer the SQL type to use for an instance of java.util.ArrayList. Use setObject() with an explicit Types value to specify the type to use.

If I comment out the following code, it works fine.
Op.kt:74, InListOrNotInListOp.toSQL

DatabaseVendor.PostgreSQL -> {
    queryBuilder.registerArgument(list, expr.columnType)
}

Cycle references detected, can't sort table references

Certain model structures cause SchemaUtils.create and many other operations to fail with java.lang.IllegalStateException: Cycle references detected, can't sort table references!.

This seems to triggered by tables with self-references, but it only when the table has other references as well. On the attached example, removing either reference from Posts cause creation to succeed. The Post table should be perfectly valid as is.

Minimal example for bug reproduction:
cycle-example.zip

Read-only columns support

By read-only I mean columns that generated/updated by database( for example ON UPDATE for mysql, or generated column, or modified by trigger). Such columns should not be used in update queries.
How about such syntax:

object ConfigTable : IntIdTable() {
    val key = varchar("name", 255)
    val value = varchar("value", 255)
    val created = readonly(datetime("created"))
    val modified = readonly(timestamp("modified"))
}

And for entities all fields that referenced by readonly table-fields can only be val:

class ConfigEntity(id: EntityID<Int>) : IntEntity(id) {
    var key by ConfigTable.key
    var value by ConfigTable.value
// OK
    val created by ConfigTable.created
// Compile-time error
    var modified by ConfigTable.modified

    companion object : IntEntityClass<ConfigEntity>(ConfigTable)
}

ReferrersOn does not make select in DAO example

version 7.4

import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.IntIdTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils.create
import org.jetbrains.exposed.sql.StdOutSqlLogger
import org.jetbrains.exposed.sql.transactions.transaction

object Users : IntIdTable() {
	val name = varchar("name", 50).index()
	val city = reference("city", Cities)
	val age = integer("age")
}

object Cities: IntIdTable() {
	val name = varchar("name", 50)
}

class User(id: EntityID<Int>) : IntEntity(id) {
	companion object : IntEntityClass<User>(Users)

	var name by Users.name
	var city by City referencedOn Users.city
	var age by Users.age
}

class City(id: EntityID<Int>) : IntEntity(id) {
	companion object : IntEntityClass<City>(Cities)

	var name by Cities.name
	val users by User referrersOn Users.city
}

fun main(args: Array<String>) {
	Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")

	transaction {
		logger.addLogger(StdOutSqlLogger())

		create (Cities, Users)

		val stPete = City.new {
			name = "St. Petersburg"
		}

		val munich = City.new {
			name = "Munich"
		}

		User.new {
			name = "a"
			city = stPete
			age = 5
		}

		User.new {
			name = "b"
			city = stPete
			age = 27
		}

		User.new {
			name = "c"
			city = munich
			age = 42
		}

		println("Users in ${munich.name}: ${munich.users.toList().joinToString {it.name}}")
		println("Users in ${stPete.name}: ${stPete.users.joinToString {it.name}}")
		println("Adults: ${User.find { Users.age greaterEq 18 }.joinToString {it.name}}")
	}
}

Output:
SQL: CREATE TABLE IF NOT EXISTS CITIES (ID INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(50) NOT NULL)
SQL: CREATE TABLE IF NOT EXISTS USERS (ID INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(50) NOT NULL, CITY INT NOT NULL, AGE INT NOT NULL, FOREIGN KEY (CITY) REFERENCES CITIES(ID))
SQL: CREATE INDEX USERS_NAME ON USERS (NAME)
Users in Munich:
Users in St. Petersburg:
SQL: INSERT INTO CITIES (NAME) VALUES ('St. Petersburg'),('Munich')
SQL: INSERT INTO USERS (NAME, CITY, AGE) VALUES ('a',1,5),('b',1,27),('c',2,42)
SQL: SELECT USERS.ID, USERS.NAME, USERS.CITY, USERS.AGE FROM USERS WHERE USERS.AGE >= 18
Adults: b, c

DAO is not DRY

From the Readme:

class User(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<User>(Users)

    var name by Users.name
    var city by City referencedOn Users.city
    var age by Users.age
}

I have to repeat myself too much. I would like to be able to write:

class User(id: EntityID<Int>) : IntEntity<User>(id) {
   companion object Users
   var name by Users.name
}

I'm currently learning Kotlin and I understand the limitation that we can't use generic companion objects (meaning we have to rework the way the per-transaction caching works), so I'm curious if others more savvy might have ideas for how we could accomplish this. This is a pretty big refactor that I'm thinking of taking on but I was wondering if folks here might be receptive to it?

Allow to perform queries outside a db.transaction{ } block

In geospatial-messenger, I am currently forced to use db.transaction { } (see MessageRepository).

It would be nice if Exposed could make it possible to support existing transaction management system like the one provided by Spring (see this documentation and DataSourceTransactionManager Javadoc).

Exposed could maybe allow to set the default db to use at Table level, delegate the transaction management to any kind of other mechanism and allow to perform queries with Table.foo() functions without a wrapping them into a db.transaction{ } block.

My goal is to be able, potentially with some extensions, to be able to annotate service functions with @Transactional and just rely on our regular JDBC based transaction management (more details here).

Did this project works on eclipse?

My project was developed with eclipse, and want to use kotlin and exposed to handle the work of DAL, but after some try, I found this project is not working in my project,.following part got compiled issue, which the "forEach" and "it" is not found
(Users join Cities).slice(Users.name, Cities.name). select {(Users.id.eq("andrey") or Users.name.eq("Sergey")) and Users.id.eq("sergey") and Users.cityId.eq(Cities.id)}.forEach { println("${it[Users.name]} lives in ${it[Cities.name]}") }

Use slf4j instead of log4j

Using Exposed in other projects requires to add log4j to runtime, this is uncomfortable when you use another logging tool. Slf4j is de-facto logging facade especially for libraries.
Log4j can be moved to test scope.

Add Documentation

Outside of the samples there seems to be little to no documentation on the library.

What I haven't been able to figure out for example:
Using the DAO method, can I have the object extend Table() and set the primary key myself (say I have some kind of unordered UUIDs or something), if so how would that change the sample code.

There also isn't any kind of javadoc, which makes working with the library quite difficult.

IdTable<int> Can't use not autoincrement id field

issue is only in 0.7.0, 0.6.8 is fine with it
Entity.kt 430

                for ((entry, genValues) in toFlush.zip(ids)) {
// genvalues are empty, null pointer Exception
                    val id = genValues[table.id]!!

// rewrites always by generateg value
                    entry.id._value = (table.id.columnType as EntityIDColumnType<*>).idColumn.columnType.valueFromDB(when (id) {
                        is EntityID<*> -> id._value!!
                        else -> id
                    })

Thank you!

Timeout if sql takes more than 2 seconds to execute

Hi, I am using latest version of exposed and I was getting following
warning WARN 70808 --- [pool-2-thread-1] Exposed : Long query: [2113ms] .

I am OK with my query taking more than 2 seconds to execute. But it looks like this is hard coded in Transaction.kt in variable warnLongQueriesDuration and I get exception. Can you make this configurable or is there any other way around.

Thanks

How To: Many-to-Many and DAO?

First, I've searched everywhere but couldn't find a forum / detailed guide on Exposed framework so I'm resorting to post an issue here.

How do you model a many-to-many relationship using the DAO (Entities) method? Take these models:

package abcd

import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.sql.*

object Tags : IntIdTable(name = "tags") {
    val name = text("name")
}

object Posts : IntIdTable(name = "posts") {
    val uploadDate = datetime("upload_date")
}

object PostsTags : Table(name = "posts__tags") {
    val post = reference("post", Posts).primaryKey(0)
    val tag = reference("tag", Tags).primaryKey(1)
}

class Tag(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<Tag>(Tags)

    var name by Tags.name
}

class Post(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<Post>(Posts)

    var uploadDate by Posts.uploadDate
    val tags by Tag referrersOn PostsTags.tag // Obviously does not work. But how else?
}

How can I modify the Post entity so it has a tags property returning all tags associated to it through PostsTags? val tags by Tag referrersOn PostsTags.tag obviously doesn't work, and I couldn't find any similar alternative constructs.

In case such a scenario is not supported, how could one implement it? By looking on Referrers implementation I've got a general grasp on how to do it, but I'm struck at how to return a SizedIterable of tags from the results of a native SQL DSL query. Is there any mapping from SizedIterable<ResultRow> to SizedIterable<Entity>?

Postgresql support in Exposed

Hi,
we are trying to make Exposed work with Postgresql... looks like the select and insert queries work, but the creat methods fail due to "show tables" commnad that is not supported...
Postgresql needs some other command for finding the existing tables...
can you please help, Thanks.

ok, fixef it, don't know if it's right but:

fun Table.exists (): Boolean {
val tableName = this.tableName
var sql = "show tables"
var resultSet:java.sql.ResultSet? = null
if (Session.get().vendor == DatabaseVendor.PostgreSQL) {
sql = "SELECT tablename FROM pg_catalog.pg_tables"
}
resultSet = Session.get().connection.createStatement()?.executeQuery(sql)

if (resultSet != null) {
    while (resultSet!!.next()) {
        val existingTableName = resultSet!!.getString(1)
        if (existingTableName?.equalsIgnoreCase(tableName) ?: false) {
            return true
        }
    }
}

return false

}

`AssertionError: Entity must be inserted` with self-references - cache problem

When executing multiple inserts for the same table and setting up references between those new entities, the entity cache will fail to flush with java.lang.AssertionError: Entity must be inserted (full stack trace in annexed files).

This can be avoided by flushing the cache explicitly in-between inserts, and doesn't happens with a debbugger atached (inspecting probably triggers cache flushing). My guess is that BatchInsertStatement is trying to build a statement of the form INSERT INTO table (v) VALUES (v1), (v2), ..., which won't work in this case because the ID of the parent entity is still unknown.

Bellow are an complete test case for reproduction and the test reports from my executions:
insert-flush-example.zip
reports.zip

Order by String case insensitive

Is there a way to order by on a text field in case insensitive?
In SQL I know 2 ways:
SELECT * FROM xxxxxx ORDER BY LOWER(yyyy)
SELECT * FROM xxxxxx ORDER BY yyyy COLLATE NOCASE
I would avoid to write plain SQL but I didn't find a dedicated method in this library, so if you have any workaround with the current version, I'm interested.

If it's not present in the library, maybe this ticket can be a good feature to add?

Unable to check table existence MySQL

Whenever I try to check if a table exists using SchemaUtils.create(MyTable) which thus calls table.exists(). An exception is thrown with this message "Table name pattern can not be NULL or empty."

I'm using mysql-connector-java 6.0.3 and Exposed 0.5.0.

Syntax seems degraded since the fork

The syntax with all the it[xxx] calls seems a uglier than before the fork (and the current readme which shows different syntax)

The other choice is to have an object representing schema, but a class to represent state so that you can have an instance of that class available during insert, update, etc. I think the kotlin-nosql went that route.

Without state classes, this is similar to JDBC where you are always having to copy things OUT of something to put into state and pass around. It is more similar to ResulSet now than it was when it started...

Entity queried from database after creation on any prop access

Found some non-obvious behavior: when creating Entity by EntityClass.new with all fields population, then accessing any prop at first time leads to querying this entity from DB. This last query was unexpected for me if I populate all props upon creation.

Internally this is because upon save Entity._readValues backing field is null and written doesn't cached in it.
I think Entity should cache inserted values and return them without additional queries...or maybe I've missed something and this behavior considered as normal?

Calling Database.connect() replaces the global TransactionManager so running threads lose their thread-local transaction reference

Maybe I'm doing an odd thing, but this was a very confusing bug that Exposed seems to be vulnerable to. I'm writing a web server. On each request I call connect on org.jetbrains.exposed.sql.Database. That creates a new ThreadLocalTransactionManager and assigns it to TransactionManager.manager. This means that if I have 2 calls come in at the same time, the first gets a ThreadLocalTransactionManager with a ThreadLocal pointing to its Transaction. The second comes in and gets a second ThreadLocalTransactionManager with a ThreadLocal pointing at its Transaction. However when the first thread goes to find its Transaction it looks at TransactionManager.manager (which is the second ThreadLocalTransactionManager) and asks for the ThreadLocal value for its Transaction. However that's the wrong ThreadLocal, it really needs the one from the first ThreadLocalTransactionManager. So it gets null and Exposed thinks that thread has no transaction in progress which is wrong.

Although I can call Database.connect less often, I would expect your code would be better if the ThreadLocal inside ThreadLocalTransactionManager were inside a companion object. I've tried that and it solves my problem.

InsertStatement.generatedKey doesn't work for IntIdTable

From Queries.kt:

fun <T:Table> T.insert(body: T.(InsertStatement<Number>)->Unit): InsertStatement <Number> = InsertStatement<Number>(this).apply {
    body(this)
    execute(TransactionManager.current())
}

Trying to run the following:

val userId = Users.insert {
            it[email] = "${UUID.randomUUID()}@example.com"
        }.generatedKey

Results in the following error:

Exception in thread "main" java.lang.ClassCastException: org.jetbrains.exposed.dao.EntityID cannot be cast to java.lang.Number

The default insert statements should be updated for IDTables.

Aggregate functions can return null (per the spec) but not in Exposed

Aggregate SQL functions such as sum, avg, max and such return NULL if there was no rows. Has this been considered ? It seems null cannot be expressed currently and will show itself as a NPE with quite high surprise factor in the it[sumCol] call.

val sumCol = TestTable.intCol.sum()
TestTable
        .slice(sumCol)
        .selectAll()
        .map { it[sumCol] }
        .firstOrNull()

*http://www.h2database.com/html/functions.html#sum
*http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_sum

Is this project released?

How can I get any documents for this project?
I want to use kotlin in my project to take the role of DAO layer, and want to use this project, but actually I don't know how can I install this project, should I copy all the src into my project or there is any way to add them by maven ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.