Kotlin Exposed Many-to-Many Jackson Infinite recursion (StackOverflowError) - kotlin

I have done the many-to-many reference in ExposedBD (kotlin) as follows in the wiki:
https://github.com/JetBrains/Exposed/wiki/DAO#many-to-many-reference
However, there is a problem of Infinite Recursion (Jackson) when I am trying to return a list of objects in my API (Javalin).
So, I would like to know how to put the annotation #jsonIgnore or if there are other alternative solutions in this case. Here is the mapping:
// many-to-many Actor--StarWarsFilms
// Actor Entity
object Actors: IntIdTable() {
val firstname = varchar("firstname", 50)
val lastname = varchar("lastname", 50)
}
class Actor(id: EntityID<Int>): IntEntity(id) {
companion object : IntEntityClass<Actor>(Actors)
var firstname by Actors.firstname
var lastname by Actors.lastname
}
// StarWarFilm Entity
object StarWarsFilms : IntIdTable() {
val sequelId = integer("sequel_id").uniqueIndex()
val name = varchar("name", 50)
val director = varchar("director", 50)
}
class StarWarsFilm(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<StarWarsFilm>(StarWarsFilms)
var sequelId by StarWarsFilms.sequelId
var name by StarWarsFilms.name
var director by StarWarsFilms.director
var actors by Actor via StarWarsFilmActors
}
// Intermediate table
object StarWarsFilmActors : Table() {
val starWarsFilm = reference("starWarsFilm", StarWarsFilms).primaryKey(0)
val actor = reference("actor", Actors).primaryKey(1)
}

Related

what is the equivalent of python id in kotlin?

In python you can get a unique numeric ID for any object via id(object):
person = Person()
person_id = id(person)
What is the equivalent function in Kotlin?
Classes in Kotlin do not automatically have a unique ID. As mentioned in the comments, you can get the identityHashCode. It is not guaranteed unique, but in practice if you are just using it to compare items in a log, it is probably sufficient.
class Person() {
val id: Int get() = System.identityHashCode(this)
}
If you need unique IDs, you could assign them at construction time using a counter in a companion object.
class Person() {
val id: Long = nextId
companion object {
private var nextId: Long = 0L
get() = synchronized(this) { ++field }
set(_) = error("unsupported")
}
}
// Or simpler on JVM:
class Person() {
val id: Long = idCounter.getAndIncrement()
companion object {
private val idCounter = AtomicLong(1L)
}
}
Or if you are on JVM, you can use the UUID class to generate a statistically unique ID for each class as it is instantiated, but this is probably not very useful just for logging.
class Person() {
val id = UUID.randomUUID()
}

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.

Bind dirty properties of different view-models

I have a tornadoFX application following the MVVM pattern with the model:
data class Person (
val name: String,
val cars: List<Car>
)
data class Car (
val brand: String,
val model: String
)
The application defines the following view:
There is a list-view that lists all persons. Besides the listView is a details-view with a text-field for the person´s name and a table-view for the person´s cars.
A double click on a car entry in the table opens a dialog, in which one can edit the car´s properties.
I want, that if I open the car-details and edit an entry, the changes will be reflected in the table-view. Since i can´t alter the Car-model (which is an immutable type) by adding fx-properties, i came up with the following view-model:
class PersonViewModel(): ItemViewModel<Person> {
val name = bind(Person::name)
val cars = bind { SimpleListProperty<CarViewModel>(item?.cars?.map{CarViewModel(it)}?.observable()) }
override fun onCommit {
// create new person based on ViewModel and store it
}
}
class CarViewModel(item: Car): ItemViewModel<Car> {
val brand = bind(Car::name)
val model = bind(Car::model)
init {
this.item = item
}
}
This way, if double-click on a car-entry in the table-view and open the car-detail-view, an update on the car will be directly reflected in the table-view.
My Problem here is, that I can´t find a way to bind the dirty properties of all my CarViewModels in the table to the PersonViewModel. So if I change a car, the PersonViewModel is not marked as dirty.
Is there a way to bind the dirty-properties of PersonViewModel and CarViewModel? (And also rebind them, if another person is selected).
Or is there even a better way to define my view-models?
I've made a change to the framework to allow ViewModel bindings towards lists to observe ListChange events. This enables you to trigger the dirty state of a list property by altering the list somehow. Merely changing a property inside an item in the list will not trigger it, so in the following example I just get the index of the Car before committing, and reassigning the Car to the same index. This will trigger a ListChange event, which the framework now listens for.
The important action happens in the Car dialog save function:
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
The index of the car is recorded before the values are committed (to make sure that equals/hashCode matches the same entry), then the newly committed item is inserted in the same index, thus triggering a change event on the list.
Here is a complete example, using mutable JavaFX properties, since they are the idiomatic JavaFX way. You can pretty easily adapt it to using immutable items, or use wrappers.
class Person(name: String, cars: List<Car>) {
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
val carsProperty = SimpleListProperty<Car>(FXCollections.observableArrayList(cars))
var cars by carsProperty
}
class PersonModel : ItemViewModel<Person>() {
val name = bind(Person::nameProperty)
val cars: SimpleListProperty<Car> = bind(Person::carsProperty)
}
class Car(brand: String, model: String) {
val brandProperty = SimpleStringProperty(brand)
var brand by brandProperty
val modelProperty = SimpleStringProperty(model)
var model by modelProperty
}
class CarModel(car: Car? = null) : ItemViewModel<Car>(car) {
val brand = bind(Car::brandProperty)
val model = bind(Car::modelProperty)
}
class DataController : Controller() {
val people = FXCollections.observableArrayList<Person>()
init {
people.add(
Person("Person 1", listOf(Car("BMW", "M3"), Car("Ford", "Fiesta")))
)
}
}
class PersonMainView : View() {
val data: DataController by inject()
val selectedPerson: PersonModel by inject()
override val root = borderpane {
center {
tableview(data.people) {
column("Name", Person::nameProperty)
bindSelected(selectedPerson)
}
}
right(PersonEditor::class)
}
}
class PersonEditor : View() {
val person: PersonModel by inject()
val selectedCar : CarModel by inject()
override val root = form {
fieldset {
field("Name") {
textfield(person.name).required()
}
field("Cars") {
tableview(person.cars) {
column("Brand", Car::brandProperty)
column("Model", Car::modelProperty)
bindSelected(selectedCar)
onUserSelect(2) {
find<CarEditor>().openModal()
}
}
}
button("Save") {
enableWhen(person.dirty)
action {
person.commit()
}
}
}
}
}
class CarEditor : View() {
val car: CarModel by inject()
val person: PersonModel by inject()
override val root = form {
fieldset {
field("Brand") {
textfield(car.brand).required()
}
field("Model") {
textfield(car.model).required()
}
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
}
}
}
The feature is available in TornadoFX 1.7.17-SNAPSHOT.

