IWETHEY v. 0.3.0 | TODO
1,095 registered users | 0 active users | 0 LpH | Statistics
Login | Create New User
IWETHEY Banner

Welcome to IWETHEY!

New Is this guy right about Spring?
Obviously he's trying to promote his own product, so will present the alternative in the worst possiblt light but [link|http://www.theserverside.com/articles/article.tss?l=SQLExecutor|this guy] says:
I looked at some already existing JDBC frameworks, but they either didn't provide the exception handling flexibility I wanted or they were overly complicated to use. The JDBC Spring Framework fell into the latter category-excellent in many respects, but it required creating a subclass for each query, learning a somewhat complicated exception hierarchy, and requiring framework users to implement anonymous inner classes.
I haven't seen this yet, but I've only done trivial examples so far. Is it going to get as bad as he says, or is that only if you're doing something really obscure?
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New Uneducated guess
required creating a subclass for each query
And the problem with that is? In an OOP framework, this is exactly the thing that you want to happen, as subclassing means that a lot of the grunt work is taken care of. What you don't want is raw access to the database with no layer. If you wanted that, you don't want a framework.
learning a somewhat complicated exception hierarchy
IIRC, spring rips out the checked exceptions java experiment. You only have to catch exceptions if you really need to.
requiring framework users to implement anonymous inner classes
That's just Java for you. Delegates and Callbacks.
New Here's what's wrong with it
Now before I start, I should point out that it's my first reaction. But the problem I have with a subclass for each query -- at least as implemented by Scott below -- is that you end up with classes designed according to the db schema. Got a product table? Need a product query class and subclasses. Got a category table? Need a category query class and subclasses.

This could just be a matter of the trivial example suggesting a general practice that isn't actually accurate. But I worked with a framework before that was very tightly tied to the underlying schema, in that the base classes were named according to the table names. Scott's example reminds me of that framework. And we had a case where a schema change required changes all throughout the code.

I think the problem I'm having is that the degree of modularization in the examples is out of proportion to the requirements of the trivial examples. I'm used to objects having more functionality than in these examples.

While I understand the idea of small pieces each doing small things, and I see how Spring is using the xml files to allow multiple implementations to be swapped out, so far it seems like each piece is doing such a small amount that I wouldn't bother making the individual classes.
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New But there wasn't a subclass for every query.
My examples were intended to point out that such a design isn't necessary, and to discuss the article you linked.

A data manager is there to provide an interface to the database. Whether that's a one-to-one mapping or not depends on the types of operations you need to perform. I have another query that returns calculated data and history from a nested view. The objects being created consist of a data object with a collection of history objects. Neither of these represents the actual structure of the data within the database.

I will point out, however, that typical CRUD applications will have a one-to-one mapping, since they are basically table maintenance applications.

A data manager is also intended to localize required changes when a schema changes. But schema changes are schema changes. At the very least you're going to have a change in the form that creates the data, a change somewhere in mapping code (unless there is an exact one-to-one relationship and you can do this automatically), in any stored procedures wrapping the table, and the table itself. With a data manager, none of the query/update code in the rest of the application changes (other than field additions, perhaps), because the interface is confined to one location. In fact, you can swap out a data manager with a mock data manager that provides and manipulates canned data for consistent unit testing of the rest of the application, and none of the other code will know the difference.

I would point out that, while I used it in the example, the technique of passing a recordset to an object to construct itself is only useful when you have non-hierarchical data being returned. In Oracle I have an Oracle object mapping library that automatically maps database object types to Java beans using reflection.

Perhaps the discussion will be more fruitful if you describe the sort of framework that you would rather use?
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
New It *is* about the size, then
What you describe is actually at least one more layer than the sample code seems to use. First a layer to abstract the db connection. Then a layer to model the db schema. Then a layer to map busines objects to the db schema layer. That makes perfect sense, but neither you nor any of the tutorials explicitly said that.

The objects I tend to work with are already way too large. We're working on refactoring, but we never considered moving the data access completely out of the business objects.

