How to re-use DAO class of Exposed for JSON serialization or transfering data? - kotlin

Recently I try to learn Kotlin by writing a Kotlin + Exposed demo.
Common steps using Java + MyBatis would be: creating Java Bean class, mapper class, service class and service implementation class
#Data class UserBean { String username; String password; }
#Mapper interface UserMapper extends BaseMapper<UserBean> {}
interface UserService extends BaseService<UserBean> {}
#Service UserSerivceImpl extends BaseServiceImpl<UserBean> implements UserService {}
Then those Java Bean classes are used in any other parts of system for database IO and JSON serialization.
// create instance and convert to JSON
var user = new UserBean();
user.setUsername("username");
user.setPassword("password");
var om = new ObjectMapper();
var json = om.valueToTree(user);
Following official doc of Exposed DAO API, I create those classes:
class User(id : EntityID<Int>) : IntEntity(id)
{
companion object : IntEntityClass<User>(Users)
var username by Users.username
var password by Users.password
}
object Users : IntIdTable()
{
val username = varchar("username", 64)
val password = varchar("password", 64)
}
When performing database IO, User.all() and User.new { } api work well. But creating instance directly would throw an exception:
// get data from JSON or somewhere else
val username = ...
val password = ...
// trying to create instance
val id = EntityID(-1, User.table) // trying to create an empty ID. I don't know if this is allowed
val user = User(id)
user.username = username // this would throw exception
user.password = password
Caused by: java.lang.IllegalStateException: Property klass should be initialized before get.
at kotlin.properties.NotNullVar.getValue(Delegates.kt:62)
at org.jetbrains.exposed.dao.Entity.getKlass(Entity.kt:34)
at org.jetbrains.exposed.dao.Entity.setValue(Entity.kt:198)
Post here says Exposed does not allow creating DAO objects by yourself. So is there a easy way to re-use those DAO classes for JSON serialization or transfering data between parts of program? Should I create a DTO class with identical data fields?

Exposed Entities are stateful objects. You shouldn't serialize them directly. Instead, as you mentioned, you should use a simple data class with serialization annotations relevant to you.
For example:
class User(id : EntityID<Int>) : IntEntity(id)
{
companion object : IntEntityClass<User>(Users)
var username by Users.username
var password by Users.password
fun asResponse(): UserResponse {
return UserResponse(username, password)
}
}
#Serializable
data class UserResponse(val username: String, val password: String)

Related

How to change return type based on a function input which is a class name?

