Hi Joe,

First, this e-mail wound up a lot longer than I intended, apologies! The short 
version is: having used both a fair bit, I prefer cayenne, but they both have 
strengths and weaknesses. Most of this e-mail details what I view as weaknesses 
in Hibernate. :)

On to the long version! 

I still know cayenne better than hibernate, but I've used both extensively (2+ 
years of experience with hibernate, on a fairly large system with > 80 tables; 
I've used cayenne for > 5 years now).  I don't have time right now to put 
together a systematic comparison, but here are a few notes:

Hibernate: POJO - some people love it, some hate it. I'm in the latter camp.  
You lose out on a lot of code-reuse, debugging is more difficult, and it 
necessitates constructs like hibernate proxies, which are a PITA to deal with, 
IMO.
Cayenne: interface (inheritance, in practice)-based design.  Some people don't 
like the domain-structure constraints it imposes.  I find it makes debugging 
easier and results in more code re-use. And, no proxies.  Objects are what they 
are.  More importantly, objects are what you think they are (in hibernate-land, 
I've had code like the following:

public class MyObject2 extends MyObject1 {...}

elsewhere:

MyObject1 someobj = someCodeThatReturnsMyObject1();
if (MyObject2.class.isInstance(someobj)) {
  ((MyObject2)someobj).callMyObject2Method();
}

And the above code will throw a cast class exception.  Yup.  That's right.  
Because someobj is a proxy.  A call to getClass() returns the getClass() of the 
underlying object (an instance of MyObject2), but someobj, the proxy, 
technically only implements MyObject1.  So you get a ClassCastException.  Other 
variants include your code not executing at all, even when you know that 
someobj /should/ be an instance of MyObject2. The code above is, of course, 
contrived, but I've hit this in numerous "real world" scenarios.

Hibernate & Cayenne both support "lazy fetching" in some form, but Cayenne's 
support is far superior, IMO (with bonafide faulting, etc.).  This is a 
function, I think, of having supported it far longer (this was initially the 
reason that I used Cayenne rather than Hibernate).  Google "hibernate 
lazyinitializationexception" to see what I mean.  In particular, if you go the 
hibernate route, be very /very/ careful what you do in event listeners 
(pre/post commit, etc.) because it's very easy to hit exceptions there.  
Basically, I find that in Hibernate, event listeners are just barely better 
than useless.  A great idea, but you can't really /do/ anything useful in them. 
 And even the 3rd party modules, written by long-time hibernate developers, hit 
these edge cases (eg: hibernate envers for entity auditing/logging has had 
issues reported where they hit issues with lazyinitializationexception from 
using lifecycle listeners).  The hibernate "way" was originally to not support 
lazy fetching at all; either all of the data you needed came into the 
request/session at once, or it wasn't there at all.  This probably resulted in 
more /performant/ code (fewer queriest/hits to the db, for instance), but is 
basically not feasible in the world of 3rd party 
modules/integration/development: it's not always possible to know exactly what 
information you need at the beginning of, eg, a web request.

I have to give kudos to the hibernate team for the extremely flexible mappings 
they support.  I find cayenne mapping more /intuitive/ (thanks in part to the 
modeler), but there are edge mapping cases that are supported in hibernate that 
are not, to the best of my knowledge, supported in cayenne (cayenne 3.0 
improves this discrepancy, though).  As an example, hibernate supports more 
inheritance modeling schemes (table per concrete subclass, table per class, 
single table) than does cayenne, although cayenne 3.0 has improved in this 
regard.  For simple mapping, hibernate may even be more straightforward than 
cayenne due to it's ability to analyze your domain objects and figure out the 
appropriate tables, etc. to create.  On the other hand, I personally shy away 
from having hibernate auto-create my table structure.  I find it results in 
less thinking about what's really occurring at the db level.  Although that is, 
to a greater or lesser extent, the point of an ORM system, it's my opinion that 
it's still important to think about how the data is physically mapped at the db 
level.  (I should note that you can specify the exact mapping characteristics 
in hibernate.  But my observation is that the tendency is to let hibernate "do 
it's thing" until you find a problem with the way it did it's thing, and you 
tell hibernate the "right way" to do it). 

Metadata: There's no "dbentity" vs. "objentity" separation.  That's great for 
some people... but really too bad. :) My personal experience is that cayenne's 
meta-data support is more accessible and richer than Hibernate's, but that's 
probably a function, at least in part, of familiarity with the frameworks.  

pks: Cayenne's approach is: "these are a database-artifact and shouldn't 
pollute your data model, unless you need them to be there".  Hibernate's 
approach is: "pk's are an integral part of your domain object" (for the most 
part).

ObjectContext vs. Session.  Session is a poor man's ObjectContext. ;) That's an 
opinion, of course.  But. On the surface, these two objects do similar sorts of 
things: save, commit transactions, etc.  But in reality, they are completely 
different paradigms.  In Cayenne, an ObjectContext is very much a "sandbox" 
where you can make changes, roll them back, commit them, etc.  A hibernate 
session is more like a command queue: you instruct it to update, save, or 
delete specific objects, ask it for "Criteria" for criteria-based queries, etc. 
 They may sound similar but there's a big difference in how you use them.  
