How to use Type converters for complex data type in ROOM library? - android-room

I'm new to ROOM library. I've some complex json data structure which i would like to store in ROOM database, i don't know how to use Type convertes for multiple list of objectes. Following are my Entities,
// Trying to put all my custom models in a single table
#Entity(tableName = "myTable")
data class RaceModelDatabase(
#PrimaryKey
val ID: String,
#Embedded val info: CustomModel,
#Embedded(prefix = "parti")
val parti: Map<String,UserModelDatabase> ,
#Embedded val totalTime: Map<String,TimeDataModel>
)
// Custom Models which also has Map objects
data class CustomModel (val name :String, val crdate : String )
data class UserModelDatabase(#Embedded val info : CustomModel,
#Embedded(prefix = "Result_") val result :Map<String,CustomModel>
)
data class TimeDataModel (
val Start : Long,
val End : Long
)

Here is an example of how to use TypeAdapter for one of your Map objects. You could follow the same for the rest.
class RaceTypeConverter {
#JvmStatic
#TypeConverter
fun fromString(value: String): Map<String, TimeDataModel > {
val mapType = object : TypeToken<Map<String, TimeDataModel >>() {}.type
return Gson().fromJson(value, mapType)
}
#TypeConverter
#JvmStatic
fun fromStringMap(map: Map<String, TimeDataModel>): String {
val gson = Gson()
return gson.toJson(map)
}
}

Related

Kotlin & Jackson: type error when specifying custom serialisation for a data class field

I have a Kotlin data class that is serialised to JSON in a Spring Boot project. I'd like to customise how date is formatted when serialising to JSON. The name of the field should be serialised using default rules. That expresses what I'd like to do:
class ZonedDateTimeSerialiser : JsonSerializer<ZonedDateTime>() {
#Throws(IOException::class)
fun serialize(value: ZonedDateTime, gen: JsonGenerator, serializers: SerializerProvider?) {
val parseDate: String? = value.withZoneSameInstant(ZoneId.of("Europe/Warsaw"))
.withZoneSameLocal(ZoneOffset.UTC)
.format(DateTimeFormatter.ISO_DATE_TIME)
gen.writeString(parseDate)
}
}
data class OrderNotesRequest(
#JsonSerialize(using = ZonedDateTimeSerialiser::class)
val date: ZonedDateTime = ZonedDateTime.now()
)
But I get a type error:
Type mismatch.
Required:
KClass<out (JsonSerializer<Any!>..JsonSerializer<*>?)>
Found:
KClass<ZonedDateTimeSerialiser>
I did try switching the parameter to annotation to contentUsing but the type error remained the same.
Following is working for me
object JacksonRun {
#JvmStatic
fun main(args: Array<String>) {
val objMapper = ObjectMapper().apply {
registerModule(KotlinModule())
}
val order = OrderNotesRequest()
println(objMapper.writeValueAsString(order))
}
}
data class OrderNotesRequest(
#JsonSerialize(using = ZonedDateTimeSerialiser::class)
val date: ZonedDateTime = ZonedDateTime.now()
)
class ZonedDateTimeSerialiser : JsonSerializer<ZonedDateTime>() {
#Throws(IOException::class)
override fun serialize(value: ZonedDateTime, gen: JsonGenerator, serializers: SerializerProvider?) {
val parseDate: String = value.withZoneSameInstant(ZoneId.of("Europe/Warsaw"))
.withZoneSameLocal(ZoneOffset.UTC)
.format(DateTimeFormatter.ISO_DATE_TIME)
gen.writeString(parseDate)
}
}
build.gradle.kts:
dependencies {
implementation("com.fasterxml.jackson.core:jackson-core:2.13.2")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.2")
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.0")
}
Gives me output:
{"date":"2022-03-21T10:29:19.381498Z"}
Do make sure you have the correct import for JsonSerializer
import com.fasterxml.jackson.databind.JsonSerializer
and add override marker to serialize method

No value passed for parameter

