[2.0] ANORM and Scala Templates - a conflict in type-safe philosophy?

979 views
Skip to first unread message

Daryl Teo

unread,
Feb 8, 2012, 12:36:03 PM2/8/12
to play-fr...@googlegroups.com
I have been building a application in Play2.0 for learning purposes, and I am slightly confused by how ANORM fits into the framework.

The new Scala Template is a great improvement from the old templates (having come from MVC3 I have been spoilt by Razor). One of the boasts is that it allows for type-safe template parameters. However, this implies that the template parameters have to be a class of some sort for it to be effective.

ANORM however, has gone in the opposite direction. by basically declaring that type-safe DSL to SQL translation is bad. Thus, all results through ANORM are either primitive scalar types, or useless ANORM types. Incidentally the documentation also states that 

"it is an anorm type that it is not really convenient to use outside of your database access code."

 So, in the end, you have a few choices:
  • build a POJO based model, but now have to manually map them from ANORM results.
  • pass ANORM types into template (urgh)
  • flatten ANORM types into Tuples
  • use case classes to map.
But this also raises several problems for me:

Lets examine this case study:
I have a Calendar, on the Calendar I have Appointments. On the Calendar page I only want to query a portion of all the Appointment details (date, description), while on the Appointment details page I want to query the whole Appointment. 

Scenario 1: POJO Entity Model
In this scenario, we have a single domain (entities) model as per usual. This means the use of ANORM is completely negated (as I see it), since you cannot (should not?) query a portion of an entity. So each time you wish to query an object it is all or nothing. In this case, a ORM would do the job just fine anyway.

Scenario 2: ANORM types into templates
Absolutely bad practice. Don't bother. You wouldn't need type-safety for templates anyway in this case.

Scenario 3: Tuples
Possible solution, but in this case your template is now dependent on the ordering of the Tuple, again meaning that either the template doesn't need to be type-safe, or ANORM is not necessary.

Scenario 4: Case Classes, Entity Model
Similar to Scenario 1. At least we don't need to manually map them, but this doesn't work if you want your models defined in Java instead of Scala (as far as I could tell)

Scenario 5: Case Classes, Non-Entity Model
In this case, we define Result classes for each type of result. So, we'll have a NonDetailedAppointment, and a DetailedAppointment. However, this kind of violates DRY, and would be a maintenance nightmare.


So in the end, I've come to this final scenario:

Scenario 6: Just deal with it
Stick to POJO classes, and just accept that your entity may not be completely constructed from the query. 

Admittedly, this isn't really a problem exclusive to ANORM, but rather a problem with object-relational impedance mismatch. ORMs try to solve this by moving the mapping closer to the database, whereas if you were using ANORM or plain SQL you would be expected to solve this by yourself in your data access layer. However, it is the fact that Play!2.0 is pushing ANORM and Scala Templates as the default, and yet I can't seem to find an elegant way to fit the two together in a way that utilises the benefits of both to its potential.

So Play!gurus, lend me your thoughts on this. Am I barking up-the-wrong-tree/mad? Or maybe I've just missed something big? Is there another less unsavoury of dealing with impedance mismatch? Is this a case where a templating engine built on a dynamic language be a better fit?

Disclaimer: I'm not an experienced Play! developer, and I barely know any Scala at all.

Thanks for your time
Daryl

Erem Boto

unread,
Feb 8, 2012, 3:41:02 PM2/8/12
to play-fr...@googlegroups.com
Hi Daryl,

I echo your sentiment on ANORM. I think it approaches database access from a very interesting direction, but I have found it fundamentally more difficult to work with than some other database packages when it comes to translating relational data into my domain objects and vice versa. I think you have succinctly detailed some of the reasons why. 

My organization chose instead to integrate the Squeryl ORM and Query DSL. I have found that package to really hit the spot relative to the O/R impedance mismatch in our relatively simple application. We define our entities as either classes or case classes, specify our schema in Scala, and query out either whole objects or tuple subsets thereof as we see fit using a type-safe DSL. We've run our app on top of H2, MySQL, and postgres with 0 code changes. 

