Post #156,169
5/20/04 11:29:49 PM
|

Nice attempt
The POINT that you missed is that THERE IS NO BLOAT. You found an example of the programmatic query language that they suggest people NOT use, in order to show how bloated it was. You failed. Accept it and move on. Difficult queries are no more convoluted using Hibernate than they are using SQL. A good many queries are more straight-forward, such as the getAccounts() example. Are you willing to admit this yet? That is just yet another CRUD-screen (create,read,update,delete) framework it appears. No, it's quite a bit more flexible than just CRUD. But it makes CRUD dead stupid simple to do.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,172
5/20/04 11:40:36 PM
|

re: Nice attempt
We would probably have to do a code-for-code shootout to settle this. A lot of the "simple" examples require a fair amount of set-up work to define stuff (a lot of it essentially duplicating the RDB schemas with an OO twist.)
Even if by chance the code-size was the same (not convinced yet), you still have not justified the extra OR-mapper layer, per other message.
________________ oop.ismad.com
|
Post #156,180
5/21/04 12:09:30 AM
|

re: Nice attempt
Hibernate definitions. Note that this can be used to generate the database schema in any of the supported databases automatically. \n<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE hibernate-mapping PUBLIC\n "-//Hibernate/Hibernate Mapping DTD//EN"\n "[link|http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd|http://hibernate.sou...e-mapping-2.0.dtd]">\n\n<!-- $Id: iwethey.hbm.xml 17 2004-04-28 22:13:29Z anderson $ -->\n<hibernate-mapping auto-import="true">\n\n\t<class name="org.iwethey.domain.User" table="iwethey_user" lazy="true">\n\t\t<cache usage="read-write"/>\n\n\t\t<id name="id" column="user_id" unsaved-value="0">\n\t\t\t<generator class="sequence">\n\t\t\t\t<param name="sequence">seq_user</param>\n\t\t\t</generator>\n\t\t</id>\n\t\n\t\t<property name="nickname"/>\n\t\t<property name="encryptedPassword" column="password"/>\n\t\t<property name="created" type="timestamp" insert="false"/> <!-- update="false"/> -->\n\t\t<property name="lastPresent" column="last_present" type="timestamp" insert="false"/>\n\n\t\t<map name="properties" table="user_property" cascade="all" lazy="true">\n\t\t\t<cache usage="read-write"/>\n\t\t\t<key column="user_id"/>\n\t\t\t<index column="name" type="string"/>\n\t\t\t<element column="value" type="string"/>\n\t\t</map>\n\t</class>\n</hibernate-mapping>\n Data interface class. Exists for the purpose of allowing swappable data sources (note the "implements UserManager"). Not strictly necessary as a separate object/interface if you are using only a single data source. \npublic class HibUserManager extends HibernateSupportExtensions implements UserManager\n{\n\tpublic List getUserList()\n\t{\n\t\treturn getHibernateTemplate().loadAll(User.class);\n\t}\n\n\tpublic User getUserByNickname(String nickname)\n\t{\n\t\tList l = getHibernateTemplate().find("from User user where user.nickname = ?", nickname);\n\t\tif (l.size() == 1)\n\t\t\treturn (User) l.get(0);\n\t\treturn null;\n\t}\n\n\tpublic User getUserById(int id)\n\t{\n\t\ttry\n\t\t\t{\n\t\t\t\treturn (User) getHibernateTemplate().get(User.class, new Integer(id));\n\t\t\t}\n\t\tcatch (HibernateObjectRetrievalFailureException e)\n\t\t\t{\n\t\t\t\treturn null;\n\t\t\t}\n\t}\n\n\tpublic boolean checkLogin(User user)\n\t{\n\t\tList l = getHibernateTemplate().find(\n\t\t\t"from User user where user.nickname = ? and user.encryptedPassword = ?",\n\t\t\tnew Object[] { user.getNickname(), user.getEncryptedPassword() }\n\t\t\t);\n\t\treturn l.size() == 1;\n\t}\n\n\tpublic void saveUser(User user)\n\t{\n\t\tgetHibernateTemplate().saveOrUpdate(user);\n\t}\n\n\tpublic void saveUserAttributes(User user)\n\t\t{\n\t\t\tif (!user.isAnonymous())\n\t\t\t\tgetHibernateTemplate().saveOrUpdate(user);\n\t\t}\n\n\tpublic void removeUser(User user)\n\t\t{\n\t\t\tif (!user.isAnonymous())\n\t\t\t\tgetHibernateTemplate().delete(user);\n\t\t}\n\n\tpublic int getUserCount()\n\t\t{\n\t\t\treturn single("select count(*) from User user").intValue();\n\t\t}\n\n\tpublic int getActiveUserCount()\n\t\t{\n\t\t\treturn single("select count(*) from User user where (CURRENT_TIMESTAMP - last_present) < interval '10 minutes'").intValue();\n\t\t}\n\n\tpublic boolean getExists(String nickname)\n\t\t{\n\t\t\treturn single("select count(*) from User user where nickname = ?", nickname).intValue() > 0;\n\t\t}\n\n\tpublic boolean getExists(User user)\n\t\t{\n\t\t\treturn getExists(user.getNickname());\n\t\t}\n}\n User object. Serves as a form backing object as well as a data object. Supports an arbitrary number of properties, any type that can be represented as a string. I've omitted some convenience methods (getPropertyAsBoolean, etc.) that aren't necessary for the purpose of proving the point. This class can also be used in anonymous mode before the user has logged in, allowing preference settings that persist for the session only. Note that other OO languages do not require nearly as much boiler-plate set/get cruft as Java. \npublic class User implements Serializable\n{\n\tprivate int mId = 0;\n\tprivate String mNickname = null;\n\tprivate String mUnencryptedPassword = null;\n\tprivate String mEncryptedPassword = null;\n\tprivate String mPasswordCheck = null;\n\tprivate Date mCreated = null;\n\tprivate Date mLastPresent = null;\n\tprivate Map mProperties = null;\n\n\tpublic User() { }\n\n\tpublic User(User user)\n\t\t{\n\t\t\tsetFromUser(user);\n\t\t}\n\n\tpublic User(String nickname)\n\t\t{\n\t\t\tsetNickname(nickname);\n\t\t}\n\n\tpublic User(String nickname, String password)\n\t\t{\n\t\t\tthis(nickname);\n\t\t\tsetUnencryptedPassword(password);\n\t\t}\n\n\tpublic void setId(int id) { mId = id; }\n\tpublic int getId() { return mId; }\n\n\tpublic void setNickname(String nickname) { mNickname = nickname; }\n\tpublic String getNickname() { return mNickname; }\n\n\tpublic void setUnencryptedPassword(String password)\n\t\t{\n\t\t\tmUnencryptedPassword = password;\n\t\t\tif (password != null && !password.equals(""))\n\t\t\t\tmEncryptedPassword = encrypt(password);\n\t\t}\n\tpublic String getUnencryptedPassword() { return mUnencryptedPassword; }\n\n\tpublic void setEncryptedPassword(String password) { mEncryptedPassword = password; }\n\tpublic String getEncryptedPassword() { return mEncryptedPassword; }\n\n\tpublic void setPasswordCheck(String passwordCheck) { mPasswordCheck = passwordCheck; }\n\tpublic String getPasswordCheck() { return mPasswordCheck; }\n\n\tpublic void setCreated(Date created) { mCreated = created; }\n\tpublic Date getCreated() { return mCreated; }\n\n\tpublic void setLastPresent(Date lastPresent) { mLastPresent = lastPresent; }\n\tpublic Date getLastPresent() { return mLastPresent; }\n\n\tpublic void setProperty(String name, String value)\n\t\t{\n\t\t\tgetProperties().put(name, value);\n\t\t}\n\n\tpublic Map getProperties()\n\t\t{\n\t\t\tif (mProperties == null)\n\t\t\t\tmProperties = new HashMap();\n\n\t\t\treturn mProperties;\n\t\t}\n\n\tpublic void setProperties(Map props)\n\t\t{\n\t\t\tmProperties = props;\n\t\t}\n\n\tpublic String getProperty(String name, String defaultValue)\n\t\t{\n\t\t\tString val = (String) getProperties().get(name);\n\n\t\t\tif (val == null)\n\t\t\t\treturn defaultValue;\n\n\t\t\treturn val;\n\t\t}\n\n\tpublic void setFromUser(User user)\n\t\t{\n\t\t\tassert user != null;\n\n\t\t\tsetId(user.getId());\n\t\t\tsetNickname(user.getNickname());\n\t\t\tsetUnencryptedPassword(user.getUnencryptedPassword());\n\t\t\tsetEncryptedPassword(user.getEncryptedPassword());\n\t\t\tsetPasswordCheck(user.getPasswordCheck());\n\t\t\tsetCreated(user.getCreated());\n\t\t\tsetLastPresent(user.getLastPresent());\n\t\t\tsetProperties(user.getProperties());\n\t\t}\n\n\tpublic Object clone()\n\t\t{\n\t\t\treturn new User(this);\n\t\t}\n\n\tprivate static final char mHexChars[] =\n\t{\n\t\t'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'\n\t};\n\n\tpublic String encrypt(String buffer)\n\t\t{\n\t\t\tMessageDigest crypt = null;\n\n\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tcrypt = MessageDigest.getInstance("MD5");\n\t\t\t\t}\n\t\t\tcatch (NoSuchAlgorithmException e1)\n\t\t\t\t{\n\t\t\t\t\tLogFactory.getLog(getClass()).error("No MD5 implementation!");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\tcrypt.update(buffer.getBytes());\n\n\t\t\tbyte[] bytes = crypt.digest();\n\n\t\t\tStringBuffer hex = new StringBuffer(2 * bytes.length);\n \n\t\t\tfor (int i = 0; i < bytes.length; i++)\n\t\t\t\t{\n\t\t\t\t\tbyte bch = bytes[i];\n\n\t\t\t\t\thex.append(mHexChars[(bch & 0xF0) >> 4]);\n\t\t\t\t\thex.append(mHexChars[bch & 0x0F]);\n\t\t\t\t}\n\t\t\n\t\t\treturn hex.toString();\n\t\t}\n\n\tpublic boolean isAnonymous()\n\t\t{\n\t\t\treturn mId == 0;\n\t\t}\n}\n Omitted: Spring bean factory XML, as this is application dependent and not actually necessary to use the code. It adds things like declarative transaction boundaries, cache controls, and the like. This is working code. If you don't believe me, go [link|http://lord.gregfolkert.net:8180/iwethey/main.iwt|here]. Note that there are few interface bugs in that version, since fixed.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,183
5/21/04 12:40:28 AM
|