I use kotli. I define everything as per requirement, why I am getting
thing type of Issue
UserRegistrationService.kt: (25, 36): No value passed for parameter 'userRegistration'
I got this type of issue at my UserRegistration class No value passed
for parameter department, and userRegistration
I Creat ResponseTemplateVO POJO Class
ResponseVO.kt
package com.userservice.userregistration.VO
import com.userservice.userregistration.entity.UserRegistration
data class ResponseTemplateVO(
var userRegistration: UserRegistration,
var department: Department
)
Department.kt
package com.userservice.userregistration.VO
data class Department(
val departmentId:Long=-1,
val departmentName:String="",
val departmentAddress:String="",
val departmentCode:String=""
)
UserRegistration.kt
package com.userservice.userregistration.entity
data class UserRegistration(
val userId:Long=-1,
val firstName:String="",
val lastName:String="",
val email:String="",
val departmentId:Long=-1,
)
UserRegistrationService.kt
package com.userservice.userregistration.service
import com.userservice.userregistration.VO.Department
import com.userservice.userregistration.VO.ResponseTemplateVO
import com.userservice.userregistration.entity.UserRegistration
import com.userservice.userregistration.repository.UserRegistrationRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
#Service
class UserRegistrationService {
#Autowired
private lateinit var userRegistrationRepository: UserRegistrationRepository
#Autowired
private lateinit var restTemplate: RestTemplate
fun saveUserDetails(userRegistration: UserRegistration): UserRegistration {
return userRegistrationRepository.save(userRegistration)
}
fun getUserWithDepartment(userId: Long): ResponseTemplateVO {
val vo= ResponseTemplateVO()
val userRegistration:UserRegistration=userRegistrationRepository.findUserById(userId)
val department: Department? =
restTemplate.getForObject("http://localhost:9001/departments/"+userRegistration.departmentId,
Department::class.java)
vo.userRegistration=userRegistration
if (department != null) {
vo.department=department
}
return vo
}
}
I am getting error at this below method at the line 2
val vo= ResponseTemplateVO()
No value passed for parameter department and userRegistration .This is
the error
fun getUserWithDepartment(userId: Long): ResponseTemplateVO {
val vo= ResponseTemplateVO()
val userRegistration:UserRegistration=userRegistrationRepository.findUserById(userId)
val department: Department? =
restTemplate.getForObject("http://localhost:9001/departments/"+userRegistration.departmentId,
Department::class.java)
vo.userRegistration=userRegistration
if (department != null) {
vo.department=department
}
return vo
}
This declaration:
data class ResponseTemplateVO(
var userRegistration: UserRegistration,
var department: Department
)
packs multiple things:
it declares 2 properties userRegistration and department
it defines the primary constructor of the class with 2 arguments: userRegistration and department
When you write:
val vo = ResponseTemplateVO()
You're calling the constructor of that class, but you don't specify the 2 required arguments. You should instead call it by passing the arguments:
fun getUserWithDepartment(userId: Long): ResponseTemplateVO {
val userRegistration:UserRegistration=userRegistrationRepository.findUserById(userId)
val department: Department? = restTemplate.getForObject("http://localhost:9001/departments/"+userRegistration.departmentId,
Department::class.java)
if (department == null) {
// here you should decide if it should have a default value
// or throw an exception
}
return ResponseTemplateVO(userRegistration, department)
}
Note that you declared the department property as non-null, so you need a non-null department in order to create an instance of your class.
So if department is null you have 3 options:
throw an exception
use a default value instead
change the type of department in ResponseTemplateVO so it accepts nulls (Department? with ?)
Also, if you instantiate your class with all required value like that, and you don't need to modify its properties later, the properties can be declared val. This is usually more idiomatic Kotlin. With immutability, it's easier to reason about the values.
The issue is in the data class.
data class ResponseTemplateVO(
var userRegistration: UserRegistration,
var department: Department
)
Here you have added the following params into the constructor of the data class. Hence you will need to pass the values to the constructor of the class before you can initialise it.
Hence your ResponseTemplateVO data class will become like this
data class ResponseTemplateVO(
var userRegistration: UserRegistration?=null,
var department: Department?=null)
Now since we have already assigned null as the default value. Now you can initialise the data class and it creates the data class with the values set to null and you do not need to pass any value for params to the constructor. Now you can access each of the variables and set the respective data into them.

How to save field as JSON with Spring Data R2DBC and Postgres