So in short, I had the same issues with ANORM as you did, but I've found that Squeryl and Play's scala templating engine are a match made in heaven.

Just my vaguely related 2c =)

Erem

Noel Welsh

unread,
Feb 8, 2012, 4:21:10 PM2/8/12
to play-fr...@googlegroups.com
I'm not a fan of ANORM, preferring ScalaQuery for accessing relational DBs. With ScalaQuery your queries can return case classes(case classes are just fancy tuples) and these are trivial to deal with in your templates while still providing useful types.

HTH,
N.

Drew Hamlett

unread,
Feb 8, 2012, 7:06:49 PM2/8/12
to play-framework
Actually, I really like Anorm. It's not straight JDBC and it's not
over complicated ORM. It sits right in between. I think it feels
good to write plain queries. You have more control on what the
queries are doing. In a lot of cases they can be more optimized and
specific to what you need.

I just felt that the ORM way is ugly, bloated, and over complicated.

At the end of the day I just want my query to work. If I want to
insert data into a database I can do that. I'm not dealing with
detached entities, merging, saving, etc. I can just insert.

On Feb 8, 12:36 pm, Daryl Teo <i...@darylteo.com> wrote:
> I have been building a application in Play2.0 for learning purposes, and I
> am slightly confused by how ANORM fits into the framework.
>
> The new Scala Template is a great improvement from the old templates
> (having come from MVC3 I have been spoilt by Razor). One of the boasts is
> that it allows for type-safe template parameters. However, this implies
> that the template parameters have to be a class of some sort for it to be
> effective.
>
> ANORM however, has gone in the opposite direction. by basically declaring
> that type-safe DSL to SQL translation is bad. Thus, all results through
> ANORM are either primitive scalar types, or useless ANORM types.
> Incidentally the documentation<https://github.com/playframework/Play20/wiki/ScalaAnorm>also states that
>
> "it is an *anorm* type that it is not really convenient to use outside of
>
> > your database access code."
>
>  So, in the end, you have a few choices:
>
>    - build a POJO based model, but now have to manually map them from ANORM
>    results.
>    - pass ANORM types into template (urgh)
>    - flatten ANORM types into Tuples
>    - use case classes to map.

Daryl Teo

unread,
Feb 8, 2012, 9:33:24 PM2/8/12
to play-fr...@googlegroups.com
Hi Everyone,

  thank you all for your responses.

However it seems that the answer, so far, to "how does ANORM and templates work together" is "don't use ANORM".

Erem Wrote:
I think it approaches database access from a very interesting direction

I don't particularly think this is the case; ANORM simply tries to streamline the creation of SQL queries that you would otherwise have been using JDBC prepared statements for. 

My organization chose instead to integrate the Squeryl ORM and Query DSL.

I will have a look at this. Thanks.

Noel Welsh Wrote:
With ScalaQuery your queries can return case classes(case classes are just fancy tuples)

Indeed they are. My issue wasn't with case classes being useless, rather, their use in ANORM completely negates the "benefits" of ANORM since I must query all of the entity in order to pattern match with the case class (ANORM purported that controlling your own SQL is best since you can optimise your query for different situations instead of a 1-size fits all approach)

Drew Hamlett Wrote:
I just felt that the ORM way is ugly, bloated, and over complicated. 

This is my sentiment also. For those people approaching architecture using a CQRS approach, writing your own SQL is a given. ANORM provides this streamlining approach nicely.

The problem here is that in the 2 use cases I described, my queries would look like this

SELECT id, name FROM Appointment WHERE date BETWEEN blah AND blah;
SELECT id, name, date, description, location FROM Appointment WHERE id = ?;

In each case, the return schema is different. This makes mapping to an entity object for use in the default templates rather unsavoury. 

One could get around this perhaps, using the following query:

