Return-Path: Delivered-To: apmail-cayenne-user-archive@www.apache.org Received: (qmail 20842 invoked from network); 6 Sep 2010 16:16:32 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 6 Sep 2010 16:16:32 -0000 Received: (qmail 80321 invoked by uid 500); 6 Sep 2010 16:16:32 -0000 Delivered-To: apmail-cayenne-user-archive@cayenne.apache.org Received: (qmail 80236 invoked by uid 500); 6 Sep 2010 16:16:31 -0000 Mailing-List: contact user-help@cayenne.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: user@cayenne.apache.org Delivered-To: mailing list user@cayenne.apache.org Received: (qmail 80228 invoked by uid 99); 6 Sep 2010 16:16:31 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 06 Sep 2010 16:16:31 +0000 X-ASF-Spam-Status: No, hits=-0.0 required=10.0 tests=RCVD_IN_DNSWL_NONE,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) Received: from [209.86.89.61] (HELO elasmtp-galgo.atl.sa.earthlink.net) (209.86.89.61) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 06 Sep 2010 16:16:23 +0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=dk20050327; d=earthlink.net; b=nz6wMze/9Ci/egLkZCQXMJO4K85SUtFl131ebV8+r76LDv4PQL9ICXKsadhMAukd; h=Received:Content-Type:Mime-Version:Subject:From:In-Reply-To:Date:Content-Transfer-Encoding:Message-Id:References:To:X-Mailer:X-ELNK-Trace:X-Originating-IP; Received: from [24.253.146.147] (helo=[10.0.1.5]) by elasmtp-galgo.atl.sa.earthlink.net with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.67) (envelope-from ) id 1OseMT-0005Cd-5I for user@cayenne.apache.org; Mon, 06 Sep 2010 12:16:02 -0400 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Apple Message framework v1081) Subject: Re: Cayenne vs Hibernate Comparison From: Joe Baldwin In-Reply-To: Date: Mon, 6 Sep 2010 12:16:00 -0400 Content-Transfer-Encoding: quoted-printable Message-Id: <999807D0-CC07-4199-90E7-C2B8191EBA1A@earthlink.net> References: <6DB73E94-92A4-4B85-BFB2-6C12768E4D40@earthlink.net> To: user@cayenne.apache.org X-Mailer: Apple Mail (2.1081) X-ELNK-Trace: 74aacf41df18ac9f85338a7d01cb3b6a7e972de0d01da940af0c89940c92c0c43980547adf5ea992350badd9bab72f9c350badd9bab72f9c350badd9bab72f9c X-Originating-IP: 24.253.146.147 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 =20 On Sep 6, 2010, at 11:06 AM, Robert Zeigler wrote: > Hi Joe, >=20 > 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. :) >=20 > On to the long version!=20 >=20 > 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: >=20 > 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: >=20 > public class MyObject2 extends MyObject1 {...} >=20 > elsewhere: >=20 > MyObject1 someobj =3D someCodeThatReturnsMyObject1(); > if (MyObject2.class.isInstance(someobj)) { > ((MyObject2)someobj).callMyObject2Method(); > } >=20 > 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. >=20 > 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. >=20 > 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).=20 >=20 > 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. =20 >=20 > 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). >=20 > 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: >=20 > void someMethod(ObjectContext context) { > = context.newObject(SomePersistentObject.class).setSomeProperty("foo"); > ... > } >=20 > 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): >=20 > public T createProject(Class type) { > T project =3D codeToCreateProject(); > Audit a =3D objectContext.newObject(Audit.class); > a.setProject(project); > a.setMessage("Project Created"); > a.setDate(new Date()); > return project; > } >=20 > 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: >=20 > public T createProject(Class type) { > T project =3D codeToCreateProject(); > Audit a =3D new Audit(); > a.setProject(project); > a.setMessage("Project Created"); > a.setDate(new Date()); > return project; > } >=20 > 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 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). =20 >=20 > 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. >=20 > 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. >=20 > Cheers, >=20 > Robert >=20 > On Sep 5, 2010, at 9/51:21 PM , Joe Baldwin wrote: >=20 >> Hi, >>=20 >> 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). >>=20 >> 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). >>=20 >> 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. :) ) >>=20 >> 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. >>=20 >> My vote is obviously for Cayenne, but I would definitely like to = update my understanding of the comparison. >>=20 >> Thanks, >> Joe >>=20 >=20