Création d’un projet Spring + hibernate + web service REST + Spring security. 2 – Création des DAO

Dans un premier billet, j’expliquais la mise en place du projet et la création des objets métiers.
Dans ce deuxième billet de la série, on va ajouter au projet précédemment créé les DAO (Data Access Objects, les objets d’accès aux données).
Commençons donc par définir l’interface DAO de base, que tous les DAO implémenteront. Cette interface va définir la signature des méthodes CRUD :

public interface DaoBase<T extends IOM> {

	/**
	 * . Retourne la classe de l objet metier.
	 *
	 * @return la classe de l'objet metier
	 */
	Class<T> getOMClass();

	/**
	 *
	 * @param entityClass
	 *            entityClass.
	 * @param identifiant
	 *            identifiant.
	 * @return Class<T>.
	 */
	T load(Class<T> entityClass, Serializable identifiant);

	/**
	 *
	 * @param entityClass
	 *            entityClass.
	 * @param id
	 *            identifiant.
	 * @return Class<T>.
	 */
	T get(Class<T> entityClass, Serializable id);

	/**
	 * loads an object from its id. Returns null if no object was found
	 *
	 * @param id
	 *            identifier
	 *
	 * @return Class<T>.
	 */
	T load(Serializable id);

	/**
	 * @return List<T>.
	 */
	List<T> list();

	/**
	 * @param entity
	 *            objet.
	 */
	void delete(T entity);

	/**
	 * Deletion by identifier
	 *
	 * @param identifier
	 *            the identifier of the object to delete
	 */
	void delete(Integer identifier);

	/**
	 * @param entity
	 *            objet.
	 * @return the object
	 */
	T save(final T entity);
}

Comme on peut le constater, on utilise la généricité dans cette interface, afin que tous les objets qui implémentent IOM (c’est à dire tous nos objets métiers) puissent l’utiliser. La méthode getOMClass() permettra de récupérer la classe exacte de l’objet métier manipulé.
On définit ensuite l’implémentation de cette classe :

public class DaoBaseImpl<T extends OMBase> extends HibernateDaoSupport implements DaoBase<T> {

	@Override
	public void delete(T entity) {
		delete(entity.getIdentifier());

	}

	@Override
	public void delete(Integer identifier) {
		if (identifier != null) {
			// checks if it exists
			T object = load(identifier);
			if (object != null) {
				this.getHibernateTemplate().delete(object);
			}
		}

	}

	public final List<T> executeListCriteria(final DetachedCriteria requete) {
		return (ArrayList<T>) getHibernateTemplate().findByCriteria(requete);
	}

	public final T executeObjectCriteria(final DetachedCriteria requete) {
		List<T> liste = (List<T>) getHibernateTemplate().findByCriteria(requete);
		if (liste.size() == 0) {
			return null;
		}
		return liste.get(0);
	}

	@Override
	public T get(Class<T> entityClass, Serializable id) {
		final Object result = this.getHibernateTemplate().get(entityClass, id);
		return (T) result;
	}

	@Override
	public Class<T> getOMClass() {
		ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
		return (Class<T>) parameterizedType.getActualTypeArguments()[0];
	}

	@Override
	public List<T> list() {
		return (List<T>) this.getHibernateTemplate().loadAll(getOMClass());
	}

	@Override
	public T load(Class<T> entityClass, Serializable identifier) {
		final Object result = this.getHibernateTemplate().load(entityClass, identifier, null);
		return (T) result;
	}

	@Override
	public T load(Serializable id) {
		final Object result = this.getHibernateTemplate().get(getOMClass(), id);
		return (T) result;
	}

	@Override
	public T save(T entity) {
		this.getHibernateTemplate().saveOrUpdate(entity);
		return entity;
	}

}

On peut voir que cette classe hérite de HibernateDaoSupport, une classe fournie par Spring. Utiliser cette classe n’est pas nécessaire, et n’est pas forcément recommandée. Elle n’est pas nécessaire parce que d’autres mécanismes permettent d’utiliser simplement Spring et Hibernate. Elle n’est pas recommandée parce qu’elle implique un couplage avec Spring. Dans notre cas, comme il s’agit principalement de montrer un exemple de sécurisation d’un Web Service Rest, on va l’utiliser quand même. On peut assez facilement trouver des articles qui indiquent comment se passer de HibernateDaoSupport.
On peut voir que toutes les méthodes font appel à la méthode getHibernateTemplate(), qui est disponible grâce à HibernateDaoSupport.

Maintenant qu’on a créé les DAO de base, on va pouvoir commencer à s’attaquer à la configuration du projet par Spring.
La première chose à faire est de créer le fichier applicationContext.xml (il peut prendre n’importe quel nom). Dans mon cas, ce fichier sert juste à inclure d’autres fichiers de configuration de Spring. On va placer ce fichier dans le dossier WEB-INF de l’application web (on peut le placer ailleurs).
Voici le contenu du fichier :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- Base de donnée -->
	<import resource="applicationContext-db.xml" />

	<!-- DAO -->

	<import resource="applicationContext-dao.xml" />

	<!--Services -->
	<import resource="applicationContext-service.xml" />

	<!--Security -->
	<import resource="applicationContext-security.xml" />

</beans>