Basically, hibernate doesn't have the notion of "modified or new object that 
needs to be saved at some point in the future, but which I should retain a 
reference to now." :) In cayenne, you can do something like this:

void someMethod(ObjectContext context) {
   context.newObject(SomePersistentObject.class).setSomeProperty("foo");
   ...
}

Now when that particular context is committed, a new instance of 
SomePersistentObject will be committed, without the calling code having to know 
about it.  Arguably, this is a method witih "side effects" that should be 
avoided, but there are legitimate use cases for this.  Consider a recent 
example I encountered.  A hibernate project I work on manages a set of 
"projects".  Changes to projects are audited, except when the project is first 
created/in state "project_created" (a custom flag, unrelated to hibernate).  I 
recently needed to add support for one auditing operation: record the date of 
creation, and the user who created the project.  WIthout getting into gory 
details, the simplest way to do this would have been to modify the service 
responsible for creating all project types, along the lines of this (how I 
would do this in cayenne):

public <T extends Project> T createProject(Class<T> type) {
   T project = codeToCreateProject();
   Audit a = objectContext.newObject(Audit.class);
   a.setProject(project);
   a.setMessage("Project Created");
   a.setDate(new Date());
   return project;
}

Notes: the project creator is not (and cannot, due to design constraints) 
commit the project to the database at this point in the code.  That's fine in 
cayenne: as long as the calling code is using the same object context (it 
always would be in my case), the Audit object would be committed at the same 
time the project is, and life would be happy.  But the project is not cayenne. 
It is hibernate.  So:

public <T extends Project> T createProject(Class<T> type) {
   T project = codeToCreateProject();
   Audit a = new Audit();
   a.setProject(project);
   a.setMessage("Project Created");
   a.setDate(new Date());
   return project;
}

Except, what happens to a? The answer is: nothing.  It isn't ever saved.  It 
would be, if Project had a reverse relationship to audit (List<Audit> 
getAudits()), that was set to cascade the "save" and "update" operations.
But Project didn't/doesn't, and I wasn't allowed to add it.  There was no way 
to tell hibernate: "Look, I've got this object, and I wan't you to save it, 
but, not right this second".  You can call: session.save(a).  But that results 
in an immediate commit the audit object (and ONLY the audit object!), so if the 
project isn't yet persisted to the db, you get a relationship constraint 
violation, trying to save a relationship to an unsaved object.  There's also a 
session.persist(a) method, part of EJB3 spec, which is theoretically like 
cayenne's "register", but in hibernate, its functionally equivalent (or very 
nearly so) to session.save(a): it triggers an immediate commit to the database 
(at least in our application setup).  There is no equivalent to cayenne's 
"context.register(a)".  I finally solved this issue via life cycle event 
listeners, and it was a pain (you have to be /extremely/ careful about what you 
do in hibernate event listeners.  In particular, read operations that result in 
a hit to the database will cause you major grief, even if you don't modify 
anything, and modification of any kind is next to impossible).  

All that said, there are /some/ good ideas in hibernate. :)  For one thing, 
Cayenne's /requirement/ that two objects with a shared relationship be in the 
same ObjectContext can cause grief, particularly in web applications.  Imagine 
you have a form to create a new object of type Foo.  Foo has a relationship to 
Bar.  You may not want to register this object with the context until you know 
that the new Foo object is a "valid" object (lest you wind up with "dirty" 
objects polluting subsequent commits, using an ObjectContext-per-user session 
paradigm).  But you can't do that: when you set the Bar relationship, Foo will 
be registered with the context.  That's usually fine... you can usually 
rollback the changes... but it does mean sometimes having to think carefully 
about what "state" your objects are in.

I've yet to find the "perfect" ORM.  THere isn't one, as far as I'm concerned, 
b/c there's simply a mismatch between the db model and the object model that 
will result in tradeoffs.  But I find Cayenne far easier to learn and use than 
Hibernate.

Cheers,

Robert

On Sep 5, 2010, at 9/51:21 PM , Joe Baldwin wrote:

> Hi,
> 
> I am again responsible for making a cogent Cayenne vs Hibernate Comparison.  
> Before I "reinvent the wheel" so-to speak with a new evaluation, I would like 
> to find out if anyone has done a recent and fair comparison/evaluation (and 
> has published it).
> 
> When I initially performed my evaluation of the two, it seemed like a very 
> easy decision.  While Hibernate had been widely adopted (and was on a number 
> of job listings), it seemed like the core decision was made mostly because 
> "everyone else was using it" (which I thought was a bit thin).
> 
> I base my decision on the fact that Cayenne (at the time) supported enough of 
> the core ORM features that I needed, in addition to being very similar 
> conceptually to NeXT EOF (which was the first stable Enterprise-ready ORM 
> implementations).  Cayenne seems to support a more "agile" development model, 
> while being as (or more) mature than EOF.  (In my opinion. :) )
> 
> It seem like there is an explosion of standards, which appear to be driven by 
> "camps" of opinions on the best practices for accomplishing abstraction of 
> persistence supporting both native apps and highly distributed SOA's.
> 
> My vote is obviously for Cayenne, but I would definitely like to update my 
> understanding of the comparison.
> 
> Thanks,
> Joe
> 

Reply via email to