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 [EMAIL PROTECTED] "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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]