I have multiple data classes and each class has a corresponding class containing more info. I want to write a function in which I should be able to pass an identifier (table name corresponding to the data class). Based on this identifier, object of the corresponding class should be made, the value changed and this object should be returned as output of the function. I have written a simplified version of it on playground but I am unable to get it to work. Any help is appreciated.
class someClass(
)
class objectForSomeClass(
var value: String
)
class someOtherClass(
)
class objectForSomeOtherClass(
var value: String
)
class doSomething() {
companion object {
val classMap = mapOf(
"someClass" to objectForSomeClass::class,
"someOtherClass" to objectForSomeOtherClass::class,
)
}
// Create a map of class name to a new object based on the class name input
fun dummyFun(className: String, valueInput: String): Map<String, kotlin.Any> {
var returnObject = mutableListOf<Pair<String, kotlin.Any>>()
when(className) {
"SOME_CLASS" -> {
returnObject = mutableListOf<Pair<String, justDoIt.classMap["someClass"]()>>()
}
"SOME_OTHER_CLASS" -> {
returnObject = Map<String, justDoIt.classMap["someOtherClass"]()>
}
}
returnObject[className].value = valueInput
return returnObject
}
}
fun main() {
var obj = doSomething()
var t = obj.dummyFun("SOME_CLASS", "Value to be inserted")
// do something with t
}
Not knowing more about your classes (the ones in your code are not data classes – a data class in Kotlin is a specific type of class) I still think a lot could be simplified down to maybe even this:
fun createObject(className: String, value: String): Any? {
return when (className) {
"SomeClass" -> ObjectForSomeClass(value)
"SomeOtherClass" -> ObjectForSomeOtherClass(value)
// ...
else -> null
}
}
Additionally:
The classMap is not necessary, you can hard-code the cases in the when clause as in my example. There is also no need for reflection, which you would need to create instances from SomeType::class.
With getting rid of classMap you also do not need the companion object holding it anymore, and then you are left with one function for creating instances of your classes, and this function does not have to be in a class. You might put it into a singleton class called object in Kotlin (https://kotlinlang.org/docs/object-declarations.html#object-expressions)
Data classes in Kotlin: https://kotlinlang.org/docs/data-classes.html
You could maybe also replace each class someClass & class objectForSomeClass pair with a class someClass with a companion object.

POJO class mismatch

I have the following class User that extends the BaseResponse class. I
am getting a type mismatch error:
Required => String
Found => String.Companion
for return apiKey
package com.touchsides.rxjavanetworking.network.model
import com.google.gson.annotations.SerializedName
class User: BaseResponse()
{
#SerializedName("api_key")
val apiKey = String
fun getApiKey(): String
{
return apiKey
}
}
abstract class BaseResponse(var error: String?=null)
{
}
How is the current implementation of this wrong
You used = instead : while declaration of api_key (apiKey = String). Which actually means you are initialising api_key with String.Companion Object.
And you don't need to create getApiKey() (getter) method as by default you will have getter method for your properties.
class User : BaseResponse() {
#SerializedName("api_key")
var apiKey: String? = null
private set
}
abstract class BaseResponse(var error: String? = null)
in fact you can use data class for this purposes
data class User(#SerializedName("api_key") val apiKey: String):BaseResponse()
fun main(args: Array<String>) {
Gson().fromJson<User>("{\"api_key\":\"my api key\"}", User::class.java).let {
println(it.apiKey)
}
}
A complete answer is that your code should look like this:
class User: BaseResponse()
{
#SerializedName("api_key")
lateinit var apiKey: String // must be set by something before being read
}
abstract class BaseResponse(var error: String?=null) {
}
You do not need a default value for the apiKey property if you intend to set it via deserialization later, if not then you should also add a default value as below. The getApiKey() method is removed because you do not need that in Kotlin, all properties have automatically generated getters built-in and by adding your own you would end up with a conflict between the generated getter and the one you manually created (two methods with the same name, same signature).
If you do need a default value for apiKey then stay with a var so that deserialization can work (if you intend to do that) and add a default empty string or make it a nullable string and set it to null.
class User: BaseResponse()
{
#SerializedName("api_key")
var apiKey: String = "" // if you want a default regardless, or make it nullable and null
}
abstract class BaseResponse(var error: String?=null) {}
You're stuck with the way Java do things. In kotlin when defining Getter and Setter, you don't have to write them yourself. Once you declare a variable, both methods would be automatically created.
EDIT: So delete the getter in your POJO class.

Kotlin delegate all fields by class property

I have a wrapper class Manager that has a property managerInfo of type UserInfo that I can’t modify. The wrapper class just add business rules around the info object. In code I need to access properties of the managerInfo and write each time manager.managerInfo.username is a little verbose.
I know that I can delegate property to this info object like that:
class Manager {
...
val username by UserInfo
...
}
And then I can simply do manager.username. But the problem is that the info class has about 15 properties and do this manually will be messy.
Is there is way to delegate all properties by means of Kotlin lang or by some library?
You can do implementation by delegation, which looks like this:
interface UserCommon {
val username: String
val email: String
}
data class UserInfo(
override var username: String,
override var email: String
) : UserCommon
class Transaction(
userInfo: UserInfo
) : UserCommon by userInfo
After that class Transaction will have all properties that UserCommon does, but implementation is delegated to userInfo which is passed to constructor.
The other way is to convert UserInfo to MutableMap and delegate property invocation to that map:
class Transaction(
userInfoMap: HashMap<String, Any>
) {
var username: String by userInfoMap
var email: String by userInfoMap
}

Creating a DB factory using kotlin

So I'm trying to create a MongoDB factory with kotlin... but I think I don't really understand the concept of companion object very well because I can't even get this to compile:
package org.jgmanzano.storage
import com.mongodb.MongoClient
import com.mongodb.MongoClientURI
import com.mongodb.client.MongoDatabase
class MongoConnectionFactory(private val connectionURI: String) {
private var database: MongoDatabase
init {
val connectionString = MongoClientURI(connectionURI)
val mongoClient = MongoClient(connectionString)
database = mongoClient.getDatabase("paybotDB")
}
companion object {
fun getDatabase() : MongoDatabase {
return database
}
}
}
How would you guys achieve this? My idea is to create what in Java would be a kind of factory method. I can't seem to get the syntax right tho.
Furthermore, would this be a correct approach to DB connection factories?
Move everything to the companion object, pass the connection URI to the getDatabase method.
Companion objects get compiled as a static field inside the containing (outer class). Since the field is static, it cannot access outer class's fields because the outer class is an instance.
I assume you want to cache database objects.
class MongoConnectionFactory() {
companion object {
private var database: MongoDatabae? = null
fun getDatabase(connectionURI: String) : MongoDatabase {
if (database != null) {
return database
{
val connectionString = MongoClientURI(connectionURI)
val mongoClient = MongoClient(connectionString)
database = mongoClient.getDatabase("paybotDB")
return database
}
}
}
But then you don't need a companion object nested inside containing class.
You can create an object instead.
object MongoConnectionFactory {
private var database: MongoDatabae? = null
fun getDatabase(connectionURI: String) : MongoDatabase {
if (database != null) {
return database
{
val connectionString = MongoClientURI(connectionURI)
val mongoClient = MongoClient(connectionString)
database = mongoClient.getDatabase("paybotDB")
return database
}
}
If you need multiple databases with different connection URIs then store them inside the hash table.

How do I test generators for delegated properties?

In my current project there is a class that will later on be implemented by many others. This class provides some generators for delegated properties.
abstract class BaseClass {
protected val delegated1 get() = new Delegated1Impl()
protected val delegated2 get() = new Delegated2Impl()
...
}
This base class can be used this way:
class Example : BaseClass() {
var field1 by delegated1
var field2 by delegated2
}
Now I want to test these delegated generators. Some of them contain logic which I want to test, but for now I only want to know that everytime they are called they return a new instance.
Now my question is: how can I test these generators?
The generators are not visible outside of extending classes so I cannot simply create an instance of it and call these methods.
#Test
fun `delegated1 should always return a new instance`() {
val target = object: BaseClass()
val first = target.delegated1 // This does not work since it is protected
val second = target.delegated1
assertTrue(first !== second)
}
You need a new object created whenever you "call" the get method. So how to test it? With a provider
A Provider<T> is just an object that provides you new instances of a concrete class. Its signature is something like this:
interface Provider<T> {
fun get() : T
}
So you need to inject a new Provider<T> into your BaseClass:
abstract class BaseClass(
private val implementation1Provider : Provider<YourInterface>,
private val implementation2Provider : Provider<YourInterface>) {
protected val delegated1 get() = implementation1Provider.get()
protected val delegated2 get() = implementation2Provider.get()
...
}
Now you can inject your custom providers in the test and assert that they have been called:
#Test
fun `delegated1 should always return a new instance`() {
val implementation1Provider = ...
val target = Example(implementation1Provider, ...)
val first = target.field1
// assert that implementation1Provider.get() has been called
}