Forgot to add:
The user properties are lazily loaded when called. This is one of the main reasons I went with Hibernate. The excess code I was able to jettison was just an added benefit.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,252
5/21/04 3:33:20 PM
|

Aieee! Actual code! HOW DARE YOU!!!
Peter [link|http://www.debian.org|Shill For Hire] [link|http://www.kuro5hin.org|There is no K5 Cabal] [link|http://guildenstern.dyndns.org|Blog]
|
Post #156,257
5/21/04 4:23:49 PM
|

Making it interesting
How about a nice useful assignment that all in these parts could actually benefit from. Let's say that Admin goes about building a useful app with OOP techniques. Let's say that Bryce gets to write the same app using only procedural constructs.
The application is the zIWETHEY board. Bryce will have the advantage that Admin will have provided a working set of software that will be open sourced. He can use that to construct a working prototype in any procedural language he so desires (assuming, of course, that the language runs under Apache).
Bryce claims that the procedural version will be much cleaner. Let's see some actual proof.
Let the games begin. :-)
|
Post #156,261
5/21/04 4:36:53 PM
|

Heh. I think Bryce would rather have someone else write it
...then he can tear it apart and say, "it's not how *I* would have done it. Case dismissed." ;)
|
Post #156,265
5/21/04 5:09:06 PM
|

