Room returns java.lang.Object for all my suspended Dao functions - kotlin

I have a project with a Room database, which was working fine, and now for some reason I can't find out, it isn't anymore.
I am getting the following error when compiling:
C:\(...)\FlightDao.java:18: error: Not sure how to convert a Cursor to this method's return type (java.lang.Object).
public abstract java.lang.Object getFlightById(int id, #org.jetbrains.annotations.NotNull()
This is from a class that is generated from my Dao:
#Dao
interface FlightDao {
#Query("SELECT * FROM FlightData WHERE DELETEFLAG == 0 AND flightID = :id LIMIT 1")
suspend fun getFlightById(id: Int): FlightData?
}
Now, if I change the function to not suspend, return a Flow and get its first result, it doesnt give an error. But, according to documentation I should do it the way I did, as it is a one-shot operation.
My gradle file has these (entire file here):
apply plugin: 'kotlin-kapt'
(...)
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
and now I am at a loss for why this isn't working. Only things I find are about suspend not being compatible with LiveData, but I don't use LiveData. As far as I can tell, I did it exactly as in the documentation. Anybody has any ideas? (I did try clean and rebuild)
Kotlin version 1.7.0
Room version 2.4.2

I fixed this by switching from kapt to ksp (link)
Still not a clue why kapt didn't want to do it anymore, but meh, it works now :)
new gradle:
plugins {
id 'com.google.devtools.ksp' version '1.7.0-1.0.6'
}
(...)
dependencies {
implementation "androidx.room:room-runtime:$room_version"
ksp "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
(...)
}

try to delete ? in FlightData for null safety
second notice you must write query for condition with one equal sign not twice like programming
use this
#Query("SELECT * FROM FlightData WHERE DELETEFLAG = 0 AND flightID = :id LIMIT 1")

Related

Unable to use Completable in Room dao interface

I'm trying to use Completable in Dao class on #Insert annotated methods but when tried to compile the project, I've got this error:
error: Methods annotated with #Insert can return either void, long, Long, long[], Long[] or List<Long>.
public abstract io.reactivex.Completable insert(#org.jetbrains.annotations.NotNull()
Here my related codes:
#Insert
fun insert(kanal: Kanal): Completable
#Update
fun update(kanal: Kanal): Completable
#Delete
fun delete(kanal: Kanal): Completable
And my dependencies:
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
implementation "android.arch.persistence.room:rxjava2:$room_version"
implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
According to this link it's supported by Room.
#Insert, #Update, and #Delete methods: Room 2.1.0 and higher supports
return values of type Completable, Single, and Maybe.
Update your room from 1.1.1 to 2.1.0 or higher and it will work.
Completable isn't working with #Insert in 1.1.1 version of Room. You have to use 2.1.0 version or higher which is only available in Android X.
Make sure to use these dependencies instead of regular android.arch.persistence.room:
def room_version = "2.2.0-alpha02"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" // For Java use annotationProcessor instead of kapt
implementation "androidx.room:room-rxjava2:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
Here's the link that provides all of the Android X dependencies of Room.
if you dont want to update the Room's Version, you can try to make a Completable return like this:
fun insertSomthing():Completable{
return Completable.fromAction{
insert(kanal: Kanal)
}.subscribeOn(Schedulers.io())
}
above didnt work for me, here is what i used in oder fix it
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"

Insert a row into Room using Rxjava on Kotlin

This article says that I can use Completable as return type for #Insert
But as I do that, the error occured:
error: local variable pointToInsert is accessed from within inner class; needs to be declared final
This error happens with AndoridX since Rxjava return types support included only since 2.1 version : https://issuetracker.google.com/issues/63317956#comment25
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertPoint(pointToInsert: ControlPoint): Completable
So, how to make this thing work?
As this feature is completely unavailable unless you use version 2.1+, you can actually solve this problem using lower version by making some kind of adapter for you DAO:
#Dao
interface Original {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertPoint(pointToInsert: ControlPoint)
}
class AdHocCompletableAdapter(private val dao: Original) {
fun insertPoint(pointToInsert: ControlPoint) =
Completable.create {
dao.insertPoint(pointToInsert)
it.onComplete()
}
}
Or create some more flexible solution (using, for instance, function composition).

Kotlin support for JDBI SqlObject gives UnsupportedOperationException

Extending the Kotlin equivalent of the Dropwizard JDBI3 setup listed in the official Dropwizard documentation, I fail to get automatic parameter binding without #Bind and the Kotlin-specific mapping magic for JDBI to work as shown in Kotlin support for SqlObject. Instead of this...
data class Thing(val id: Int, val name: String,
val nullable: String?,
val nullableDefaultedNull: String? = null,
val nullableDefaultedNotNull: String? = "not null",
val defaulted: String = "default value")
interface ThingDao {
#SqlUpdate("insert into something (id, name) values (:something.id, :something.name)")
fun insert(something: Thing)
#SqlQuery("select id, name from something")
fun list(): List<Thing>
...
}
..I always have to do:
interface ThingDao {
#SqlUpdate("insert into something (id, name) values (:id, :name)")
fun insert(#Bind("id") id: Int?, #Bind("name") name: String)
#SqlQuery("select id, name from something")
fun list(): List<Thing>
...
}
Gradle has these JDBI-specific settings:
...
compile "io.dropwizard:dropwizard-jdbi3:1.3.5"
compile "org.jdbi:jdbi3-sqlobject:3.3.0"
compile "org.jdbi:jdbi3-postgres:3.3.0"
compile "org.jdbi:jdbi3-kotlin:3.3.0"
compile "org.jdbi:jdbi3-kotlin-sqlobject:3.3.0"
....
The Dropwizard application has the following run configuration:
override fun run(configuration: MyConfig, environment: Environment) {
val factory = JdbiFactory()
val jdbi = factory.build(environment, configuration.database, "postgresql")
// This is said to install all available plugins and is thus redundant.
// I have tried to include various combinations of the following in
// some desperation. None work.
jdbi.installPlugins()
jdbi.installPlugin(SqlObjectPlugin()) // This...
jdbi.installPlugin(KotlinPlugin())
jdbi.installPlugin(KotlinSqlObjectPlugin()) // ..and this alone are said to do the job
...
Otherwise, everything seems to run just fine with custom UUID mappings, Jackson Kotlin data object mapping and such.
The result of using :something.id in particular always is:
java.lang.UnsupportedOperationException: No argument factory registered for 'Thing(...'
The solutions suggested in the comments all work. Thanks very much! The trouble seemingly arose in an unfortunate sequence of adding the right libraries and fixing the code in a process that left the impression of things not working (I am still not a fan of annotation-mania).
Summary for anyone that might find anything useful in it:
Adding #BindBean made data classes work as arguments indeed.
Leaving out #Bind for arguments apparently worked for quite a while, which I just didn't notice...
..possibly in parts as I incorrectly tried to use it with data classes, too: #Bind("something") something: Thing instead of the correct (but still unnecessary) #BindBean("something") something: Thing
Given this, removing #Bind and #BindBean worked (at least with the 1001st gradle clean build).
I removed compile "org.jdbi:jdbi3-sqlobject:$jdbi_version", which either way doesn't seem to have any effect on things working properly (as Dropwizard is said to reference it already).
For the record, these are the dependencies that seem to work for me right now:
buildscript {
ext.kotlin_version = '1.2.51'
ext.dropwizard_version = '1.3.5'
ext.jdbi_version = '3.3.0'
...
}
...
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "io.dropwizard:dropwizard-core:$dropwizard_version"
compile "io.dropwizard:dropwizard-jdbi3:$dropwizard_version"
compile "io.dropwizard:dropwizard-auth:$dropwizard_version"
compile "io.dropwizard:dropwizard-views-freemarker:$dropwizard_version"
// This is to serve static content from resources/static
compile "io.dropwizard:dropwizard-assets:$dropwizard_version"
compile 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4'
compile 'com.fasterxml.jackson.module:jackson-modules-java8:2.9.4'
// Removed as redundant
// compile "org.jdbi:jdbi3-sqlobject:$jdbi_version"
compile "org.jdbi:jdbi3-postgres:$jdbi_version"
// So, these are still required as Dropwizard doesn't know Kotlin
compile "org.jdbi:jdbi3-kotlin:$jdbi_version"
compile "org.jdbi:jdbi3-kotlin-sqlobject:$jdbi_version"
// Database
compile 'org.postgresql:postgresql:42.1.4'
// ...
testCompile "io.dropwizard:dropwizard-testing:$dropwizard_version"
// ...
}

kotlin : cannot parse string to enum with Jackson

I use the lib jackson-module-kotlin to parse string of json into object.
My issue is when I parse a string into an enum , and when I launch with intellij, I have this stack trace:
Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
Reflection on built-in Kotlin types is not yet fully supported. No
metadata found for public final val name: kotlin.String defined in
kotlin.Enum[DeserializedPropertyDescriptor#212b316a]
I don't have this issue when I launch with maven.
I use kotlin 1.1.51, with intellij kotlin plugin 1.2.0-release-IJ2017.3-1, I target a JVM 1.8, and i use jackson-module-kotlin version 2.8.7
what should I do?
enum class CType { DEAL, FILE }
data class Code(val code: String, val type: CType)
fun testDeserialization() {
val mapper = jacksonObjectMapper()
// following line throws an exception:
mapper.readValue("""{"code":"A","type":"DEAL"}""", Code::class.java)
}
The only way I got it working is by adding additional #JvmStatic annotation. I had mapper.registerModule(new KotlinModule()); and all, nothing worked but this:
package nc.features.algo.model
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonValue
enum class LHStatus (
#get:JsonValue val code: Int
) {
LH_POS_OVU_WAITING(1),
LH_NEG_OVU_WAITING(2),
;
companion object {
#JsonCreator
#JvmStatic
fun deser(code: Int?): LHStatus? {
if (code == null) return null
for (i in values()) {
if (i.code == code) return i
}
return null
}
}
}
You have to do a few things.
Update Jackson dependencies to the latest version (right now, 2.9.4).
Update Kotlin version to a version equal or greater than 1.3.0.
Be sure to add the following dependencies to your build.gradle:
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
... then you call registerKotlinModule() on your Jackson ObjectMapper and the code of your enum should be just like this:
enum class CType(#get:JsonValue val value: String) {
DEAL("deal"),
FILE("file");
companion object {
#JsonCreator
fun fromString(value: String): CType? {
for (type in CType.values()) {
if (type.name.equals(value, true)) {
return gender
}
}
return null
}
}
}
Intellij is most likely using the kotlin compiler version 1.2.0 (from the plugin) and it doesn't seem to support reflection properly.
I suggest you do one of the following:
Upgrade your kotlin version in maven and the intellij kotlin plugin to newer versions (e.g. 1.2.30). If you do that, you also have to update jackson-module-kotlin to >= 1.9, since there is an incompatibility with kotlin 1.2 (see here).
Set the kotlin compiler version to 1.1 in Intellij Idea settings.
It is generally a good idea to use the same version of kotlin in Intellij Idea and maven/gradle.
You need to use the Kotlin module for Jackson that is compatible with Kotlin 1.2.x, this includes minimally these three versions of the module:
2.9.4.1 (works with any 2.9.x of Jackson, but best to use most recent)
2.8.11.1 (for Jackson 2.8.x)
2.7.9.1 (for Jackson 2.7.x)
Otherwise, you will run into a problem with library mismatches.
The jackson-module-kotlin homepage lists these as the current versions, but they are likely to change and you can check the various Maven repository search engines to see which library versions are available and which dependencies they have on Kotlin to find matching versions.
Also note you can import the extensions for the ObjectMapper class and use reified types, so instead of:
val something = mapper.readValue("""{"code":"A","type":"DEAL"}""", Code::class.java)
you would have:
val something: Code = mapper.readValue("""{"code":"A","type":"DEAL"}""")
or alternatively:
val something = mapper.readValue<Code>("""{"code":"A","type":"DEAL"}""")
It is usually bad to use the erased type (i.e. Whatever::class.java) since this does not work for anything with generic type parameters, and using reified types also works nicely when deserializing into collections.

How to use DeprecationLevel.ERROR

Let's say I am writing a library and have a class that looks something like this (contrived example, but shows self reference:
import java.util.logging.Logger
class MyClass(private val myNum: Int) {
companion object {
private val LOG = Logger.getLogger(MyClass::class.java.canonicalName)
}
constructor() : this(1337)
fun addTo(num: Int): Int {
LOG.fine { "Adding num $num to $myNum" }
return myNum + num
}
fun doubleAdd(num: Int): Int = 2 * addTo(num)
}
Now, I have decided that I want to deprecate this class and have my consumers move on to to better things, so I give them a warning.
#Deprecated("Don't use!", level = DeprecationLevel.WARNING)
class MyClass(private val myNum: Int) {
// ...
}
Now, after some more time I'd like to increase the strictness with my deprecation. I still want the library to be binary compatible, so I do not remove the code I see that there is the DeprecationLevel.ERROR available, so I try to use it.
#Deprecated("Don't use!", level = DeprecationLevel.ERROR)
class MyClass(private val myNum: Int) {
// ...
}
Except now, when I try to compile my own project, I get compiler errors:
e: /path/to/project/src/main/kotlin/MyClass.kt: (7, 44): Using 'MyClass' is an error. Don't use!
e: /path/to/project/src/main/kotlin/MyClass.kt: (10, 23): Using 'MyClass' is an error. Don't use!
This is on both the MyClass reference and the this primary constructor reference.
What is the point of DeprecationLevel.ERROR? If I am using it wrong, what is the intended use, and how do I use it?
NOTE: This whole example was done with Kotlin 1.2.21
It does exactly what is described in the documentation: DeprecationLevel
ERROR means usage of that code generates an error in the compiler. This is when you know using the code is going to cause problems and you'd rather crash the compilation, even if that code compiled fine previously.
There is also the HIDDEN deprecation level which does what you describe. It 'hides' the annotated element from the compiler but leaves it in the binary output. This will still cause a compilation error in your project because it is meant for binary compatibility, not newly compiled code.