SELECT id, name, null [Date], null [description], null [location] FROM Appointment WHERE date BETWEEN blah AND blah;

But you can see how this quickly gets out of hand, yes? Also, as noted in the comments of the ANORM documentation, this doesn't even begin to skim the surface of how complex optimized sql queries can get, in the case of 1-M,M-M joins. Now you have to be responsible for identifying that each row does not refer to a single instance of an entity.

This post is a search of best practice when dealing with this. Any thoughts?

Perhaps it is my past experience with ORMs that is causing me confusion.

Regards,
Daryl

Guillaume Bort

unread,
Feb 9, 2012, 3:53:28 AM2/9/12
to play-fr...@googlegroups.com
> The problem here is that in the 2 use cases I described, my queries would
> look like this
>
> SELECT id, name FROM Appointment WHERE date BETWEEN blah AND blah;
> SELECT id, name, date, description, location FROM Appointment WHERE id = ?;
>
> In each case, the return schema is different. This makes mapping to an
> entity object for use in the default templates rather unsavoury.

I would do:

case class Appointment(id: Long, name: String, detail:
Option[AppointmentDetail] = None)
case class AppointmentDetail(description: String, location:String)

Then write a parser for Appointment as

parser = get[Long]("id") ~ get[String]("name") map {
case id~name => Appointment(id, name)
}

then a parser for AppointmentDetail:

detailParser = get[String]("description") ~ get[String]("location") map {
case desc~loc => AppointmentDetail(desc,loc)
}

And compose them to get a full Appointment parser:

fullParser = parser ~ detailParser map {
case a~d => a.copy(detail = d)
}

> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/play-framework/-/GVX-oiF6-8AJ.
>
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/play-framework?hl=en.

--
Guillaume Bort

Sadache Aldrobi

unread,
Feb 9, 2012, 5:07:33 AM2/9/12
to play-fr...@googlegroups.com
On the same lines you could do

case class Calendar(appointment:AppointmentHeader)

case class AppointmentHeader(id: Long, name: String)

case class Appointment(header: AppointmentHeader, description: String, location:String)

You can get even a less verbose usage if you make Appointment extend AppointmentHeader but that's beyond the point, just you have all the flexibility to model your domain objects the way they make sense to you.
www.sadekdrobi.com
ʎdoɹʇuǝ

Daryl Teo

unread,
Feb 9, 2012, 6:33:25 AM2/9/12
to play-fr...@googlegroups.com
Thank you for your replies again.

Guillaume Bort Wrote: 
I would do:
case class Appointment(id: Long, name: String, detail:
Option[AppointmentDetail] = None)
case class AppointmentDetail(description: String, location:String)

Yes. This is probably one of the two more elegant solutions available (I noted this as scenario 5 in my initial post).

However, I am sure you must agree that my example is quite contrived, and is not representative of a large application. You would essentially have to write case classes and parsers for each and every type of use case with different query result, which increases the amount of work and maintenance, and also introduces more possibilities for inconsistencies.

Sadache Wrote:
On the same lines you could do
case class Calendar(appointment:AppointmentHeader)
case class AppointmentHeader(id: Long, name: String)
case class Appointment(header: AppointmentHeader, description: String, location:String)

This is a nice solution, but again the example is quite contrived. I suspect this solution won't work in some complex scenarios.

I have mulled upon a separate solution which I will attempt... I will update if it turns out to be a good idea.

Drew Hamlett

unread,
Feb 9, 2012, 5:01:45 PM2/9/12
to play-framework
There's always Select * . If you need 3 columns in one situation, 10
another time and 5 another time, hopefully your not going to write
different parsers for that. I'd just do a Select *. There's nothing
wrong with that. In fact most of my applications are built like
this. Your customers always going to want more information anyway.

Daryl Teo

unread,
Feb 13, 2012, 4:35:17 AM2/13/12
to play-fr...@googlegroups.com
Drew Hamlett said: 
 If you need 3 columns in one situation, 10 
