Parcelize and ObjectBox clash - kotlin

I'm using kotlin and ObjectBox in my application. My object box entity looks something like
#Entity
class Order {
#Id var id: Long = 0
lateinit var customer: ToOne<Customer>
}
#Entity
class Customer {
#Id var id: Long = 0
#Backlink
lateinit var orders: List<Order>
}
But when I use #Parcelize, the properties are being ignored in the parcel. How do I use #Parcelize but still include these properties? I tried overriding writeToParcel but I am not allowed to override it due to #Parcelize.

According to docs, you have to declare all properties in primary constructor, which should be serialized via #Parcelize. All other ones are ignored.
ObjectBox doesn't support ToOne so you have to write custom Parceler. In the end your solution should look like this:
#Entity
#Parcelize
#TypeParceler<ToOne<Customer>, ToOneCustomerParceler>
class Order(
#Id var id: Long = 0,
var customer: ToOne<Customer>
) : Parcelable
#Entity
#Parcelize
class Customer(
#Id var id: Long = 0,
#Backlink var orders: List<Order>
) : Parcelable
object ToOneCustomerParceler : Parceler<ToOne<Customer>> {
override fun create(parcel: Parcel): ToOne<Customer> {
//Somehow recreate ToOne instance
...
}
override fun ToOne<Customer>.write(parcel: Parcel, flags: Int) {
val customer = target
...
}
}
Also don't forget to include correct dependencies:
dependencies {
compile "io.objectbox:objectbox-android:$objectboxVersion"
compile "io.objectbox:objectbox-kotlin:$objectboxVersion"
}
P.S. Use different models for each purpose (#Entity and #Parcelize) even if both are the same. It is much easier to manage them since you separate your intentions into 2 models, rather than trying to push everything into single one.

Related

How to adjust table name in Kotlin Exposed on runtime

We are using database table names which are prefixed with environment names e.g:
instead of just 'Cities' we have 'ci_Cities', 'dev_Cities' and 'prod_Cities'.
The problem is that Schema definitions are based on Kotlin objects, which is nice in an usage, but doesn't allow me to simply inject table prefix in e.g. constructor.
So the question is how to implement such a functionality in Kotlin-Exposed?
In the end I have found solution, which seems to be quite elegant.
But I think, that some improvements could be done also in Kotlin Exposed, so that in most cases solution is more concise.
City.kt
data class City(val id: Int, val name: String, val timestamp: Instant)
Schema.kt
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.`java-time`.timestamp
class CitiesSchema(environment: String) {
val cities = CitiesTable(environment)
}
class CitiesTable(environment: String) : Table(environment + "_Cities") {
val id = varchar("id", 99)
val name = varchar("name", 99)
val timestamp = timestamp("timestamp")
}
CitiesRepository.kt
class CitiesRepository(dataSource: DataSource, private val schema: CitiesSchema) {
private val database = Database.connect(dataSource).defaultFetchSize(10000)
override fun save(city: City): City {
transaction(database) {
schema.cities.insert {
it[schema.cities.id] = city.id
it[schema.cities.name] = city.name
it[schema.cities.timestamp] = city.timestamp
}
}
return city
}
Then in e.g. Spring you can instantiate your schema:
#Bean
public CitiesSchema schema(#Value("${spring.application.env}") String environment) {
return new CitiesSchema(environment);
}
It would be nice to have in Kotlin Exposed ability to rename tables/columns on runtime. Then it would be possible to access Kotlin objects without additional ceremonies.
Such a feature could look like in Jooq:
https://www.jooq.org/doc/3.14/manual-single-page/#settings-render-mapping

Non null relationships in primary constructor of entity

I'm creating application with Spring Data Neo4j and Kotlin. I use standard kotlin way to declare entities (class with primary constructor). Everything worked fine until I wanted to create simple, one-to-many and mandatory relationship between my entities. When I'm calling .findAll() on my repository I get Parameter specified as non-null is null: method ...model.Campaign.<init>, parameter client.
I tried to call .findAll(depth = 1) to load related entities to my entity but that didn't help.
#NodeEntity
class User(var name: String)
{
#Id #GeneratedValue
var id: Long? = null
}
#NodeEntity
class Campaign(
var name: String,
#Relationship(type = "CLIENT", direction = Relationship.OUTGOING)
var client: User)
{
#Id #GeneratedValue
var id: Long? = null
}
interface CampaignRepository : Neo4jRepository<Campaign, Long>
//...
campaignRepository.save(Campaign("C1", user))
campaignRespository.findAll()
Of course, I can just declare var client: User? as nullable and everything is fine. But, since in my model I will have both mandatory and optional relationships I want to know if there's a way to overcome this.
I found a solution, but not very elegant:
#NodeEntity
class Campaign(
var name: String,
client: User?)
{
#Id #GeneratedValue
var id: Long? = null
#Relationship(type = "CLIENT", direction = Relationship.OUTGOING)
lateinit var client: User
init
{
client?.let { this.client = it }
}
}

Deserialize a nested json field with Jackon in Kotlin

I've already deserialized some nested field in the past in Java, following instructions from https://www.baeldung.com/jackson-nested-values (section 5) :
#JsonProperty("brand")
private void unpackNested(Map<String,Object> brand) {
this.brandName = (String)brand.get("name");
Map<String,String> owner = (Map<String,String>)brand.get("owner");
this.ownerName = owner.get("name");
}
ownerName being a field in the bean.
Now, I need to do something similar in Kotlin, but I am not happy with what I have so far. Assuming I have a MyPojo class that has a createdAt field, but in the JSON that represents it, the field is nested under a metadata attribute:
data class MyPojo(var createdAt: LocalDateTime = LocalDateTime.MIN) {
#JsonProperty("metadata")
private fun unpackNested(metadata: Map<String, Any>) {
var createdAtAsString = metadata["createdAt"] as String
this.createdAt = LocalDateTime.parse(createdAtAsString,DateTimeFormatter.ISO_DATE_TIME)
}
}
One of the thing I don't like here is that I am forced to make createdAt a var, not a val.
Is there a Kotlin trick to make things overall better here?
For the sake of simplicity, I used Int as type for createdAt.
You could do it like this:
class JsonData(createdAt: Int = 0) {
private var _createdAt: Int = createdAt
val createdAt: Int
get() = _createdAt
#JsonProperty("metadata")
private fun unpackNested(metadata: Map<String, Any>) {
_createdAt = metadata["createdAt"] as Int
}
}
createdAt will be a parameter with a default value. Since a data classe's constructor can only have properties (var/val) you will loose the advantages of a data class (toString() out of the box etc.).
You will assign this parameter to a private var _createdAt when the class is instantiated.
The only thing that will be exposed to the outside is a property without a backing field createAt (just a getter in Java terms). So, _createdAt cannot be changed after instantiation.
There are two cases now:
If you instantiate the class, _createdAt will be set to the value you specify.
If Jackson instantiates the class the value of _createdAt will be overwritten by the unpackNested call.
Here is an example:
val jsonStr = """{
"metadata": {
"createdAt": 1
}
}
""".trimIndent()
fun main() {
val objectMapper = ObjectMapper()
// Jackson does instantiation
val jsonData = objectMapper.readValue(jsonStr, JsonData::class.java)
// you do it directly
JsonData(5)
}

What is the benefit of using primarykey and references method in class jooq

I'm start the learn jooq. I have mssql server. I create some class the represent table on my server. But I don't understand what is the benefit when I was using getPrimaryKey and getReferences methods in my table class?
class User : TableImpl<Record>("users") {
companion object {
val USER = User()
}
val id: TableField<Record, Int> = createField("id", SQLDataType.INTEGER)
val name: TableField<Record, String> = createField("name", SQLDataType.NVARCHAR(50))
val countryId: TableField<Record, Short> = createField("country_id", SQLDataType.SMALLINT)
override fun getPrimaryKey(): UniqueKey<Record> = Internal.createUniqueKey(this, id)
override fun getReferences(): MutableList<ForeignKey<Record, *>> =
mutableListOf(Internal.createForeignKey(primaryKey, COUNTRY, COUNTRY.id))
}
class Country : TableImpl<Record>("country") {
companion object {
val COUNTRY = Country()
}
val id: TableField<Record, Short> = createField("id", SQLDataType.SMALLINT)
val name: TableField<Record, String> = createField("name", SQLDataType.NVARCHAR(100))
override fun getPrimaryKey(): UniqueKey<Record> =
Internal.createUniqueKey(this, id)
}
The generated meta data is a mix of stuff that's useful...
to you, the API user
to jOOQ, which can reflect on that meta data for a few internal features
For instance, in the case of getPrimaryKey(), that method helps with all sorts of CRUD related operations as you can see in the manual:
https://www.jooq.org/doc/latest/manual/sql-execution/crud-with-updatablerecords/simple-crud
If you're not using the code generator (which would generate all of these methods for you), then there is no need to add them to your classes. You could shorten them to this:
class User : TableImpl<Record>("users") {
companion object {
val USER = User()
}
val id: Field<Int> = createField("id", SQLDataType.INTEGER)
val name: Field<String> = createField("name", SQLDataType.NVARCHAR(50))
val countryId: Field<Short> = createField("country_id", SQLDataType.SMALLINT)
}
However, using the code generator is strongly recommended for a variety of advanced jOOQ features which you might not get, otherwise.

How to show pojo properties in TornadoFX tableview?

I'm writing a very simple TornadoFX table demo, trying to display the properties of some pojos in a table, but the cells are all empty.
The main code is:
data class User(val id: Int, val name: String)
private val data = listOf(User(111, "AAA"), User(222, "BBB"), User(333, "CCC"), User(444, "DDD")).observable()
class HelloWorld : View() {
override val root = vbox {
tableview(data) {
column("id", User::id.getter)
column("name", User::name.getter)
}
}
}
I use User::id.getter to make it compiling, but the cells are empty.
I did a lot of search, but can't find code to work with current latest tornado (1.7.16)
Here is a complete demo for this: https://github.com/javafx-demos/tornadofx-table-show-pojo-demo
You need to reference the property, not the getter, ie. User::id. To reference immutable properties you need to use the readonlyColumn builder:
readonlyColumn("id", User::id)
readonlyColumn("name", User::name)
That said, you really should use JavaFX properties in your domain objects instead. Not doing so in a JavaFX based application just makes everything harder, and you loose out on a lot of benefits, or at the very least you have to jump through hoops.
Here is the complete application written with observable JavaFX properties. Note that you would then access the idProperty and nameProperty properties instead. With this approach, changes to the underlying data item would automatically be visible in the tableview as well:
class User(id: Int, name: String) {
val idProperty = SimpleIntegerProperty(id)
var id by idProperty
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
}
private val data = listOf(User(111, "AAA"), User(222, "BBB"), User(333, "CCC"), User(444, "DDD")).observable()
class HelloWorld : View() {
override val root = vbox {
tableview(data) {
column("id", User::idProperty)
column("name", User::nameProperty)
}
}
}