Wednesday, October 7, 2009

Generic DAO Factory for ColdFusion 9

Joe Rinehart has a nice post on creating a generic DAO wrapper for Hibernate in ColdFusion 9. For example, if you wanted to create a UserDAO using the GenericDAO, you could set this up in ColdSpring like:


<beans>

<bean id="userDAO" class="GenericDAO" />

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Then to get a User record from your UserDAO inside your UserService, your code might look like:


component {
public any function get(string id) {
return getDAO().get("User",id);
}
}


While this works, it seems a little weird having to pass in the name of the entity to the DAO. Ideally the code would simply be:


component {
public any function get(string id) {
return getDAO().get(id);
}
}


I decided to play around a little bith with the code and was able to accomplish this with the help of ColdSpring. Rather than having my userDAO point directly to the GenericDAO class, I've created a DAOFactory instead to generate an instance of a DAO. Here's what my config looks like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Here's what my DAOFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("dao",class);
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {

var object = getObject();

object.entityName = getEntityName(missingMethodName);

return object;

}

}


Here's what SingletonFactory.cfc looks like, which the DAOFactory extends:


component {

public any function init(string type, string class) {
variables.type = type;
variables.class = class;

return this;
}

private any function getObject() {
return createObject("component",variables.class);
}

private string function getEntityName(string methodName) {
methodName = replaceNoCase(methodName,"get","");

return left(methodName,len(methodName)-len(variables.type));
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {
return getObject();
}

}


Finally, here's what my GenericDAO.cfc looks like:


component {

this.entityName = "";

public array function list(string criteria, array params) {

var hql = "from " & this.entityName;

if (structKeyExists(arguments,"criteria")) {
hql = hql & " where " & criteria;
}

if (structKeyExists(arguments,"params")) {
return ormExecuteQuery(hql,params);
}

return ormExecuteQuery(hql);
}

public any function get(string id) {
return entityLoad(this.entityName, id);
}

public void function save(any entity) {
entitySave(entity);
}

public void function delete(any entity) {
entityDelete(entity);
}

}


Taking this one step further, you could use the same idea and create really simple Services using a ServiceFactory.

Here's what your coldspring.xml might look like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" factory-bean="serviceFactory" factory-method="getUserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

<bean id="serviceFactory" class="ServiceFactory">
<constructor-arg name="class">
<value>GenericService</value>
</constructor-arg>
</bean>

</beans>


Here's what the ServiceFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("service",class);
}

}


And here's GenericService.cfc:


component {

public void function setDAO(any dao) {
variables.dao = arguments.dao;
}

private string function getDAO() {
return variables.dao;
}

public array function list(string criteria, array params) {
if(!StructKeyExists(arguments,"criteria")){
arguments.criteria = "";
}
if(!StructKeyExists(arguments,"params")){
arguments.params = [];
}

return getDAO().list(criteria,params);
}

public any function get(string id) {
return getDAO().get(id);
}

public any function save(any entity) {
return getDAO().save(entity);
}

public any function delete(any entity) {
return getDAO().delete(entity);
}

}


I took most of this code from Joe's example, but just shrunk it down a little bit for the sake of the post. Also, I haven't hooked this up to a database to test it, so there might be some syntax errors. Finally, I wrote this during a rather boring session at MAX, so I'll admit there's a lot of rambling going on. Hopefully you can kind of follow the logic.

No comments:

Post a Comment