On voit que ce fichier fait appel à 4 autres fichiers : un fichier qui permet de configurer l’accès à la base de données (applicationContext-db.xml), un fichier de configuration des DAO (applicationContext-dao.xml), un fichier de configuration de services (applicationContext-service.xml) et un fichier de configuration de la sécurité (applicationContext-security.xml).
On déclare ensuite ce fichier dans le web.xml de l’application :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
	<context-param>
	  <param-name>contextConfigLocation</param-name>
	  <param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

Pour l’instant, on a juste géré les DAO de bases. On va donc s’intéresser aux deux premiers fichiers.
Tout d’abord, le fichier de configuration de la base de données :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost/jfreenotes" />
		<property name="username" value="jfreenote" />
		<property name="password" value="jfreenote" />
		<property name="validationQuery" value="select 1" />
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<prop key="hibernate.connection.release_mode">auto</prop>
				<prop key="hibernate.bytecode.provider">javassist</prop>
				<prop key="hibernate.show_sql">false</prop>
				<prop key="hibernate.cache.use_second_level_cache">false</prop>
			</props>
		</property>

		<property name="packagesToScan">
			<list>
				<value>fr.mael.jfreenote.transverse.om</value>
			</list>
		</property>
	</bean>

</beans>

On définit tout d’abord un bean qui va instancier une BasicDataSource. Il n’y a pas grand-chose à dire sur ce bean, il me semble que tout est clair.
On définit ensuite un second bean : une Session Factory et plus exactement une AnnotationSessionFactoryBean. Elle va permettre de gérer les annotations hibernate qu’on a indiquées dans les objets métiers. On lui injecte la DataSource définie précédemment, on définit également quelques propriétés hibernate (voir la doc pour connaitre leur signification). Et on définit quels packages doivent être scannés à la recherche des annotations. On indique ici tout le package d’objets métiers.

Voyons maintenant à quoi ressemble le fichier applicationContext-dao.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- DAO -->
	<bean id="daoBase" class="fr.mael.jfreenote.persistance.dao.base.impl.DaoBaseImpl" abstract="true">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

</beans>

Pour l’instant, on ne fait que définir notre dao de base, qui utilise l’implémentation qu’on a créée précédemment. On indique à Spring que le bean est « abstract » car il ne servira pas directement, mais sera utilisé comme « parent » par les DAO.

On va maintenant pouvoir définir les interfaces de nos DAO. Les voici :

public interface FileDao extends DaoBase<File> {
}
public interface FileTypeDao extends DaoBase<FileType> {
}
public interface NoteDao extends DaoBase<Note> {
}
public interface UserDao extends DaoBase<User> {

	User loadByUserName(String username);

}

La seul interface qui n’est pas vide est celle de UserDao. Elle définit la signature d’une méthode qui est utilisée par Spring Security (dans les services, qu’on définira plus tard) , qui permet de charger un utilisateur d’après son « username ».

Définissons maintenant les implémentations de ces interfaces :

public class FileDaoImpl extends DaoBaseImpl<File> implements FileDao {
}
public class FileTypeDaoImpl extends DaoBaseImpl<FileType> implements FileTypeDao {
}
public class NoteDaoImpl extends DaoBaseImpl<Note> implements NoteDao {
}
public class UserDaoImpl extends DaoBaseImpl<User> implements UserDao {

	@Override
	public User loadByUserName(String username) {
		DetachedCriteria criteria = DetachedCriteria.forClass(getOMClass());
		criteria.add(Restrictions.eq("login", username));
		return executeObjectCriteria(criteria);
	}

}

Les implémentations des trois premières interfaces sont vides également. Mais par les mécanismes de l’héritage, elles héritent de toutes les méthodes de DaoBaseImpl. UserDaoImpl définit la méthode loadByUserName.
Cette méthode crée juste un DetachedCriteria, auquel elle ajoute une restriction d’égalité sur le login. Puis elle fait appel à la méthode de DaoBaseImpl qui permet de charger un objet unique. Attention, l’utilisation de « login » en dur est une mauvaise pratique. Il faudrait normalement utiliser un enum, pour centraliser tous les appels à cette propriétés qui pourraient être faits (ici, je ne l’ai pas fait, pour ne pas allonger encore un peu plus le billet, qui est déjà long).

Maintenant qu’on a défini nos DAO, on peut les déclarer dans le fichier de configuration de Spring (applicationContext-dao.xml) qui devient :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- DAO -->
	<bean id="daoBase" class="fr.mael.jfreenote.persistance.dao.base.impl.DaoBaseImpl" abstract="true">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<bean id="noteDao" class="fr.mael.jfreenote.persistance.dao.impl.hibernate.NoteDaoImpl" parent="daoBase"/>
	<bean id="userDao" class="fr.mael.jfreenote.persistance.dao.impl.hibernate.UserDaoImpl" parent="daoBase"/>
	<bean id="fileTypeDao" class="fr.mael.jfreenote.persistance.dao.impl.hibernate.FileTypeDaoImpl" parent="daoBase"/>
	<bean id="fileDao" class="fr.mael.jfreenote.persistance.dao.impl.hibernate.FileDaoImpl" parent="daoBase"/>
</beans>

Il n’y a pas grand commentaire à faire, à part que les bean créés déclare daoBase comme « parent », ils auront ainsi accès au bean sessionFactory injecté dans le parent.

Voilà pour ce qui est de la configuration des DAO.
La suite dans le prochain billet.

Vus : 1556
Publié par mael : 17