Marcelus Trojahn
Sep 6 · 5 min read

UML to Entity

Hello everyone and welcome back.

On today’s post I’m going to continue the development of a finance management website. For those who are just finding me, this is not your usual tutorial. This is a log of sorts of my activities and thinking while I develop this application. You will find novice to advanced information here and it will come slowly because, most of all, I want to log my thinking and practice writing.


Our entities

On my last post I guide us on creating a Ktor project. If you wanna have a look, the link is here and on the previous post I created a very simple UML to represent the early stages of our classes. Also here if you want to check it out.

Today we are going to create those classes in our project following what we designed in the UML. Since I know you don’t want to read this forever, I’m just going to add the 3 main classes to our project and make sure Ebean recognizes them and they are persistable to the database. For that, I’m going to use a simple test class that will also briefly introduces us to TDD.

Do you remember this, right? This a representation of our main classes: Person and Company. Nearly all other classes on our application will be, somehow, related to them.

Since I want to demonstrate some abstraction throughout this project, I designed them to be a “child” of BaseEntity which will hold every property that is shared by the two classes. Some ORMs call entities like BaseEntity a MappedSuperClass. Depending on the ORM we use, we can either create a single table that will store all companies and people or two, nearly identical tables, for Person and Company.

I’m going with the single table approach for this project.

Unlike Java, Kotlin allow us to create several classes per file. For the sake of simplicity, I’m going to group all entities related to Person, Company and the BaseEntity in the same file.

For this purpose, I’ve created a new file called BaseEntityRelated.kt in my models package. The file contents is as follows:

package com.marcelustrojahn.modelsimport io.ebean.Finder
import java.time.LocalDate
import javax.persistence.*
@Entity
@Inheritance
abstract class BaseEntity(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Long? = null,
var name: String = "",
var enabled: Boolean = true,
var addressStreet: String = "",
var addressNumber: Int = 0,
var addressDistrict: String = "",
var addressCity: String = "",
var addressState: String = "",
var addressZip: String = "",
var birth: LocalDate = LocalDate.MIN,
@Version
var version: Long = 0
)
@Entity
class Person(
var cpf: String? = "",
var rg: String? = "",
) : BaseEntity()
@Entity
class Company(
var officialName: String? = "",
var cnpj: String? = "",
var ie: String? = "",
var im: String? = ""
) : BaseEntity()

For those who already are familiar with Kotlin you may notice I’m not using data classes. Many developers like to use data classes (myself included) since will provide them methods like toString() or equals() automatically. I’m not using them here because Ebean enhances the classes and apparently this conflicts with the data classes. I’ve had some weird problems with that (not going into detail) so I just dropped data classes for now.

Ok, so let me explain the classes a bit.

BaseEntity contains all properties that are shared by Person and Company. The @Id and @GeneratedValue annotations tell Ebean that in the database, id will be the primary key and it will be auto incremented.

The @Version annotation tells Ebean that column will be used to versioning the records of the database, meaning, every time it’s going to save a record it will first check if the version is the same. If the version is different, someone else already changed that record before you and it will fail to persist.


Preparing the Test

Let’s make sure our entities are properly configured. For that, we need to create a file called application.properties that Ebean will grab it’s configuration. That file will be created in /src/test/resources. Creating the file there will make sure it will only be picked up during tests. Later there will be another version of that file for production which will be saved in another folder of our project. We’ll get there.

This is our /src/test/resources/application.properties

ebean.packages=com.marcelustrojahn.models
ebean.db.ddl.generate=true
ebean.db.ddl.run=true
datasource.db.username=sa
datasource.db.password=
datasource.db.databaseUrl=jdbc:h2:file:~/monies_db_test;AUTO_SERVER=true
datasource.db.databaseDriver=org.h2.Driver

Let me go through the file real quick.

  • ebean.packages tell Ebean where to find my models.
  • the DDL options tell Ebean to recreate my database from scratch every time the tests are run.
  • username, password and driver are pretty much self explanatory
  • databaseUrl tell how Ebean should connect to the database. For the testing, I’m using a local H2 database. I like to use it as a file instead of memory so I can inspect it if I need to debug something. AUTO_SERVER=true allows me to connect to the database from another client even if the file is in use.

The Test

The test will be a new file in /src/test/kotlin. It could be anywhere in here but I like to keep it a little organized so I put them in a directory with the same name as the package of the class I’m testing. So, in this case, the final path is /src/test/kotlin/com/marcelustrojahn/models/BaseEntityTests.kt

package com.marcelustrojahn.modelsimport io.ebean.Ebean
import io.kotlintest.matchers.shouldBe
import io.kotlintest.matchers.shouldNotBe
import io.kotlintest.specs.FreeSpec
class BaseEntityTests: FreeSpec() {
init {
// remember the database will always be empty in the beggining of tests
// so the id will be 1
"should be able to save a Person" {
val person = Person().apply { name = "John" }
Ebean.save(person)
person.id shouldNotBe null // id is filled on save
}
"should be able to select the Person from the database" {
val person = Ebean.find(Person::class.java).where().idEq(1).findOne()
person shouldNotBe null
}
"should be able to update the Person" {
val person = Ebean.find(Person::class.java).where().idEq(1).findOne()
person?.name = "Jack"
Ebean.save(person)
Ebean.find(Person::class.java).where().idEq(1).findOne()?.name shouldBe "Jack"
}
"should be able to delete the Person" {
Ebean.delete(Person::class.java, 1)
Ebean.find(Person::class.java).where().idEq(1).findOne() shouldBe null
}
}
}

This is a very basic test for now. I just want make sure Eban can talk to the database. As you can see, kotlintest syntax is very easy to understand and I really love it. Here, I test if I’m able to create a new record, select, update and delete it. Once we progress with this project, I’ll show even better ways to interact with the database with Ebean.

This is the result of these tests:

On the next post, I’ll show how we can enhance our models and make querying even easier with Ebean.

See you soon!


Originally published at https://www.marcelustrojahn.com on September 6, 2019.

Marcelus Trojahn

Written by

dev. likes: kotlin, javascript, vue, react in that order. dbs: relational. also tries to write about dev stuff.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade