DRY iBatis DAOs build an UserDetailsService
Don't repeat the DAO! with iBatis
DRY Don't repeat yourself - an implementation based on iBatis.
UserService based on Spring's SqlMapClientDaoSupport.In the near future the
UserService will be used by this Pebble installation. So it feels natural to create some OSGi bundles:
- util.dao - holding the DAO interfaces and the
FinderIntroductionInterceptor - util.dao.ibatis - iBatis based implementation:
IbatisCrudDaoImpl - util.hsqldb - provides a
javax.sql.DataSourceas OSGi service - userservice.model - yeah right, a
User;-) - userservice.dao - holds the iBatis based persistence layer with the
UserDao - userservice.service - the transactional service layer:
UserService - userservice.security - a thin adapter implementation publishing the service as
UserDetailsServiceinside the OSGi container
An implementation of FinderIntroductionInterceptor using annotations. Every call to a method named find* on a type implementing CrudDao will be intercepted and the finder code will be executed. In our case IbatisCrudDaoImpl.executeFinder() will be called.
@Aspect
public final class FinderIntroductionInterceptor implements Advice {
@SuppressWarnings("unchecked")
@Around(value = "execution(* de.datenkollektiv.util.dao.CrudDao+.find*(..))")
public Object invoke(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
FinderExecutor finderExecutor = (FinderExecutor) proceedingJoinPoint.getThis();
// check if the called function is a finder method
String methodName = proceedingJoinPoint.getSignature().getName();
Object[] arguments = proceedingJoinPoint.getArgs();
// execute introduced method
return finderExecutor.executeFinder(methodName, arguments);
}
}
Let's have a closer look at the iBatis based CrudDao: It extends Spring's SqlMapClientDaoSupport and implements the needed FinderExecutor as shown in Don't repeat the DAO! modified to use iBatis not Hibernate:
public class IbatisCrudDaoImplextends SqlMapClientDaoSupport implements CrudDao , FinderExecutor { // Type of the entities to persist. private Class entityClass = null; ... public Entity create(final Entity newEntity) { getSqlMapClientTemplate().insert(entityName + ".create", newEntity); return newEntity; } ... @SuppressWarnings("unchecked") public final List executeFinder(final String methodName, final Object[] arguments) { final String queryName = entityClass.getSimpleName() + "." + methodName; if (arguments.length == 0) { return getSqlMapClientTemplate().queryForList(queryName); } if (arguments.length == 1) { if (String.class.isAssignableFrom(arguments[0].getClass())) { return getSqlMapClientTemplate().queryForList(queryName, (String) arguments[0]); } } return getSqlMapClientTemplate().queryForList(queryName, arguments); } }
Some of the Spring glue XML: No surprise compared with the Hibernate implementation mentioned above.
<bean id="abstractIbatisCrudDaoImpl" class="de.datenkollektiv.util.dao.impl.IbatisCrudDaoImpl" abstract="true">
<property name="sqlMapClient">
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
p:configLocation="classpath:userservice-sql-map-config.xml">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
</bean>
<bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="de.datenkollektiv.userservice.dao.UserDao" />
<property name="target">
<bean parent="abstractIbatisCrudDaoImpl">
<constructor-arg>
<value>de.datenkollektiv.userservice.model.impl.UserImpl</value>
</constructor-arg>
</bean>
</property>
</bean>
Where is the SQL? iBatis queries are defined in a sql map.
<sqlMap namespace="USERS">
...
<insert id="UserImpl.create" parameterClass="de.datenkollektiv.userservice.model.impl.UserImpl">
insert into USERS (USERNAME, EMAIL, PASSWORD, REGISTRATIONKEY, ENABLED, ROLES)
values (#username:VARCHAR#, #email:VARCHAR#, #password:VARCHAR#, #registrationKey:VARCHAR#, #enabled:BOOLEAN#, #roles:VARCHAR#)
</insert>
...
<select id="UserImpl.findByUsername" parameterClass="java.lang.String" resultMap="abatorgenerated_IbatisUserImplResult">
select USERNAME, EMAIL, PASSWORD, REGISTRATIONKEY, ENABLED, ROLES
from USERS
where USERNAME = #username:VARCHAR#
</select>
...
</sqlMap>
I will skip the boring details of the user service/security layer. Only showing the short Spring dynamic modules snippet creating the spring-security adapter simply consuming and exposing OSGi services.
<osgi:reference id="userService" interface="de.datenkollektiv.userservice.service.UserService" /> <osgi:service ref="userDetailsService" interface="org.springframework.security.userdetails.UserDetailsService" /> <bean id="userDetailsService" class="de.datenkollektiv.userservice.security.UserDetailsServiceAdapter" autowire="constructor" />
