English | 简体中文
Edit Page

Break Changes in Ktorm 3.0

After a few months, we finally ushered in another major version update of Ktorm (Ktorm 3.0). This update contains many optimizations, and there are also some incompatible changes, which is hereby explained.

If these incompatible updates have an impact on your project, we are very sorry, but this is a trade-off that must be made in order to ensure the long-term iteration of the framework. Please simply modify your code according to this document, which will only cost you a few minutes time.

ktorm-global

Ktorm 2.7 deprecated the global variable Database.global and a series of APIs based on it, making users explicitly specify the Database instances to use while performing database operations, instead of implicitly use Database.global. For more information about the previous version, please refer to About Deprecating Database.global.

Using global variables is a bad design pattern. Code written in this way will be coupled with some global states, which is difficult to be tested and extended, and this is why we have to deprecate Database.global. However, there are also some advantages, as we can make some APIs more concise with the help of the global variable. For example, Employees.findAll(), after Ktorm 2.7, we have to write database.sequenceOf(Employees).toList(), which looks a lot more verbose.

Ktorm 3.0 has completely removed Database.global and its related APIs. But in order to give you more choices, we provide an additional module ktorm-global, which reimplements those deprecated APIs in version 2.7. You can use it as needed.

To use ktorm-global, you should add a Maven dependency first:

1
2
3
4
5
<dependency>
<groupId>org.ktorm</groupId>
<artifactId>ktorm-global</artifactId>
<version>${ktorm.version}</version>
</dependency>

Or Gradle:

1
compile "org.ktorm:ktorm-global:${ktorm.version}"

Then connect to the database via function Database.connectGlobally:

1
Database.connectGlobally("jdbc:mysql://localhost:3306/ktorm", user = "root", password = "***")

This function returns a new-created Database object, you can define a variable to save the returned value if needed. But generally, it’s not necessary to do that, because ktorm-global will save the latest created Database instance automatically, then obtain it via Database.global when needed.

1
2
3
Database.global.useConnection { conn -> 
// Do something with the connection...
}

With the help of the global object, our code can be more shorter, for example, create a query by directly using the extension function Table.select:

1
2
3
for (row in Employees.select()) {
println(row[Employees.name])
}

Use Table.findList to obtain entity objects in the table that matches the given condition:

1
val employees = Employees.findList { it.departmentId eq 1 }

Use Table.sumBy to sum a column in the table:

1
val total = Employees.sumBy { it.salary }

For more convenient usages, please explore by yourself. You can also refer to Changes in Ktorm 2.7. Almost all those deprecated functions are reimplemented in ktorm-global.

Use = Instead of Property Delegation to Define Columns

Before Ktorm 3.0, when we created a table object, we needed to use the by keyword and define the columns as property delegates, like this:

1
2
3
4
5
6
// Before Ktorm 3.0
object Departments : Table<Nothing>("t_department") {
val id by int("id").primaryKey()
val name by varchar("name")
val location by varchar("location")
}

But now, we no longer need property delegates anymore, just use the equal sign =:

1
2
3
4
5
6
// Ktorm 3.0
object Departments : Table<Nothing>("t_department") {
val id = int("id").primaryKey()
val name = varchar("name")
val location = varchar("location")
}

Using the equal sign = is more simple and straightforward, and avoids some extra fields generated by the compiler for property delegates. However, this change will cause many compilation errors in your project after upgrading to the new version. Don’t worry, the only thing you need to do is just to find out all table objects in your project, and replace the by keywords with equal signs = in batches.

Query doesn’t Implement Iterable anymore

In the past, in order to easily obtain the query results, we decided to let Query implement the Iterable interface, so that we can iterate the results by a for-each loop, or process them via extension functions like map, flatMap, etc. For example:

1
2
3
4
5
6
7
8
9
data class Emp(val id: Int?, val name: String?, val salary: Long?)

val query = database.from(Employees).select()

query
.map { row -> Emp(row[Employees.id], row[Employees.name], row[Employees.salary]) }
.filter { it.salary > 1000 }
.sortedBy { it.salary }
.forEach { println(it.name) }

But this also brings us a lot of problems, because the names of many extension functions of Iterable are similar to the functions of Query, and there may even be name conflicts, which will cause many misunderstandings for users, such as #124, #125.

Therefore, we decided that in Ktorm 3.0, Query no longer implements the Iterable interface anymore. And to keep our DSL code unchanged, we also provide some extension functions that are equivalent to those of Iterable‘s. After the upgrade, you will find that although some compilation errors may occur, your code is almost not needed to change. The only thing you may need is to add a line of import statement to change the original calls to the Iterable.map function to Query.map:

1
import org.ktorm.dsl.*

Support Compound Primary Keys

A compound primary key is composed of multiple columns, which together uniquely identify a table row. In Ktorm 3.0, we also support configuring compound primary keys for tables. The usage is very simple, we just need to call the primaryKey function for each column of the compound keys when creating table objects:

1
2
3
4
5
object Departments : Table<Nothing>("t_department") {
val id = int("id").primaryKey()
val name = varchar("name").primaryKey()
val location = varchar("location")
}

This is not just a simple enhancement feature, but there is also an incompatibility with previous versions. The val primaryKey: Column<*> property in the BaseTable class has been removed and changed to val primaryKeys: List<Column<*>>, which is used to get all the columns that make up the primary key.

Others

In addition to the incompatible changes above, Ktorm 3.0 also contains many updates from enthusiasts in the open source community, thanks for their contributions:

  • MySQL bulkInsert function supports on duplcate key update. Thank @hangingman
  • PostgreSQL hstore data type and a series of operators for it. Thank @arustleund
  • ktorm-jackson supports simple Jackson annotations, like @JsonProperty, @JsonAlias, @JsonIgnore. Thank @onXoot