mardi 4 janvier 2011

Generic dao avec Spring Jdbc



Les opérations de base pour la persistance des données sont toutes identiques quelques soit
l’objet à sauvegarder, les méthodes en question sont celles d’un CRUD : enregistrement
(Create), lecture (Read), mise à jour (Update) et suppression (Delete).

La plupart du temps ces méthodes sont répétées et redéfinies dans chacun des DAO de notre
application, la seule différence notable est le type des objets que ces méthodes prennent en
paramètre, cependant avec Java 5 et les generics pourquoi ne pas les écrire une fois pour
toutes dans un DAO générique ? cela est possible et est considéré comme une bonne pratique,
la redondance des données étant à éviter à tout prix.


La classe centrale du support JDBC de Spring se nomme org.springframework.
jdbc.core.JdbcTemplate. Elle propose une API très directe pour effectuer toute opération en base de données, sans se soucier des problématiques telles que la gestion des ressources ou des exceptions. Généralement, un JdbcTemplate est injecté dans DAO, qui l’utilise ensuite pour effectuer toutes ses opérations en base de données


public interface IGenericDao <T extends Serializable>{
           
    T find(String xmlName, String sqlKey,ParameterizedRowMapper<T>   mapper,Object... args);
  
    T find(String sql,ParameterizedRowMapper<T> mapper,Object... args);
   
    T singleResult(String xmlName, String sqlKey,ParameterizedRowMapper<T>  mapper,Object... args);

    T singleResult(String sql,ParameterizedRowMapper<T> mapper,Object... args);

   Collection<T> findAll(String xmlName, String  sqlKey,ParameterizedRowMapper<T> mapper, Object... args);

    Collection<T> findAll(String sql,ParameterizedRowMapper<T> mapper, Object... args);

     int saveOrUpdate(String xmlName, String sqlKey,Object... args);
    
     int saveOrUpdate(String sql,Object... args);
             
}

Dans Spring, il existe des classes de support pour les DAO fondés sur JDBC, il est  préconisions d’utiliser la classe de support : SimpleJdbcDaoSupport. Pour bénéficier de ce support, un DAO doit hériter de cette classe :







public class GenericDaoImpl <T extends Serializable>
                  extends SimpleJdbcDaoSupport implements IGenericDao<T>
{
     
      private SqlParser sqlParser;
               
                public T find(String xmlName, String sqlKey, ParameterizedRowMapper<T> mapper, Object... args) {
                              
                               String sql = sqlParser.getSQL(xmlName, sqlKey);
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return  (results.isEmpty() ? null : results.get(0));
                }
               
               
                public T find(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return  (results.isEmpty() ? null : results.get(0));
                }
               
               
                public T singleResult(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               return (T) DataAccessUtils.singleResult(getSimpleJdbcTemplate(). query(sql,mapper,args));
                }
   
             public Collection<T> findAll(String sql, ParameterizedRowMapper<T> mapper, Object... args) {
                               List<T> results = getSimpleJdbcTemplate().query(sql,mapper,args);
                               return results;
                }
            public int saveOrUpdate(String xmlName, String sqlKey, Object... args) {
                               String sql = sqlParser.getSQL(xmlName, sqlKey);
                               return getSimpleJdbcTemplate().update(sql, args);                  
                }
               
      ...
}



L’idée est d’externalisé les déclarations des requêtes SQL dans des fichiers xml. Ce qui ne
permettra de changer les requêtes a l’exécution, car le code SQL n’est pas figé dans le code
java.

Exemple

<sqls name="user">
      <sql name="SELECT_USER_SQL">
            <description></description>
            <query>
               <![CDATA[
            SELECT USR_PRENOM,USR_NOM,USR_MAIL,RLE_ID,USR_ACTIF FROM USR_USER WHERE USR_ID = ?
               ]]>
            </query>
      </sql>
      ...
<sqls>

Signature de la méthode qui permet de retourner le code sql souhaité

String getSQL(String name,String sqlKey);            

Name   : attribut du tag sqls, qui représentera dans la plupart des cas le nom logique du fichier xml 
sqlKey : attribut du tag sql, qui représente le nom de la requête SQL

exemple

String sql = sqlParser.getSQL("user","SELECT_USER_SQL">);



 



2.5) utilisation des Annotations