How do I search from multiple tables with DAO?

Say I have tables like this:
object Leagues : IntIdTable() {
val name = varchar("name", 50).uniqueIndex()
}
object Matches: IntIdTable() {
val game = reference("game", Games)
}
object Users: IntIdTable() {
val name = varchar("name", 50).uniqueIndex()
}
object Bets: IntIdTable() {
val match = reference("match", Matches)
val user = reference("user", Users)
}
Daos are in the lines of:
class Bet(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Bet>(Bets)
var match by Bets.match
var user by Bets.user
}
How do I write the dao or the query for the Bets class so I can query "give me all bets player X has made in league Y". Bet.find { (user eq X) and (/* what here to get the leagues table ? */) }
val bets = Bet.wrapRows(
Bets.innerJoin(Matches).innerJoin(Leagues).select {
Bets.user eq X.id and (Leagues.name eq "Y"
}
).toList()

Cannot save data model that contains List<Model> with Room ORM Kotlin

I have a problem with Room ORM working on Kotlin. My task is having ability to save and get data models RouteTemplateModel, that contains list of addresses of type AddressModel and object of class RouteModel that contains title of the specific route. Here is my code:
AddressModel.kt
#Entity(foreignKeys = arrayOf(
ForeignKey(entity = RouteModel::class,
parentColumns = arrayOf("routeId"),
childColumns = arrayOf("parentId"))))
data class AddressModel(
#PrimaryKey(autoGenerate = true)
var addressId: Long,
var parentId: Long,
var street: String,
var house: String,
var entrance: String,
var title: String){
constructor(): this(0, 0, "", "", "", "")
}
RouteModel.kt
#Entity
data class RouteModel(
#PrimaryKey(autoGenerate = true)
var routeId: Long,
var title: String) {
constructor() : this(0, "")
}
Here is my simple models, I found in documentation of Room that for creating relations between models I need to use #ForeignKey and #Relation
So with code samples in doc and tutorials I create RouteTemplateModel that contains object of RouteModel and list of AddressModels. Here is the class
RouteTemplateModel
class RouteTemplateModel{
private var id: Long = 0
#Embedded
private var routeModel: RouteModel = RouteModel()
#Relation(parentColumn = "routeId", entityColumn = "parentId")
private var addressList: List<AddressModel> = listOf()
constructor()
constructor(id: Long, routeModel: RouteModel, title: String,
addressList: List<AddressModel>){
this.id = id
this.routeModel = routeModel
this.addressList = addressList
}
fun getId(): Long{
return id
}
fun getRouteModel(): RouteModel{
return routeModel
}
fun getAddressList(): List<AddressModel>{
return addressList
}
fun setId(id: Long){
this.id = id
}
fun setRouteModel(routeModel: RouteModel){
this.routeModel = routeModel
}
fun setAddressList(addressList: List<AddressModel>){
this.addressList = addressList
}
}
So what`s a problem? I am getting such errors:
Error:The columns returned by the query does not have the fields [id]
in com.innotech.webcab3kotlin.model.RouteTemplateModel even though
they are annotated as non-null or primitive. Columns returned by the
query: [routeId,title]
And
Error:Type of the parameter must be a class annotated with #Entity or
a collection/array of it.
It is a real problem, because if my trying to fix first error and annotate in RouteTemplateModel id variable to return this column too, I need annotate class as Entity (like in second error), but when I do it I am getting an error
Error:Entities cannot have relations.
Here is AppDatabase.kt
#Database(entities = arrayOf(RouteModel::class, AddressModel::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getRouteDao(): RouteDao
}
and RouteDao.kt
#Dao
interface RouteDao {
#Query("SELECT routeId, title FROM RouteModel")
fun getAll(): List<RouteTemplateModel>
#Insert
fun insertAll(vararg models: RouteTemplateModel)
#Delete
fun delete(model: RouteTemplateModel)
}
Thats really confusing. Please, help me)
Your "parentId" column is capable of holding long value only, make its type to "Text" then create a TypeConverter from "List" to String and vice a versa for reference please have a look at link .