Robert,
All I can say is "wow", thanks for the insights. This is especially important
because you use both frameworks.
Please let me ask some more questions. (Note: as I said, I was initially
attracted to Cayenne because it had familiar design patterns to EOF, which I
thought was fairly mature at the time, so I may not understand the
Hibernate-way of "thinking").
RE ObjectContext vs Session
I may be mixed up but it sounds like the ObjectContext is similar in concept to
EOF. It sounds like you are saying that among other things the
Hibernate-Session makes simple transactional tasks much more difficult and may
even interfere with a Factory-Method approach to building data objects within a
transaction.
RE Lazy Fetching
If I have the concept correct, this is another term for what EOF calls
"faulting" behavior. IMO optimized/smart faulting behavior is the single most
important reason to use an ORM. The conceptual differences between a RDBMS and
an OO language can result in massive problems with a macro design, one that a
good ORM solves via intelligent faulting algorithms. I had just assumed that
this was a "moot" issue based on the fundamental solutions offered by EOF. Are
there any essential differences in features of "Lazy Fetching" that you can
point out?
RE dbentity/objentity differences
Is the reason associated with the maturity of the "faulting" behavior (or
something else)?
RE Google "hibernate lazyinitializationexception" to see what I mean.
OK, I googled it as you suggested, and found a few (what I call dissertations)
on the subject that suggest that Hibernate does not have a cogent "faulting"
design, and that the Hibernate-Session is not as mature a model for
transactions as is Cayenne's ObjectContext (if I understand the issues). Is
this correct?
Correct me if I am wrong (please :) ), but it is starting to sound similar to
the discussions of C++ garbage collection vs Java garbage collection (i.e. C++
doesn't really embrace garbage collection as a problem that should be handled
by anyone but the programmer)
Thanks again for your input,
Joe
On Sep 6, 2010, at 11:06 AM, Robert Zeigler wrote:
> 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
>>
>