another time and 5 another time, hopefully your not going to write 
different parsers for that.

There is nothing wrong with writing multiple parsing algorithms, in this case. The problem isn't that I have to write multiple parser, but rather what I have to parse them into.

I'd just do a Select *

I feel you have not understood my point exactly.

Yes I COULD just do a Select *. But in that case, I'd just stick to an ORM that already does that for me OOTB, also does saving/persistence for me, and finally I don't even need to write the query in the first place. ANORM's stakes its claim as being a way for people to fine-tune their SQL; what use would there be for it, if I were simply going to use "SELECT *" statements everywhere?

Plain Jane "Select *" statements may not always be the best solution either.

Take my above contrived example, and lets say I want to display number of entries for a particular week while displaying the details of the calendar. For example:

 - Personal Appointments (2)
 - Business Meetings (5)

The ORM method would require you to pull the entire tree of data (SELECt * FROM Calendar INNER JOIN Bookings WHERE date BETWEEN blah AND blah) just to do a aggregation. Otherwise, you'd still need to do TWO separate queries which requires 2 connections: 1 to pull Calendar data, another to pull the tally as a scalar value.

ANORM would allow you to query everything I need in this case in only 1 query (e.g.:

SELECT Calendar.calendar_id, Calendar.name, Tally.tally
FROM Calendar INNER JOIN (SELECT COUNT(*) as tally FROM Bookings WHERE date BETWEEN blah AND blah GROUP BY calendar_id) Tally
ON Calendar.calendar_id = Tally.calendar_id;

This, I feel, is an example of where ANORM has a strong use-case, and an example where you'd need a separate parser. In fact, in your earlier post you also wrote:

You have more control on what the queries are doing.  In a lot of cases they can be more optimized and specific to what you need. 

So to clarify again: my view isn't that ANORM is a bad idea (I think it is a great idea). I simply feel that ANORM within the context of Play! seems out of place, as the philosophy that permeates the design of everything else (such as typed templates) negates the benefits that ANORM claims to provide if it leads you to use ANORM like an ORM (side-note: ANORM is Not an ORM). So far the discussions have revolved around

 - don't use ANORM (Erem, Noel)
 - use ANORM like an ORM through Select * (your(Drew) suggestion)
 - use ANORM with a warped entity model (as per Guillaume and Sadache)

I am attempting a PlayPlugin with PropertiesEnhancer that will enhance basic entity classes with value checking (avoiding the need for extra boilerplate overhead). This will allow people to use their basic entity model plugged into templates, still mapped to ANORM using parsers as the intermediary between the two, and throws runtime exceptions when a field is accessed without its value being previously set. Its a compromise (you don't get compile-time safety) but I believe it is sufficient.

Hopefully everyone will continue to provide me with input regarding this area of Play!Framework. :)

Sadache Aldrobi

unread,
Feb 13, 2012, 4:46:43 AM2/13/12
to play-fr...@googlegroups.com

So to clarify again: my view isn't that ANORM is a bad idea (I think it is a great idea). I simply feel that ANORM within the context of Play! seems out of place, as the philosophy that permeates the design of everything else (such as typed templates) negates the benefits that ANORM claims to provide if it leads you to use ANORM like an ORM (side-note: ANORM is Not an ORM). So far the discussions have revolved around

Anorm fits perfectly into Play2 philosophy of "no black magic, full control over what you are doing". The result you get from Anorm IS statically typed, and Anorm gives you the opportunity to model your objects the way you want (composition, inheritance, immutable). Failing to see so feels to me like favoring argument over practice.

 
-- 
You received this message because you are subscribed to the Google Groups "play-framework" group.
To view this discussion on the web visit https://groups.google.com/d/msg/play-framework/-/jltymUiinwwJ.

To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.



--
www.sadekdrobi.com
ʎdoɹʇuǝ
Reply all
Reply to author
Forward
0 new messages