We're currently working on moving all db access out of the display pages and into the business objects. Maybe once that's done we can start moving it to it's own layer. I'm not confident it will be possible though, considering how ugly most of our shemata are.
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New What that achieves.
That approach means you tend to push the crud down closer to the database as you refactor. That's good, in general, because you also tend to rewrite and refactor it away as you push down the layers. Ideally, it all gets refactored away. But in real life, that doesn't happen. What you can probably hope for are ways of isolating the troublesome stuff.

Mind you, figuring out how much knowledge of the database schema to remove from the business objects is difficult.

Wade.

Is it enough to love
Is it enough to breathe
Somebody rip my heart out
And leave me here to bleed
 
Is it enough to die
Somebody save my life
I'd rather be Anything but Ordinary
Please

-- "Anything but Ordinary" by Avril Lavigne.

New Which is this?
First a layer to abstract the db connection.
I just want to check what you think it is...

Personally, I hate having any knowledge of data access in the business objects. That just leads to all kinds of mess.
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
New The jdbc/connection pooling shtuffs
One note about the data access layer. In an ideal world, all selects are through views and all crud is through stored procedures. (Yes, this assumes you're using a Real Database[tm]. :-/ ) If you've already got that, how important is keeping the data access out of the business objects? The views and stored procs are already abstracting it.
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New Re: Ideal world
You can't do everything through a view. Sometimes you want your reads coming through stored procedures as well.

Keeping the data access out of the business objects makes unit testing easier.

If you need to serialize your business objects (by putting them in a session, for example) you can't have references to non-serializable objects in them, like database connections and the like.

There's also a lot of management that goes into the data access, like transactions, exceptions, and so on. A good deal of the code associated with data access is managing this stuff, and a data manager lets you do that centrally as opposed to having things scattered across umpty million business objects.
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
New Right, meant "view or procedure"
This does solve our current problem of how to unit test the classes with db access. I'm guessing at some point, though, the data access classes themselves can't really be unit tested.
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New No, those can be tested as well.
As long as the data is amenable. There are certain things in our system that defy easy testing because of the setup involved.

CRUD stuff is fairly easy to test. Use one data manager method to create the test data and another to remove it. I usually make the data include something that designates it as a unit test record, eg. prepending user names with "utest_". Then in the tear down test method I run a delete where user_id like 'utest_%'.

I usually build the data manager classes first, with unit tests. Then I build a mock data manager to the same interface. As a result, when I get to the controller and JSP portions I don't have to worry about data issues.
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
New Somewhat.
Some of his observations are correct, but the conclusions he draws are iffy. Notice that after he slags Spring, he uses straight JDBC in his comparison examples.

but it required creating a subclass for each query
Incorrect:
counter = new SqlFunction(ds, "select count(quote) as lrpdcount from quote where board_id = ?");\ncounter.declareParameter(new SqlParameter(java.sql.Types.INTEGER));\ncounter.compile();\nSystem.out.println(counter.run(boardId));
where ds is a bog standard JDBC datasource instead of his own pooling object (who writes their own pooling objects these days? Wait, don't answer that...). See the user queries below as well.

His example with the recordset is a bit disingenuous, since very rarely do you just want to print out a tabular query result. See the last example below.

learning a somewhat complicated exception hierarchy
As Chris pointed out, the exceptions are unchecked. If your exceptions signify a fatal condition (which is most of the time), then you want them handled by a central handler, and you don't have to know jack about the exception hierarchy.

Here's the equivalent code to his error checking example (except that I actually used braces like you should, instead of trying to make the code look tiny by omitting them...):
try { some data access code here }\ncatch (DataIntegrityViolationException v)\n{\n    applyDataIntegrityViolationRecovery();\n}\ncatch (DeadlockLoserDataAccessException d)\n{\n    applyDeadlockRecovery();\n}
So, learn a slightly complicated exception hierarchy, or learn a slightly complicated method call API. Note that an exception hierarchy allows you to stack your error handling more easily without if/then tests in each stack.

Looks to me like he copied Spring with a slightly different API (and probably a lot less functionality). Some of what Spring does allows you to manage your transactions outside of the data access code itself, for example. Spring also allows you to use things like Toplink or Hibernate with the same exception handling.

requiring framework users to implement anonymous inner classes.
this.allUsersQuery = new MappingSqlQuery(ds, "SELECT * FROM users")\n{\n    protected Object mapRow(ResultSet rs, int count) throws SQLException\n    {\n        return new User(rs);\n    }\n};\nthis.allUsersQuery.compile();\nList users = this.allUsersQuery.execute();
<dry>Oh, that was difficult.</dry> Notice the convenient lack of loop processing to run through the result set, such as SQLExecutor requires.

Or if you want to parameterize it:
this.userQuery = new MappingSqlQuery(ds, "SELECT * FROM users where user_id = ?")\n{\n    protected Object mapRow(ResultSet rs, int count) throws SQLException\n    {\n        return new User(rs);\n    }\n};\nthis.userQuery.declareParameter(new SqlParameter("user_id", Types.VARCHAR));\nthis.userQuery.compile();\nUser u = (User) this.userQuery.findObject("spork");

Owing to Spring's callback nature, you can do a lot fancier things as well, such as processing a stored procedure's returned cursor transparently.

Now, you're not strictly required to subclass things to do your queries, but if you do, you can create a more object-oriented approach to data access. I'm not sure why he thinks this is a bad thing.

Example combining the above:
private class UserQuery extends MappingSqlQuery\n{\n    public UserQuery(DataSource ds)\n    {\n        super(ds, "SELECT * FROM users");\n        compile();\n    }\n\n    public ViewRegMappingSqlQuery(DataSource ds, String where)\n    {\n        super(ds, "SELECT * FROM users WHERE " + where);\n    }\n\n    protected Object mapRow(ResultSet rs, int count) throws SQLException\n    {\n        return new User(rs);\n    }\n}\n\nthis.allUsersQuery = new UserQuery(ds);\n\nthis.userQuery = new UserQuery(ds, "user_id = ?");\nthis.userQuery.declareParameter(new SqlParameter("user_id", Types.VARCHAR));\nthis.userQuery.compile();\n\nthis.loginQuery = new UserQuery(ds, "user_id = ? AND password = ?");\nthis.loginQuery.declareParameter(new SqlParameter("user_id", Types.VARCHAR));\nthis.loginQuery.declareParameter(new SqlParameter("password", Types.VARCHAR));\nthis.loginQuery.compile();\n\nthis.lemurQuery = new UserQuery(ds, "country = 'Madagascar'");\nthis.lemurQuery.compile();

Now, granted he does say that he's just trying to make a very simple SQL framework, but I really don't see the added benefits. And as soon as you do something that he didn't allow for in his framework, you're reduced to either using straight JDBC, mixing frameworks, or rewriting the entire thing. I'll go with the tool that I know can handle anything to start with.

[Edit: slight formatting changes, fixed bug with WHERE clauses in last example]
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
Expand Edited by admin Feb. 20, 2005, 05:29:34 PM EST
New Is the sample code right?
public ViewRegMappingSqlQuery(DataSource ds, String where)\n        {\n            super(ds, "SELECT * FROM users WHERE " + where);\n        }\n\n...\n\nthis.loginQuery = new UserQuery(ds, "WHERE user_id = ? AND password = ?");
Is the 'WHERE' duplicated? It looks like it should be in the base class or the parameters in the child classes, but not both.
===

Purveyor of Doc Hope's [link|http://DocHope.com|fresh-baked dog biscuits and pet treats].
[link|http://DocHope.com|http://DocHope.com]
New Fixed.
Error in transcription, sorry.
Regards,

-scott anderson

"Welcome to Rivendell, Mr. Anderson..."
     Is this guy right about Spring? - (drewk) - (13)
         Uneducated guess - (ChrisR) - (9)
             Here's what's wrong with it - (drewk) - (8)
                 But there wasn't a subclass for every query. - (admin) - (7)
                     It *is* about the size, then - (drewk) - (6)
                         What that achieves. - (static)
                         Which is this? - (admin) - (4)
                             The jdbc/connection pooling shtuffs - (drewk) - (3)
                                 Re: Ideal world - (admin) - (2)
                                     Right, meant "view or procedure" - (drewk) - (1)
                                         No, those can be tested as well. - (admin)
         Somewhat. - (admin) - (2)
             Is the sample code right? - (drewk) - (1)
                 Fixed. - (admin)

This is nothing compared to Grand Theft Auto III, because you can't steal a taxi cab, pick up somebody, then drive into the ocean with him.
64 ms