Don't you know?
Message board is not a "business application"
--
Buy high, sell sober.
|
Post #156,311
5/22/04 1:35:42 AM
|

Putting words in my mouth
Bryce claims that the procedural version will be much cleaner. Let's see some actual proof.
I didn't claim that. I only claim that it will not be objectively worse.
The real benefit would probably be that a p/r version would be more consistent from developer to developer because OO is too open-ended. Every professed OO guru does a very different design. OO is all over the fricken map because it the Goto of structuring. Relational rules and the "group by task" of procedural will tend to produce a more consistent and predictable result than OO.
________________ oop.ismad.com
|
Post #156,325
5/22/04 7:53:24 AM
|

Actually, what you said was:
"You spend all your code translating back and forth between two discordant paradigms."
So, where's the bloat? Are you ready to admit that it isn't there yet?
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,327
5/22/04 8:10:19 AM
|

Re: Putting words in my mouth
Relational rules and the "group by task" of procedural will tend to produce a more consistent and predictable result than OO. Having seen several large procedural database programs, I question the validity of this statement. "Tend" is a weasel word. Where's your proof?
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,330
5/22/04 10:00:19 AM
|

Code Talks. Bryce Walks.
So let's set our goals a bit more realistic. Let's see if you can produce a version of zIWETHEY software that is not "objectively worse".
|
Post #156,361
5/22/04 1:37:48 PM
|

I shall consider it
________________ oop.ismad.com
|
Post #156,364
5/22/04 1:54:33 PM
|

Thanks.
Looking at this is an opportunity to learn more about the app, and make the discussions a bit more than rhetoric. Even if you don't want to piece together the whole app, perhaps you can at least show how some of the pieces of it could be written from a P/R perspective.
|
Post #156,370
5/22/04 2:37:40 PM
|

If you don't...
... then you forfeit all right henceforth to whine, "show me the code".
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #156,174
5/20/04 11:45:52 PM
|

RE: Are you willing to admit this yet?
Let's see. Wasn't it 1997 or so when this discussion began. And haven't we been through 4 boards (IWETHEY, EZBoard IWETHEY and Jay's OO, and zIWETHEY).
And you really think he's going to admit something after all this time? :-)
|
Post #156,178
5/20/04 11:57:04 PM
5/20/04 11:57:54 PM
|

Even OO fans are mixed about OR-mappers
I did NOT write this:
[link|http://www.c2.com/cgi/wiki?ObjectRelationalMappingCostsTimeAndMoney|http://www.c2.com/cg...CostsTimeAndMoney]
Go argue with those guys.
________________ oop.ismad.com

Edited by tablizer
May 20, 2004, 11:57:54 PM EDT
|
Post #156,181
5/21/04 12:14:30 AM
|

Looks pretty specific.
Took me about two hours to get Hibernate going the first time. Took me another two to port all my old JDBC code to the new mapping stuff. It adds about 5-10% to the runtime costs (which are pretty small to begin with). The cost was nil. Stuff that requires a lot of database interaction back and forth is done in a trigger or stored procedure.
Use a hammer for nails, a screwdriver for screws. This is the basic lesson you have yet to learn.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|