For the last 3 or so years, I've been using hibernate more-or-less day-to-day. Not by choice, per se, but because I've been working on a project that used it. This is my experience, some of which may stem from lack of knowledge of hibernate, but:

1) Personally, I find the "mental model" behind Cayenne easier to follow/understand. An object context is a sandbox. You make your changes and commit them. WIth hibernate, you have a session that's kinda similar, but still not the same. There's more-or-less no notion of "detached" objects in Cayenne (there are transient objects, but it's not the same). A hibernate session is much more closely tied to a transaction than Cayenne's ObjectContext (unless, maybe, you use Hibernate's session-per-user-session paradigm, which is generally discouraged).

2) As Joe mentioned, there's faulting. There are certainly things you /can't/ do in hibernate. There is no (clean) way to have a lazy, non-mandatory toOne relationship. The only relationships that can be lazily fetched are mandatory toOne, and toMany. See, eg, here: http://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one for some suggested workarounds. Hibernate's lazy fetching model really feels like an afterthought. In fact, a number of years ago, hibernate really didn't support lazy fetching at all (at least not well) and one of the devs basically said that he thought lazy fetching was a bad idea; an application should know at the beginning of the request what data it needs and fetch it all in one swoop. Certainly, that's a nice idea in theory... but what if you're not running a webapp? There are other use-cases, even in a webapp, that make it extremely naive/wishful thinking. I wish I could find the thread now, but it was one of the espoused philosophies that originally turned me off to hibernate and on to Cayenne. You can see the sentiment/resistance to lazy loading here, though: https://forum.hibernate.org/viewtopic.php?t=930457&highlight=remote+lazy+loading, albeit for remote lazy loading. But the ideas espoused there are essentially the ones that were espoused earlier, on the web tier. Hibernate does have lazy loading now, but I still find it... finicky. It's way too easy to hit LazyInitializationException.

3) POJO vs. inheritance
Cayenne uses inheritance. Technically, it uses interfaces. Your objects must implement DataObject. In theory, you can do that without inheritance. In practice, that's fighting against the framework, and your objects will inherit from, say, CayenneDataObject (there's a client version, as well, for ROP). For some people, that's a real turn-off. They like their "POJO"s. The thing is... hibernate still uses inheritance. It's just inverted. They created an instrumented class that is a subclass of your class, and all access of your class goes through the subclass. It's an interesting solution, but not without it's pain points. I find hibernate applications MUCH harder to debug than Cayenne applications, precisely because you're dealing with a proxy. And, in fact, inspecting that proxy in a debugger can trigger database access that changes the path of execution. I was debugging an application that was hitting an exception (NPE or else LazyInitializationException, or something similar). Whenever I would try to debug it, the exception wouldn't appear. When I didn't, the exception would appear. I finally realized it was because I was inspecting the object earlier on, forcing a fetch of the associated values, resulting in a fully inflated object at the normal time of the exception. Oh what fun it was to debug that problem! As for runtime bytecode generation, I've seen it used well, and not. The critical thing is to have really good exceptions if your framework is going to use bytecode generation, because debugging generated bytecode is a pain. Unfortunately, Hibernate exceptions suck. :)

Another issue occurs with the handling of inheritance. Suppose you fetch a list of SuperClass that has SubclassA and SubclassB. Depending on how you fetch SuperClass, the proxy will ONLY implement the SuperClass, so none of the objects will be instances of SubclassA or SubclassB. Ok, fine, you fetched SuperClass. Except... if you do something like:

obj.getClass().getName(); => "SubclassA"
obj.getClass().isInstance(SubclassA.class); => true
SubclassA obj = (SubclassA) obj: //ClassCastException.

This is because the proxy is only an instance of SuperClass, but the /delegate/ it wraps is a SubclassA (or B). And the calls to getClass() are passed on to the proxy. So in pretty much any way you can check for the class type, you'll be fooled into thinking you have a subclass instance, but you can't cast the object directly as a subclass. There are ways around this... but it's stupid that you have to work around it in the first place. I find Cayenne's "traditional" inheritance MUCH simpler to deal with, with fewer quirks. Also note that you can still insert your own custom superclass into the chain:

MyClass extends MySuperClass{}
MySuperClass extends CayenneDataObject{}

