I'm experimenting with jOOQ and Kotlin and seen some tutorials and docs and it looks really nice.
But if there is something very annoying with jOOQ is the code generation. It seems too complex, and eventually impossible to maintain. I decided to create my own table models (similar to how hibernate works).
I created two table models:
User
data class User(
val id: String = UUID.randomUUID().toString(),
val name: String,
val email: String,
val password: String? = null
) {
companion object {
val TABLE: Table<Record> = DSL.table("user")
val ID: Field<String> = DSL.field("id", String::class.java)
val USER_NAME: Field<String> = DSL.field("user_name", String::class.java)
val EMAIL: Field<String> = DSL.field("email", String::class.java)
val PASSWORD: Field<String> = DSL.field("password", String::class.java)
}
}
Followers
data class Followers(
val id: String,
val followerId: String,
val userId: String
) {
companion object {
val TABLE: Table<Record> = DSL.table("followers")
val ID: Field<String> = DSL.field("id", String::class.java)
val FOLLOWER_ID: Field<String> = DSL.field("follower_id", String::class.java)
val USER_ID: Field<String> = DSL.field("user_id", String::class.java)
}
}
When I did some trivial SQL statements and it worked perfectly, but when I tried the next statement, I'm getting exception.
return dsl.select().from(u.TABLE)
.rightJoin(f.TABLE).on(u.ID.eq(f.FOLLOWER_ID))
.where(u.ID.eq(id)).fetch().into(User::class.java)
The expected statement from this code is:
select *
from user u
right outer join followers f
on u.id = f.follower_id
where u.id = 'e30919bf-5f76-11e8-8c96-701ce7e27f83';
But the statement I got from this code is:
select *
from user
right outer join followers
on id = follower_id
where id = 'e30919bf-5f76-11e8-8c96-701ce7e27f83'
And of course, this givse me (rightfully) the error Column 'id' in where clause is ambiguous
It raises a few questions:
Is there a better way to declare table model without code generation.
Why the DSL select does not transform to proper SQL statement? What I'm doing wrong?
First off, some word of advice on your reluctance to use code generation:
i seems too complex, and eventually impossible to maintain.
so, i decided to create my own table models (similar to how hibernate works).
You're (probably) going down a long path of pain and suffering. First off, you will already now need to think of database migrations, which are best done using your database's DDL language. This means, your database model of your data should be more important to you in the long run, than your client model. In fact, your client model is a copy of your database model, not something you'd like to maintain independently. With this mindset, it is more reasonable to have a code generator generate your client model from the database model, not vice versa.
Sure, Hibernate makes the client first approach easy as well, when you start a project. Yet, once you go to production, you will have to migrate your database, and then this model will break. You're back to database first, and it's worth setting up everything already now.
So, no. Code generation might introduce some complexity now, but it will be much more easy to maintain down the road, than you creating your own table models.
I've written up a longer blog post about this topic, here.
Regarding your specific questions:
return dsl.select().from(u.TABLE)
.rightJoin(f.TABLE).on(u.ID.eq(f.FOLLOWER_ID))
.where(u.ID.eq(id)).fetch().into(User::class.java)
the expected statement from this code is: [...]
Well, that depends on what u and f are. You cannot just rename your Kotlin references to your table and expect jOOQ to know what they mean. I.e. you probably created the references as follows:
val u = User.TABLE;
val f = Follower.TABLE;
If that's how you created the reference, then the two things are the same thing by identity. jOOQ doesn't magically reverse engineer your Kotlin code to find out that you meant to alias your table. You have to tell jOOQ:
val u = User.TABLE.as("u");
val f = Follower.TABLE.as("f");
But now you're not done. You constructed the User.TABLE reference using the plain SQL API, which means that jOOQ's runtime has no idea about the columns in that table. You cannot reference those columns anymore from the aliased table, because the type of the aliased table for plain SQL tables is Table<?>, not User.
You could, of course, create TableImpl instances and register all columns inside of your TableImpl instance - just like the code generator does. In that case, you would have tables and columns associated with them, and could use them type safely even with aliased tables.
All of this stuff is handled automatically by generated code, which again, I recommend you use with jOOQ. The main reason why anyone would not use the code generator with jOOQ is because the data model is dynamic, i.e. not known at compile time. Otherwise, you're just going to repeat tons of work that the code generator already does for you, automatically. And, as mentioned before, you will have much more work later on, when you start migrating your schema.
Related
I am new to kotlin and I am trying to loop through two list and insert email in people.
data class User(val id:String,val name: String,val email: String)
data class Person(val id:String,val name: String, val email: String="")
//Input
val users = listOf(User("1","John","john#a.com"),User("2","Doe","Doe#a.com"))
val people = listOf(Person("1","John"),Person("2","Doe"))
//expected
val userToPerson = listOf(Person("1","John","john#a.com"),Person("2","Doe","Doe#a.com"))
I am trying with this.
val map = people.map { it ->
{
val foundUser = users.find { user -> user.id == it.id }
if (foundUser != null) {
it.email = foundUser.email
}
}
}
map.forEach(System.out::print)
I am getting error for foundUser.isNotNull() here Unresolved reference: isNotNull
Updated with suggested:
() -> kotlin.Unit() -> kotlin.Unit
I am trying to convert a list of users to a list of people. They both have their ids as common.
I want to update people Person class corresponding to their user email.
All people do not have email. But all users have the email.
So, the final result would have people with email. If there is a person with no matching id, we can skip that data.
First, calling find for every person is not only a bit awkward to write, it's also (which is far worse) inefficient. (It takes time proportional to the square of the number of people, which means it will perform really badly as the number of people gets large.)
To fix that, I'd create a map from an ID to its User:
val usersById = users.associateBy{ it.id }
We can then look up users by their ID quickly, in a way which scales well.
Armed with that, the solution can be fairly straightforward. Here's one which creates new* Person objects:
val userToPerson = people.map{ person ->
val user = usersById[person.id]
if (user != null && user.email != person.email)
Person(person.id, person.name, user.email)
else
person
}
This solution is a little longer than necessary, but I hope it's easy to read and understand. It also avoids creating Person objects unless necessary, for efficiency. And when there's no corresponding User, it uses the existing Person.
* As the question is currently written, Person's fields are immutable, so the existing Person objects can't be updated with a new email address. That leads naturally into a functional style.
That's not necessarily a bad thing; immutability has many benefits, such as being easier to think about, and thread safety. It can also allow some compiler optimisations. However, if you're not careful, it can generate lots of temporary objects, which can can reduce efficiency (due to cache misses, constructor calls, and then more frequent garbage collections).
The alternative would be to make Person mutable, and do all the updates in-place — which is the traditional imperative style that most of us started from.
Both approaches are valid; which one you choose is a trade-off involving safety, maintainability, and performance — Kotlin supports both.
Consider the following simple data class being used as a Room #Entity
#Entity
data class SimpleDataClass(
val name: String,
val timestamp: String,
val isInvalid: Boolean = false,
#PrimaryKey(autoGenerate = true) val id:Int=0
)
The functionality I am trying to implement is that two SimpleDataClass objects should be considered same if all there properties are same regardless of their id (which is only added to the class structure because it's going to be stored in a database). Eg. the timestamp only stores the dayOfMonth and the user did the same thing over many months
Note that as database records, they should be considered separate since the user really did generate two records hence their different auto incremented primary keys.
One can do this by writing explicit equals that ignores id comparison. But then one also needs to write other comparison functions like hashCode, toString etc. which I am trying to avoid (let the compiler generate them)
To let the compiler ignore id , I followed the advise here
#Entity
data class SimpleDataClass(
val name: String,
val timestamp: String,
val isInvalid: Boolean = false,
) {
#PrimaryKey(autoGenerate = true)
var id: Int=0
}
Note that id is now a var to allow for its setter
This achieves what I was looking for except that it breaks copying. When a record read from the database as a SimpleDataClass object is copied (via simpleDataClass.copy()), its id isn't transferred to the copy. To remedy that I am using
old.copy(/*change some params, or not*/).also { it.id = old.id}
Neither method feels satisfactory. How to achieve this?
I am trying to get to grips with Kotlin and functional programming and failing on a pretty simple exercise.
I'll modify this a little so as to make it not too obvious that it is from a specific online course but I'm just trying to get started really and not trying to fool anyone...
I am working with 2 collections
data class Pet(val name: String)
data class Household (
val pet: Pet,
...
)
data class District(
val allPets: Set<Pet>,
val allHouseholds: List<Household>,
...)
I want to find all pets not in a household. It has to be returned as a Set
as I have been given this signature to play with
fun Locality.findFeralPets(): Set<Pet> =
I was going to do a filter operation but this returns a list and I can't see how to convert this to a set. Can anyone point me in the right direction ? It is very possible that filter is the wrong approach altogether!
allPets.filter { pet -> pet.name != "Bob" }
It's more efficient to do this in a different way, avoiding a separate conversion:
allPets.filterTo(HashSet()) { pet -> pet.name != "Bob" }
filter returns an Iterable which has an extension method on it called toSet which returns a Set. e.g.
allPets.filter { pet -> pet.name != "Bob" }.toSet()
I am starting with Scala. I have my method in the model:
def get_courses = {
DB.withConnection { implicit connection =>
val result = SQL("SELECT * FROM courses")
result
}
When I call it from Controllers I am doing this (I want to get a List):
val AllCourses = CourseModel.get_courses
// Transform the resulting Stream[Row] as a List[(String)]
val CoursesList = AllCourses().map(row =>
row[String]("name")
).toList
When I am trying to transform the stream[Row] in a List (from https://www.playframework.com/documentation/2.0/ScalaAnorm) I am getting error
could not find implicit value for parameter connection: java.sql.Connection
related with AllCourses() code.
Any ideas?
However it is strange because when I add all the same method
def get_courses = DB.withConnection { implicit connection =>
val result = SQL("SELECT * FROM courses")
// Transform the resulting Stream[Row] as a List[(String)]
val CoursesList = result().map(row =>
row[String]("name")
).toList
}
it works (as the https://www.playframework.com/documentation/2.0/ScalaAnorm example)
But I want to have them separated in Controllers...
I suspect the answer to your question is that the actual database connection does not get used when you simply define the sql statement. If you hover over the SQL call, you will see it does not require a DB connection. result(), or SQL("...")() however, does need a connection and this is where the actual call to the database will take place.
Can I humbly suggest that you write your controllers only to operate on your class objects, and not to interact with anorm/database-level artefacts. So for example, your model layer would have a method get_courses : List[Course] (or perhaps simply all because you will probably reference it as a method of the Course companion object, i.e. Course.all(), so using course in the method name is probably not necessary) which returns Course objects. Your controllers would call this as needed, and not need to worry whether the Course object is coming from a database, from a json file, or from anywhere.
I also find parsers very useful, so define a parser courseP for example, which creates Course objects and is then used wherever you need to read one or more Courses - with something like SQL("select ... from course").as(courseP *) for a list of Courses, or SQL("select ...").as(courseP.singleOpt) for an Option[Course].
I will be implementing a read-only web application in Play 2.1 (Scala). Since I'll only be doing reading and marshaling the data read to JSON I would like to avoid any other DSLs and mappings.
I've done similar projects in .NET/C# using dapper-dot-net and was very happy with the way things turned out. No fuss and not much boiler plate.
I am currently looking at:
anorm (anormtyped looks very promising too, but is probably to early to adopt. Avoiding the manual mapping of variables to case class constructor parameters seems awesome.)
prequel
slick - because it is supposed to be the primary way of doing SQL in 2.1 and mainly the plain SQL API
Slick is very good. Make sure you check out this short book about it - it's very good at explaining some basics. Along with the docs it would get you forward quickly. Also, note that the docs in github are better - the latest haven't been published yet.
Using the plain sql option is very well supported. With plain sql queries you don't much in terms of type checking, though. Otherwise, using Scala 2.10 doing a plain query is as easy as:
sql"select * from coffees where name = $name".as[Coffee]
This will even protect you from sql injction, as $name is not actually in the query. See the docs for more information.
I have a project on GitHub which is massively inspired by Dapper, called dbmapper
Advantages over Slick are:
no DSL -- you already know a good data DSL, it is called SQL
totally asynchronous
very little boilerplate code
Here is an example:
// Scala class that maps to the book table, with columns to match the class members
case class Book(
bookId: Int,
title: String,
retailPrice: BigDecimal,
publishDate: LocalDate)
// mapping function from table row to Book class,
// auto generated at compile time by Scala Macro
implicit def rowToBook: RowData => Book = (r) => DbCodeGenerator.rowToClass[Book](r)
// query returning future list of books,
// safe query interpolation, the maxPrice is converted to a query argument
val maxPrice = 11.99
val allBooksFuture = DbAsync.exec[Book](q"select * from book where retail_price < $maxPrice")
val oneBook: Future[Book] = DbAsync.execOne[Book](q"select * from book where book_id = 2")
// returns Future[Option[]]
val maybeOneBook: Future[Option[Book]] = DbAsync.execOneOrNone[Book](q"select * from book where book_id = -123")
If you know dapper from the dotnet world then dbmapper will feel strangely familiar!