tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Steven J. Owens" <puffm...@darksleep.com>
Subject Re: Accessing resource in WEB-INF outside servlet
Date Mon, 08 Nov 2004 20:56:56 GMT
David Evans wrote:
> >so how do you handle this? do you pass in the connection info to every
> >DAO method, like this:
> >public static Book getBook(String connection, String bookId)
> >public static void updateBook(String connection, Book book)
> >that seems tedious, and since the all of my database

On Mon, Nov 08, 2004 at 09:35:25AM -0500, Shapira, Yoav wrote:
> You can have the beans get a datasource (from a singleton possibly) and
> call its getConnection method.  To encapsulate further, you can just
> expose a getConnection method on said singleton.  That way only one
> class needs to worry about database configuration.  And you have only
> one place to change if you switch from a Class#getResource approach to a
> JNDI one or vice versa.

     That's pretty much the approach I took - I have a singleton
Database class that primarily serves as a single point to get database
connections.  I have separate bean and bean-to-db-mapping classes for
each table.  The bean-to-db-mapping classes grab their connections via
Database.getInstance().getConnection().  The Database singleton loads
a properties file and keeps it as an instance variable, to get the
connection config variables (David, see below for further discussion).

     This worked at the time, and I had bigger problems to worry
about.  The properties file grew into being the source of all of the
application configuration variables.  This works well enough so far,
though it really seems like Properties ought to be a separate
singleton, and that's on my list of things to refactor.

     However, there are other issues that bug me in this setup (mainly
that everybody tells me that deploying and upgrading works much, much
better if you use a WAR file, see my earlier post, Re: Configuration
Management, JSP Compiles, WAR files), so I'm interested in exploring
alternatives.

     The best approach I've come up with so far (based on suggestions
here) is pretty much keeping a singleton config object, except that
the config file is loaded from a file that lives outside the webapp,
passed in via a resource ref defined in server.xml.  One JNDI hit to
load the file, stays in memory after.

     (Oh, and I'm switching to an XML-based config file instead of
properties.  I'm also considering splitting the config up into
multiple files, since a small subset change often, a medium subset
change somewhat often, and the remainder change hardly ever).

Further Discussion

     This approach still leaves the dependency on the properties
singleton AND on the connection singleton hardwired into the mapping
classes.  For David's purposes, sounds like you may want to apply the
buzz-word-du-jour, "Inversion of Control" or "Dependency Injection".

- make an interface, something like ConnectionFactory
- make a singleton that implements ConnectionFactory
- add a constructor to each bean (or to the separate mapper object)
  that takes a ConnectionFactory as a reference.

     So you inject the dependency into the bean, or into the mapper.
At the higher level, you have some setup class that injects the
dependencies into various factory singletons, that then inject them
into the beans or mappers they dispense.

     Let's sort out that bean/mapper question first.  Like I said
above, I have a separate bean and bean-to-db mapper class for each
table.  The mapper class has select, insert, update and delete
methods.  Each takes a bean of the same time as an argument and uses
that to construct the query (WHERE clause for selects, UPDATE clause
for updates, etc).  The select method has two flavors, one that
returns a bean for the first row, another that returns a List of
beans.

     For my reasons on why I set it up that way, see below in the
"Even Further Discussion" section.

     Any way you slice it, we now come to the question of where the
dependency gets injected - how the ConnectionFactory reference gets to
somewhere the mapping method can find it.  You have a single source of
Connections, and you have a set of DBMappers that are designed to take
those connections.

     Looking at it logically, you only have a few options:

1) instantiate each DBMapper with a Connection reference
2) instantiate each DBMapper with a ConnectionFactory reference
3) create a DBMapperFactory and instantiate with a ConnectionFactory
   reference (probably at some early app-wide setup point), then:
   3a) dispense DBMappers with a Connection reference
   3b) dispense DBMappers with a ConnectionFactory reference
   3c) dispense DBMappers with a DBMapperFactory reference (e.g.:
       getDBMapperFactory().getConnectionFactory().getConnection())
4) move the actual database-interaction methods off to some generic
   database interaction class, and keep the Beans and Mappers pretty
   much ignorant of things like Connection.
5) dependency injection: have an outside class set up all of the
   factories and cross-wire them with references to each other
   as appropriate.
  
     In general, I'd say the design tension is between keeping the
application code shallow, and keeping the application code clean and
simple.  Shallow code is less cryptic, but more cluttered and harder
to follow.  Clean code is easier to follow, but mysterious things
happen underneath.

Shallow:

BillMapper billMapper = new BillMapper(ConnectionFactory.getInstance().getConnection()) ;
BillBean billBean = billMapper.select(some selection criteria here) ;

Cleaner:

BillBean billBean = DBMapperFactory.getInstance().getBillMapper().select(some criteria) ;

Even Cleaner (in this version, getBillMapper() is a static method that
then does a call much like the above "Cleaner" approach):

BillBean billBean = DBMapperFactory.getBillMapper().select(some criteria) ;


     (Note: I've never heard this, but a friend just pointed out that,
in his experience a Factory implies Singleton, but Singleton does not
always imply factory, which would make my naming scheme kind of off.)

     You could get even cleaner code, but only at the expense of
