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"
or
obj.getClass().isInstance(SubclassA.class); => true
but
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.

Robert

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.
> 
> Joe
> 
> 
> 
> 
> 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.)
>> 
>> Regards,
>> Jo
> 

Reply via email to