L’objectif ici et d’avoir des requêtes sql paramétré avec des annotations.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Query {
      String name();
      String query();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented

public @interface Queries {
      Query[] value();
}


Exemple d’utilisations :

@Queries({
       @Query(name="rechercheParId",query="Select * From Personne Where id = ?"),
       @Query(name="rechercheParNom",query="Select * From Personne Where nom = ?"),
       @Query(name="rechercheParprenom",query="Select * From Personne Where prenom = ?"),
})

Pour parser ces requêtes sql, on utilisera l’introspection sur les classes dao

Map<String, Map<String, String>> sqlAnnotation = new HashMap<String, Map<String, String>>();

Class<?> clazz = bean.getClass();
if(clazz.isAnnotationPresent(Queries.class)){
Queries queries = clazz.getAnnotation(Queries.class);
       Query[] ListQuery   = queries.value();
       for(int i = 0 ; i < ListQuery.length ; i++ ){
             Query query = ListQuery[i];
             Map<String, String> sqlData = new HashMap<String, String>();
             sqlData.put( query.name(), query.query());
             sqlAnnotation.put(beanName, sqlData);
       }
}


L’accès aux sources de données est configuré dans le serveur d'applications web sphere 6. et
pour qu’une application Spring puisse accéder à une ressource telle qu'une source de données
JDBC, elle doit utiliser un fournisseur de ressource géré par ce serveur d'applications. Pour ce
faire, procédez comme suit.

Pendant le développement, consulter la configurez de module WAR avec ça référence de ressource dans le fichier web.xml.

<resource-ref id="ResourceRef_1127121358950">
                               <description></description>
                               <res-ref-name> jdbc/DTB-DS </res-ref-name>
                               <res-type>javax.sql.DataSource</res-type>
                               <res-auth>Container</res-auth>
                               <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Dans le fichier de  configuration de l'application Spring, déclarez un bean qui référence un fournisseur de ressource géré par le serveur d'applications. Affectez à la propriété jndiName la valeur java:comp/env/ suivie de la valeur de la propriété res-ref-name que vous avez déclarée dans la référence de ressource.

Exemple :

<bean id="wasDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
                               <property name="jndiName" value="java:comp/env/jdbc/DTB-DS"/>
                               <property name="lookupOnStartup" value="false"/>
                               <property name="cache" value="true"/>
                               <property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>

Puis n’oublie pas d’injecté la dataSource dans le Bean GenericDaoImpl

<bean id="genericDao" class="com.cacib.gdp.dao.GenericDaoImpl" >
                <property name="dataSource" ref="wasDataSource"  />
              ...
</bean>


Exemple d’interface d’un DAO propre à notre application GDP qui doit pouvoir appeler les méthodes du DAO générique plus d’autres qui lui sont propre :

public interface IUserDaoJdbc {

            List<User> listUsersActif();
            List<User> listUsers();
            int addUser(User user);
            ...
}





Son implémentation, désormais les méthodes find, saveOrUpdate, findAll sont accessibles.

public class UserDaoJdbcImpl implements IUserDaoJdbc {

        private IGenericDao<User> dao;
               
         public User getUserById(String id) {
                           return dao.singleResult("user", "SELECT_USER_SQL", new UserMapper(), new Object[]{id});
         }

         public List<User> listUsers() {
                  return new ArrayList<User>( dao.findAll("user", "SELECT_All_USER_SQL", new UserMapper()) );
          }
     
         public int desactiveUsers() {
                                 return dao.saveOrUpdate("….");
         }

         public boolean isRedacteur(String userId) {
                               String sql  = dao.getSQL("user", "SELECT_IS_REDACTEUR_SQL");
                               int res = dao.getJdbcTemplate().queryForInt(sql,new Object[]{userId});
                               return ( 0 != res ? true : false);
          }
         ...
}

A noté que le Bean dao, sera injecté par Spring.

3.1) Utilisation des Mapper :

Il est préconisé d’utilisé des RowMappers, dont le but est de transformer chaque ResultSet JDBC en un objet.

Exemple:

public class UserMapper implements ParameterizedRowMapper<User>{

      public User mapRow(ResultSet rs, int arg1) throws SQLException {
                               User user = new User();
                               user.setNom(rs.getString("USR_NOM"));
                               user.setPrenom(rs.getString("USR_PRENOM"));
                               user.setEmail(rs.getString("USR_MAIL"));
                               user.setRoleId(rs.getString("RLE_ID"));
                               user.setActif(rs.getInt("USR_ACTIF"));
                               return user;
                }
}


Voici un exemple de fichier de configuration Spring pour l’utilisation du DAO générique, avec la source de données utilisant JNDI


<beans       
     <bean id="wasDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
                               <property name="jndiName" value="java:comp/env/jdbc/DTB-DS"/>
                               <property name="lookupOnStartup" value="false"/>
                               <property name="cache" value="true"/>
                               <property name="proxyInterface" value="javax.sql.DataSource"/>
    </bean>
   
     <bean id="genericDao" class="com.cacib.gdp.dao.GenericDaoImpl" />
     <bean id="userDAO" class="com.cacib.gdp.dao.impl.UserDaoJdbcImpl" />
     <bean id="derogationDao" class="com.cacib.gdp.dao.impl.DerogationDaoJdbcImpl" />
                 
    <bean id="sqlParser" class="com.cacib.gdp.dao.sqlparser.SqlParserXmlImpl">
                               <constructor-arg>
                                               <list>    <!- - fichiers sqls -->
                                                               <value>classpath:sqls/usersqls.xml</value>
                                                               <value>classpath:sqls/derogationsqls.xml</value>
                                               </list>
                               </constructor-arg>
    </bean>
</beans>

Aucun commentaire:

Enregistrer un commentaire