Google Groups

Re: [play-framework] Re: [2.0] Ebean lazy loading

Timo Hoepfner Feb 22, 2012 3:01 AM
Posted in group: Play Framework
Thanks Guillaume, your comment brought me on the right track.


Why is the play.core.enhancers.PropertiesEnhancer.rewriteAccess enhancement not applied to Scala code?

Long story:

In case someone else is wondering, this is what is happening:

After Play has compiled classes, it will enhance the compiled byte code. This is happening in PostCompile in PlayCommands.scala.

1. It will add getters and setters for fields, if there aren't any in place yet (play.core.enhancers.PropertiesEnhancer.generateAccessors)
2. It will will rewrite classes that directly access fields to use the accessors insted (play.core.enhancers.PropertiesEnhancer.rewriteAccess)
3. If using Ebean, the Ebean enhancer will be applied to the classes configured via application.conf (e.g. "models.*")

The Ebean enhancer will scan classes markes with @Entity and add its own getters and setters which call additional code before the actual value is read from or set to the field.
This additional code enables lazy loading, dirty checking and optimistic concurrency checking.
Then it will (also) replace each direct field access with calls to the Ebean getters/setters.
As play already replaced all occurences of direct field access with a call to getters and setters in step 2, this should effectively only modify the getters and setters created manually or in step 1.


Employee employee=Employee.find.byId(1);

After Step 1&2, this will be converted to

Company company=employee.getCompany();

With Employee#getCompany() beeing something like

public Company getCompany(){

After step 3, the getter will be modified to be something like

public Company getCompany(){
return _ebean_get_company();

protected Company _ebean_get_company() {

The unexpected behaviour I came across, was that when I did something like in Java, lazy loading worked, but when doing in a view template it didn't.


Because the steps 1&2 above are only executed for classes that have Java source code that lives below the "app" folder of the project.
The view templates are converted to Scala sources by play and are put into target/scala-2.9.1/src_managed before compilation. The enhancement is not done for them.

What to do right now?

When using direct field access:
- Java code just works
- Don't rely on lazy loading in Scala code (including views!) 
- Never ever change values on Ebean entities from Scala code as there will be no dirty checking in place

Alternatively use property access:
- The bytecode enhancement will be done after the view templates are already compiled
- That means you cannot use the acessors generated in step 1 of the enhancement process in view templates, as they are not there yet
- So manually add getters and setters to the Ebean entities (tendious) and only use these, at least from Scala code including view templates (ugly)

How to improve that?

Enable step 2 of the enhancement process for the view templates or scala classes in general. E.g. by adding the following to PostCompile in PlayCommands.scala:

val viewClassesDir = new File(srcManaged, "views")
val viewClasses = ( viewClassesDir ** "*.scala") { sourceFile =>
viewClasses.foreach(play.core.enhancers.PropertiesEnhancer.rewriteAccess(classpath, _))

I did only test that very briefly. It appears to work, but I don't know if it has unwanted side effects.

So the big question is, are there reasons why this currently isn't done?

I do think, that lazy loading has its place, especially when combined with Ebeans auto query tuning. In one test I had a page that generated >400 SQL queries per view due to lazy loading. Within a few minutes, Ebean's query tuning converted that to a single query per page view.

Thanks for reading this far,