FYI: JPA/Ebean ORM, Maven working example on github at


#1

FYI

For those interested I have an ‘example maven project for using Ebean ORM with Kotlin’ here:
https://github.com/ebean-orm/avaje-ebeanorm-examples/tree/master/e-kotlin-maven

Any feedback is welcome, I’ve only being using Kotlin for a few hours so I’m the proverbial Kotlin newbie and ‘tough love’ comments are welcome.  I’ll look to maintain this example project going forward attempting to show ‘best practice’ and add examples that showcase Kotlin language features in the context of using Ebean ORM.

I found converting the java code over to Kotlin very easy and it all worked apart from one thing which I’ll look into (Transaction runnable using a anonymous java inner class - I converted from java manually so I probably stuffed something up here). I was most interested to initially test that the bytecode enhancement of the JPA annotated entity beans worked and that was all fine although I have yet to test non-default constructors and some other edge cases.

To the people working hard on Kotlin - Keep up the great work !! I think it is very impressive and very ‘sellable’ especially to teams with large existing java code bases and tools.

Cheers, Rob.

Background:

I’m the main contrbutor to Ebean ORM so I can make it ‘Kotlin friendly’ as necessary (perhaps add @NotNull @Nullable annotations)
Ebean ORM uses JPA annotations for mapping but it is not a JPA implementation. I’d describe Ebean as - Sessionless / No EntityManager / Query language supports ‘partial objects’


#2

Thanks Rob, I'm very pleased to see such experiments!

Small hints about the code:

  1. SAM conversions

    // // How to do this better in Kotlin ??
    // Ebean.execute(TxRunnable() {
    //   fun run() {

    Since TxRunnable is a SAM interface, you can write something like this:

    Ebean.execute {
      /* do something /
    }


    Or, if you need it to be more explicit:

    Ebean.execute(TxRunnable {
      / do something */
    })

  2. with() function

Instead of writing

  val order = Order()
  order.status = Status.COMPLETE
  order.customer = customer;
  order.add(product1, 3, 10.50)
  order.add(product3, 40, 2.10)

  order.save()


You can do

val order = with(Order()) {   status = Status.COMPLETE // it's the same as this.status = Status.COMPLETE   this.customer = customer   add(product1, 3, 10.50)   add(product3, 40, 2.10)

  save()

  this // return the Order instance
}


Here, with() is a function that takes an obejct and a lambda that has that object “imported” as the default receiver (i.e. ‘this’ means the result of Order() in the example above).
Note: if you want to refer to some other ‘this’ which is in scope (e.g. ‘this’ of the class you are in), you can @-qualify it with the name (e.g. class name).


#3

Cool! Just watch out for the final vs non-final thing I wrote about yesterday if your bytecode enhancer works by subclassing. It's easy to miss that because the Java-to-Kotlin conversion doesn't preserve the openness of the Java classes (which is arguably the right thing in most cases).


#4

Thanks Andrey and Mike,

I applied those suggested changes.  Thanks Mike, yes Ebean doesn’t use subclassing so all good there.

I have extended my testing to include:

  • Transactional enhancement
  • Using SAM closure for findVisit() … (an iterator/cursor style approach to iterate large resultSets using a closure)  
  • Used inheritance with the Singleton Finder helpers … good value there compared to the Java approach
  • Adding/using extension methods

It all worked very well.  I’ve tried to tidy up the code a bit and the end result looks very nice (readable, succient and yet explicit at the same time).

Cheers, Rob.