either hardcoding the ConnectionFactory access inside the Mapper
(in which case you could probably make the Mapper static):

BillBean billBean = BillMapper.select(some criteria) ;

     Or you could maybe do something funky.  I wonder if you could
have a DBMapper's constructor access a DBMapper static method that
then hits a static class variable that gets prepopulated,
dependency-injection-style, with the ConnectionFactory reference.  It
sounds like you could, but I'm not sure if it'd be worth the hassle.
Effectively this is a static singleton, which I seem to have a
unreasoning prejudice against (okay, I think it was reasoned at some
point in the past, but I've forgotten why).

     Ultimately, the cleaner the code gets, the deeper you have to
dig to figure out where the Connection is really coming from.  There's
a balance.  Particularly, there's a point where the cognitive overhead
of all this extra shenanigans outweighs the cost of just hardcoding
the ConnectionFactory dependency inside the code, and relying on being
able to change it when you want to.  If I were writing this code for
publication as a general purpose, closed-source toolkit, maybe it'd be
worth the hassle for both me and the users of the toolkit, but I'm
skeptical.

     You might also look into how some of the more popular O/R mappers
are designed, like Hibernate.  For that matter, you may want to use
Hibernate instead of rolling your own.  I've been hearing a lot of
good stuff about Hibernate and some similar tools, lately, and I kind
of wish they'd been around when I wrote my bean/db stuff.  I'm
seriously interested in maybe rewriting my stuff and swapping
Hibernate in for a large chunk of it... but I have so much to do that
I haven't even really had time to play with Hibernate.


Even Further Discussion

     Like I said, for each table I have a bean class and a DBMapper
class.  The other alternatives are to have the CRUD methods live on
the bean, or on one great big mapping class.

     I don't like the great-big-mapping-class approach, mainly because
it gloms together the table identity information into one big class.
If you're reading through the code and you see "BillDBMapper", you
know it's about the Bill table (I was going to use "Invoice", but
that's too long :-).  In general, I don't like to throw away
information without a good reason to do so.

     If you wanted to preserve that table identity info, you could do
so at the cost of more awkward method names, like
GreatBigMapper.selectFromBill().  It's too bad you can't have
something more formal delimiting the table name, like maybe
GreatBigMapper.Bill.select().  But wait, you can - make Bill a
separate class.

     If you have a separate class for each table, you can, for
example, use a refactoring IDE (like Intellij IDEA, if I had a license
:-) to easily modify all of the database accesses specific to that
table.  I'm not saying I need to do this often, it simply illustrates
how the table identity is more formally defined in this approach.

     (I'm not sure if there's any reason to use an inner class rather
than a separate class - AFAIK, the only really good reason to use an
inner class is when you want to get inappropriate levels of access to
another class's internals, and I don't think that applies here).

     I also didn't like having the CRUD methods on the bean - no
special reason, other than that the code for the two is fairly easily
separated.  Most of the bean methods don't need to know about the
database methods, though the database methods definitely do need to
know about the bean methods.  Also, it seemed that a large chunk of
the application had to interact with the bean as just a databean, and
another chunk of the application had to deal with the database.

     Having said that, I've noticed since then that all that *really*
needs to be unique on each mapper is the map-bean-to-SQL-clause method
and the map-resultsetrow-to-bean method.  I have since refactored
these out to separate methods, and in fact at some points in the app
where I have some tricky SQL I do just that.  

     For example, I have to join tables and I don't want to do one big
select of the main table and then a zillion small selects for
individual joined rows.  I do the SQL query in a separate method, and
then use the mapper to convert each row to a bean.  This lets me get
the benefit of efficient SQL queries in some crucial spots, but still
keep a clean O/R sort of feel.

     I'm still not *entirely* sure whether I want to take it further
and just have the CRUD methods live on a generic database class, and
pass in a bean and a mapper:

List invoices = GenericDB.selectMany(InvoiceBean, InvoiceMapper);

     I thought about something like:

List invoices = GenericDB.selectMany(InvoiceMapper.getSelectQuery(InvoiceBean)) ;

     But this is really insufficient, since I have to pass the mapper
in anyway, to map the ResultSet back to beans.  So GenericDB might as
well know about using a Mapper to generate the query as well.

     Overall, I kind of like this refactoring, because it makes the
beans and the DBMappers shallower.  The bean just knows about data.
The DBMapper just knows how to produce an SQL statement from a bean,
and a bean from an SQL statement.  The GenericDB just knows about
beans and mappers: how to apply a mapper to a bean to get SQL, and how
to apply a mapper to a ResultSet to get beans.

-- 
Steven J. Owens
puff@darksleep.com

"I'm going to make broad, sweeping generalizations and strong,
 declarative statements, because otherwise I'll be here all night and
 this document will be four times longer and much less fun to read.
 Take it all with a grain of salt." - http://darksleep.com/notablog


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-user-help@jakarta.apache.org


Mime
View raw message