There are any way to save some field of entity as Json with spring-data-r2dbc?
Example:
#Table("A")
class A {
#Id
var id: String = "1"
var some: MutableMap<String, String> = mutableMapOf()
}
And table:
create table A (
id varchar(255) not null primary key,
some jsonb
)
I've looked at Convertors of Spring data R2DBC, but It is necessery to write separate convertor for every class. Does it possible to generate converters to Json dynamically in runtime for all classes inherited from some special interface or annotated by specific annotation?
Thanks!
annotation class StoreJson
...
#Bean
fun converters() = R2dbcCustomConversions(
Reflections().getTypesAnnotatedWith(StoreJson::class.java).map { clz ->
mutableListOf(
#WritingConverter
object : GenericConverter {
override fun getConvertibleTypes() = setOf(GenericConverter.ConvertiblePair(clz, Json::class.java))
override fun convert(source: Any?, p1: TypeDescriptor, p2: TypeDescriptor) =
Json.of(objectMapper.writeValueAsString(source))
},
#ReadingConverter
object : GenericConverter {
override fun getConvertibleTypes() = setOf(GenericConverter.ConvertiblePair(Json::class.java, clz))
override fun convert(source: Any?, p1: TypeDescriptor, p2: TypeDescriptor) =
objectMapper.readValue((source as Json).asString(), clz)
}
)
}.flatten().toMutableList()
)

Spring Boot Mongo findById returns null

I have a collection, with documents having a field named _id of type String, not generated manually.
I have been trying to get a document using its id.
val criteria = Criteria.where("_id").`is`("a2z3e44R")
val document = mongoTemplate.findOne(Query.query(criteria), MyDocument::class.java) // returns null
val criteria = Criteria.where("_id").`is`(ObjectId("a2z3e44R"))
val document = mongoTemplate.findOne(Query.query(criteria), MyDocument::class.java) // returns null
val document = mongoTemplate.findById("a2z3e44R", MyDocument::class.java) // returns null
mongoTemplate.findAll(MyDocument::class.java).first { myDocument ->
myDocument._id == "a2z3e44R"
} // OK...
MyDocument is
data class MyDocument(val _id: String, val name: String)
Trying to find a document by another field works.
An idea of what I could be missing or a workaround?
You should indicate the type of the id like this
public class Article {
#MongoId(value = FieldType.OBJECT_ID)
private String id;
private String title;
private String desc;
}
Try mark _id with annotation #Id. The #Id annotation is used to specify the identifier for Spring.
data class MyDocument(#Id val _id: String, val name: String)
Ypu could define in your repository:
public interface MyDocumentRepository extends MongoRepository<MyDocument, String> {
Pets findBy_id(ObjectId _id);
}
and use it :
myDocumentRepository.findBy_id("a2z3e44R");
for more info see
or
ObjectId objID = new ObjectId("a2z3e44R");
query.addCriteria(Criteria.where("_id").lt(objID));
like this other answer link

What is the most convenient way to deal with nested objects in Room?

I want to save the server’s response in database (class Parent). The json has nested object, which also should be saved in database in new table (class Nested). The problem is what I don’t know how to write class Parent and ParentDao to make it use NestedDao
#Entity
data class Parent(
#PrimaryKey(autoGenerate = true)
var id: Long? = null,
#SerializedName(«nested»)
val homeTeam: Nested,
//other fields
)
#Entity
data class Nested(
#PrimaryKey(autoGenerate = true)
var nestedId: Long? = null,
#SerializedName("name")
val name: String,
//other fields
)
#Dao
interface ParentDao {
#Query("SELECT * FROM parent»)
fun getData(): Single<List<Parent>>
#Insert
fun insert(matches: List<Parent>)
}
This gives me an error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
So, what should I do to save and query Parent with Nested at once?
I don't know if you've succeed or not, but here is my answer I hope it'll help you.
That's what I used for my project and what is recommended for Room in Android docs
#Entity
data class Parent(
#PrimaryKey(autoGenerate = true)
var id: Long? = null,
#Embedded #ColumnInfo(name= "nested")
val homeTeam: Nested,
//other fields
)
data class Nested(
#PrimaryKey(autoGenerate = true)
var nestedId: Long? = null,
#ColumnInfo(name= "name")
val name: String,
//other fields
)
#Dao
interface ParentDao {
#Query("SELECT * FROM parent»)
fun getData(): Single<List<Parent>>
#Insert
fun insert(matches: List<Parent>)
}

Resources