Is perfectly legit.

4) runtime metadata. You can get the metadata (mapping info) for entities in hibernate and in cayenne, but it's messed up in hibernate. :) Seriously, in my experience, it's MUCH easier to work with object metadata in Cayenne than in hibernate. It's one of the advantages of inheriting from CayenneDataObject. The information is all self-contained, so you can very easily access information such as database-level constraints. The integration library for cayenne and tapestry leverages this to automatically make fields required that are flagged as "not null" at the database level, and to constrain the length of input fields when using limited-width columns. This information /is/ available in Hibernate, but it's harder to get at, and the API for using it is extremely awkward. At least compared to Cayenne! :)

Those are a few of the pain points I've experienced with Hibernate. That said, it does have some strengths.

1) I do (kind of) like the annotation-based approach that is now available in hibernate. Not enough to abandon Cayenne + Cayenne Modeler, but if you like having all of the modeling info directly in your source (some do, some don't), or you like building your object and annotating on the fly, then this is nice. Hibernate provides fairly sane defaults for most things, so it means you build your class and annotate the deviances rather than having to annotate everything.

2) Hibernate's implementation of inheritance is (probably) more complete than Cayenne's (I'm not sure where the current state is for Cayenne). So if you have really complex inheritance requirements, that's something to consider. Cayenne readily supports "single table inheritance" (there are other names). Cayenne also supports vertical/"joined table inheritance", where you have one table for the superclass, and then a table for each subclass. Cayenne currently doesn't support "horizontal" inheritance (no table for the superclass; one table for each concrete subclass); hibernate does. If you need that, you'll have to stick with hibernate (my condolences :).

3) There are relative strengths and weaknesses to Cayenne's SelectQuery API vs. Hibernate's Criteria API. One of the really nice things in the Hibernate API is projections, which aren't quite as straightforward in Cayenne. The flip is is raw SQL. I find Cayenne's raw sql support, in the form of SQLTemplate, superior to Hibernate's sql execution. You can do it in Hibernate, but it feels dirty, whereas Cayenne more readily embraces the idea that not every query should be generated by the ORM. In fact, I've often considered forking Cayenne and creating a very small library that basically consists solely of SQLTemplate and DataRows. For small projects, having a simple way to externalize and centrally manage queries without introducing additional complexity would be great. :)

In regards to your questions, specifically:

1) Should be able to use Cayenne either way
2) Starting with a new technology is always a risk, but generally, I think your risk is reasonably low with Cayenne. If you're beating your head against the wall with Hibernate, you'll probably find Cayenne's "mental model" more to your liking. As far as transitioning the project... I would probably start a new branch, reverse engineer the model from the database, tweak whatever properties need tweaking (you'll need to override the enum stuff, for instance, but that's pretty straightforward). Then generate your classes and migrate any custom code from the hibernate entities to the cayenne ones. Then delete the hibernate entities. :) You'll want to generate the cayenne entities in a different package than the Hibernate ones, at least initially. Once you nix the hibernate entities, you can bulk-move the Cayenne ones. As for specific pitfalls, without knowing more details about your project, it's hard to say. Are you required to audit entities? You're probably going to need to do that differently in Cayenne than in Hibernate (although I think listeners are friendlier in Cayenne than in Hibernate!)

3) I addressed bytecode generation above. Basically, I prefer Cayenne's approach.

4) ... maybe this is just me, but, I /usually/ try to avoid storing data that can be calculated. Sometimes it's justified, if the calculation is expensive, but unlikely to change frequently (eg: I've stored metadata about files in the db before so that the metadata was available without having to access the files). So in the case of the "totalValue", I would probably do that either as EJBQLQuery (I think it supports SUM?) or else as a SQLTemplate query. With the right query cache strategy, it should be performant. Then you don't worry about updating the value directly (you'll just have to know when to invalidate the cached query... which is when OrderDetail.value is modified or new OrderDetail is called) and cayenne can re-run the query for you, but will otherwise return the cached value. Then implement it in the object layer as a psuedo-property. Maybe other people have other solutions that are better, but that's what I'd do.

