Thanks for your input.
When someone wants DI/IoC we recommend Spring. They don't use the DAO
framework at all. What they DO provide is transaction management and
DI across various persistence mechanisms (jdbc, ibatis sqlmaps,
hibernate, ...). I know Clinton has worked on a replacement for the
DAO framework because we are all aware of it's shortcomings in light
of current technology practices. A prototype has been created. But,
the OSS implementation has been slow to come and i'm not sure if/when
it will make an appearance in the ibatis project.
In short terms, our goal is to physically separate SQLMaps from the
DAO Framework, considering they are already separate frameworks that
are simply packaged together.
So, i don't see a whole lot of desire in the team to refactor the DAO
framework. We want to simply let it be what it is and role out a new
product when the time is right.
Also, i think there is considerable discussion to be had regarding
whether to use an existing IoC/DI container or build in the
functionality for it with a narrower scope than generic IoC containers
offer. Also, in the course of deciding to use an existing IoC
container or not the discussions needs to be had about which one we
would use. Personally, I made a decision not to use pico container due
to it's underlying philosophy and poor documentation. I have used and
been more intrigued by Spring and HiveMind.
Anyway, I think this dicussion would be better had after Clinton
decides if his DAO replacement framework will be unveiled.
Brandon
On Sun, 30 Jan 2005 15:51:45 -0500, J Aaron Farr <[EMAIL PROTECTED]> wrote:
> Hello all.
>
> First off, I really like iBatis. I used it back in 2002 and I'm
> really excited to see it here in Apache. However, I've never been
> happy with the DAO framework and would really love to see it
> refactored (no offense to the current developers). :)
>
> Specifically, I'm interested in adding some Inversion of Control (IoC)
> features to the iBatis DAO Framework. I'm including one simple patch
> with this email, but what I'd like to do would involve a more
> extensive refactoring which I'll describe below.
>
> IoC is more of a programming practice than a design pattern.
> Basically the idea is to manage who can control what. Generally,
> you consolidate control into a container and carefully manage access
> methods.
>
> Whether you realize it or not, the iBatis DaoManager class is a simple
> container. It contains and manages DAOs. However, there is a lot of
> room for improvement, particularly in the form of IoC. For example:
>
> * Since the DaoManager can be accessed via static methods, DAOs can
> get a hold of their own container and manipulate it. In general,
> this isn't a good practice. DAOs should never need to access the
> container directly.
>
> * DAO implementation have to manage their own dependencies. Right
> now they can do this by manipulating the container (noted above).
> The DaoManager should be able to handle all the DAOs dependencies
> such as inter-DAO dependencies, getting a handle on the local
> transaction, or accessing other external resources all without the
> DAO needing to directly call the DaoManager.
>
> * The DaoManager provides no special lifecycle support for DAOs
> beyond the basic constructor.
>
> Right now the DaoManager class methods represent an amalgamation of
> several aspects:
>
> 1. The DAO Container: the container configures and starts up
> managers and allows clients to access the managers. Right
> now this would be the current static methods.
>
> 2. The DAO Manager: allows access of DAOs to clients. This includes
> some of the public methods like getDao(String str) and
> startTransaction()
>
> 3. DAO Internals: there are some public methods which are designed
> for access by DAOs not DAO clients such as getLocalTransaction
> or getInstance(Dao). Via IoC the need for these methods
> disappears.
>
> Ultimately, refactoring the DAO Framework would split out these
> aspects into different classes, ensuring that client code only got
> access to what it needed.
>
> There are a couple of steps to apply IoC to the DAO Framework. I'm
> not sure which steps you are interested in applying, but here they
> are:
>
> 1. Replace DaoManager daoMap with a PicoContainer
>
> PicoContainer is a simple IoC container and I suggest we use it
> internally to the DAO Framework. It's a small library dependency that
> gives us huge benefits.
>
> The first step is to replace the current daoMap HashMap in the
> DaoManager with a PicoContainer. This is very simple and I've
> attached a patch with does so (you'd also need to add
> picocontainer-1.1.jar to the /lib directory). It makes no changes to
> the API and all the tests and examples still pass.
>
> With this patch, DAOs can be inter-dependent and get access to one
> another without needing to access the DaoManager. Instead, developers
> could use contructor dependency injection. For example, suppose the
> ShoppingCartDAO implementation at some point needs access to an
> AccountDAO instance. Instead of having the ShoppingCartDAO lookup the
> AccountDAO via the DaoManager, the ShoppingCartDAO would simple include
> the AccountDAO as a dependency in its contructor:
>
> public class ShoppingCartDaoImpl implements ShoppingCartDao
> extends BaseDao {
>
> private AccountDao _accountDAO;
>
> public ShoppingCartDaoImpl(AccountDao account){
> _accountDAO = account;
> }
> ...
>
> The PicoContainer takes care of figuring out and handling these
> dependencies, even circular ones.
>
> 2. Introduce a TransactionManager
>
> In order to avoid requiring DAOs to access the local transaction via
> the DaoManager (as seen in examples.dao.impl.map.BaseMapDao), a new,
> relatively simple class could be introduced called the
> TransactionManager. The DaoManager would have control of the
> TransactionManager but DAOs could access the local transaction via
> the TransactionManager.
>
> How would the DAOs get a handle on the TransactionManager? Via
> dependency injection again. The TransactionManager would be added to
> the picocontainer during DaoManager initialization and all DAOs would
> have access to it by declaring a dependency in their contructor:
>
> public class ShoppingCartDaoImpl implements ShoppingCartDao
> extends BaseDao {
>
> private AccountDao _accountDAO;
> private TransactionManager _transManager;
>
> public ShoppingCartDaoImpl(AccountDao account,
> TransactionManager manager){
> _accountDAO = account;
> _transManager = manager;
> }
>
> public void performSomeAction(....){
> SqlMapDaoTransaction trans = (SqlMapDaoTransaction)
> _transManager.getLocalTransaction();
> ...
> }
>
> ...
>
> 3. Include extra-properties in the picocontainer
>
> At this point, the only reason a DAO might need access to the
> DaoManager is to access extra-properties. These two could be added to
> the picocontainer just like the transaction manager example above.
>
> What I'd actually like to see is to allow extra-properties to be more
> than just Strings. One option is to allow scripts to create the
> extra-properties map. This allows for some more advanced
> configuration of DAOs:
>
> dao.xml:
>
> <dao-factory>
> <dao name="Account"
> implementation="examples.dao.impl.map.AccountMapDao"/>
> <dao name="ShoppingCart"
> implementation="examples.dao.impl.map.ShoppingCartDaoImpl"/>
> </dao-factory>
> <extra-properties>
> <property name="sqlmap.path"
> value="com/ibatis/example/persistence/sql-map-config.xml" />
> <script name="com/ibatis/example/script/properties.bsh"
> </extra-properties>
>
> ...
>
> properties.bsh (BeanShell Script)
>
> import java.util.HashMap;
> import java.io.File;
>
> HashMap context = new HashMap();
> context.put("name","value");
>
> File file = new File("src/conf/context-example.xconf");
> context.put("file",file);
>
> return context;
>
> ...
>
> public class ShoppingCartDaoImpl implements ShoppingCartDao
> extends BaseDao {
>
> private AccountDao _accountDAO;
> private File _contextFile;
>
> public ShoppingCartDaoImpl(Map extraProperties, AccountDao
> account){
> _accountDAO = account;
> _contextFile = (File) extraProperties.get("file");
> }
> ...
>
> The final step would be considering refactoring the DaoManager into two
> classes: a DaoContainer and a DaoManager. The DaoContainer would
> include most of the static methods we currently have in DaoManager and
> would be responsible for the configuration and initialization steps.
> However, this would be a pretty big change and break the current API
> (the other steps wouldn't have to), so I'm not sure if the developer
> team would want to go in this direction.
>
> I hope I still have one or two readers at this point. :) Thanks for
> taking the time to consider my proposal. I'm not sure if it's
> anything the iBatis team is interested in, but if so, I'd be willing
> to do all the patch work and the unit tests.
>
> Thanks again!
>
> jaaron
>
>
> Index: DaoManager.java
> ===================================================================
> RCS file: /cvsroot/ibatisdb/ibatisdb/src/com/ibatis/db/dao/DaoManager.java,v
> retrieving revision 1.21
> diff -u -r1.21 DaoManager.java
> --- DaoManager.java 10 Jan 2004 01:42:01 -0000 1.21
> +++ DaoManager.java 30 Jan 2005 19:35:07 -0000
> @@ -8,6 +8,8 @@
>
> import java.util.*;
> import java.io.*;
> +import org.picocontainer.MutablePicoContainer;
> +import org.picocontainer.defaults.DefaultPicoContainer;
>
> /**
> * DaoManager is a facade class that provides convenient access to the rest
> @@ -43,7 +45,8 @@
> protected Properties extraProperties = new Properties();
> protected ThreadLocal localTransaction = new ThreadLocal();
> protected Map daoClassMap = new HashMap();
> - protected Map daoMap = new HashMap();
> +// protected Map daoMap = new HashMap();
> + protected MutablePicoContainer daos = new DefaultPicoContainer();
>
> protected DaoManager() {
> }
> @@ -106,7 +109,7 @@
> }
>
> public Dao getDao(String name) {
> - return (Dao) daoMap.get(name);
> + return (Dao) daos.getComponentInstance(name);
> }
>
> public void addDaoClass(String name, String daoClass) {
> @@ -221,24 +224,24 @@
> String name = (String) i.next();
> String implementation = (String) daoClassMap.get(name);
> try {
> - Class c = Class.forName(implementation);
> - Object dao = c.newInstance();
> - registerDao(name, (Dao) dao);
> + Class dao = Class.forName(implementation);
> + registerDao(name, dao);
> } catch (ClassNotFoundException e) {
> throw new DaoException("DaoManager could not configure DaoFactory.
> The DAO named '" + name + "' failed. Cause: " + e, e);
> - } catch (InstantiationException e) {
> - throw new DaoException("DaoManager could not configure DaoFactory.
> The DAO named '" + name + "' failed. Cause: " + e, e);
> - } catch (IllegalAccessException e) {
> - throw new DaoException("DaoManager could not configure DaoFactory.
> The DAO named '" + name + "' failed. Cause: " + e, e);
> } catch (Throwable t) {
> throw new DaoException("DaoManager could not configure DaoFactory.
> The DAO named '" + name + "' failed. Cause: " + t, t);
> }
> }
> + daos.start();
> + List daoList = daos.getComponentInstances();
> + for(int j =0; j < daoList.size(); j++){
> + daoManagerReverseLookup.put(daoList.get(j),this);
> + }
> }
>
> - protected void registerDao(String name, Dao dao) {
> - daoMap.put(name, dao);
> - daoManagerReverseLookup.put(dao, this);
> + protected void registerDao(String name, Class dao) {
> + daos.registerComponentImplementation(name, dao);
> + // daoManagerReverseLookup.put(dao, this);
> }
>
> protected static void registerDaoManager(Object contextName, DaoManager
> daoManager) {
>
>
>