5) They aren't nested transactions; they are nested contexts. And they can be very useful for aggregating a group of related changes together and isolating them from other changes in the context so you can throw them away as a group without worrying about losing your other changes. Very handy for dealing with dirty data!

6) I would definitely start by reverse engineering the schema. You'll get all of the DbEntities created for you for free; they will usually be correct, as long as you've correctly supplied foreign key constraints, etc. You'll get the "Object Entities" as well, although you'll have to tweak those a bit to handle the enums. But reverse engineering should definitely save you time.

On Aug 12, 2011, at 8/1210:37 AM , Joe Baldwin wrote:

In My Opinion:

- the EOF-like conceptual model of Cayenne & Cayenne Modeler was important to me
- the OTB Cayenne memory management is excellent
- the Cayenne "faulting" behavior is advanced (and leaves one wondering why anyone would choose Hibernate - I think one could call Hibernate faulting-behavior "non existent" if one were to explore this issue in detail)
- Cayenne Modeler tool is professional quality and supports reverse engineering as well as an evolving design methodology
- the only "advantage" of Hibernate that I have been able to discern is that it is currently "conventional wisdom" among a certain class of project managers who's evaluation process appears to be to evaluate solely based on "conventional wisdom" and then call it a day. I find the "conventional wisdom" evaluation process to be flawed.


On Aug 12, 2011, at 10:36 AM, Durchholz, Joachim wrote:

Well of course your answer will be Yes :-)
So, more specifically, I'm interested in hearing about the relative strengths and weaknesses of sessionless ORMs, e.g. Ebean.

I'd like to hear opinion, trench stories, and such.

Here's some things I that may or may not be relevant, any feedback welcome:

1. I'm in a J2SE project. There are wishes to convert that into J2EE, or at least partition the J2SE app into a J2EE-compatible service layer and a GUI. (There is also a nightly batch app that I don't see ever arriving in J2EE land. No use case for that.)

2. I can't take any risks. The project is in bad standing already because of all the time (more than a person-year) sunk into identifying and solving Hibernate hassles; another delay on that order of magnitude and I'm out of my current job. I can probably convince my higher-ups to sink another person-month into getting away from Hibernate, but I need to give solid guarantees.
So: any feedback from people who have done that kind of transition would be very, very much appreciated, with a particular emphasis on traps to avoid.

3. I have no LOBs, no wide tables, and all bulk data goes over gigabit ethernet so I don't need no fieldwise laziness.
However, I have no experience with a framework that does not use bytecode generation, so I'm curious how the differences turn out in practice.
So... what do people think about pros and cons of runtime bytecode generation?

4. I'd like to ensure stuff like "Order.totalValue needs to be the sum of all associated OrderDetail.value fields".
"Interesting" aspects here:
- Other applications might have modified OrderDetail records during user think time. I'd prefer the ORM simply updating the totalValue to whatever is in the database over getting a concurrent modification complaint (unless the current app loaded the same OrderDetail and changed the value field to something different).
- The totalValue should get the correct value regardless of whether all pertinent OrderDetails were loaded. (This probably means running an UPDATE statement with SUM(value) in during flush/commit.)
- Various permutation about which fields in Order were modified by the current and the other application, and doing the Right Thing in each case (too much detail for a single post so I'm leaving that out for now).
BTW Hibernate doesn't do this, so supporting that use case would allow me to argue that we're not only getting rid of problems but actually getting something better than initially asked for :-)

5. Those nested transactions (savepoints) are pretty cool and would be a major advantage. Sometimes we do have a series of steps and need to roll back some but not all of them (the "wizard situation").
Are there caveats in that area, or can I go ahead and use that as a selling point without worrying about having to go back on that later?

6. How much manual work is involved in reverse engineering?
For Hibernate, I found that the tool is only marginally better than simply specifying everything by hand. Maybe automatic reverse engineering generally isn't worth the hassle; after all, the tool only sees a VARCHAR(1) but cannot know whether it's just a boolean in disguise or a one-character status code, let alone guess the actual mapping between database strings and Java enum values. (We have lots of such things in our tables.)


Search Discussions

Discussion Posts


Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 5 of 6 | next ›
Discussion Overview
groupuser @
postedAug 12, '11 at 2:36p
activeAug 15, '11 at 9:18a



site design / logo © 2022 Grokbase