juin
2008
Bonjour,
Il s’agit ici de présenter comment configurer, créer et utiliser un DAO (Data Access Object) en utilisant Spring JDBC. Pour ce faire, j’utiliserais quelques nouveautés de Spring 2 et 2.5 comme le SimpleJdbcTemplate, les annotations, etc. le tout dans le contexte d’une application Web.
Vous aurez beson au moins de Spring.jar (version 2.5) et d’un connecteur JDBC dans le classpath.
Aussi il faut configurer l’application Web pour qu’elle soit Spring-aware (J’en parle dans ce billet dans mon blog).
On commence par le fichier de configuration de Spring applicationContext.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"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd>
<!--
Active la prise en charge par Spring de diverses annotations.
-->
<context:annotation-config />
<!--
Active le scan par Spring des beans pour diverses annotations.
On spécifie le package de base où effectuer le scan.
-->
<context:component-scan base-package="exemple.jdbc" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="org.gjt.mm.mysql.Driver"
p:url="jdbc:mysql://localhost/exemple"
p:username="****" p:password="*****">
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
</beans>
On a défini:
- annotation-config: indique à Spring d’évaluer divers annotations comme @Transactional, etc.
- componenet-scan: pour ne plusa voir à déclarer nos beans dans applicationContext.xml, on indique à Spring un package de base dont tous les classes seront scannés pour voir s’ils sont annotés par l’un des stéreotypes de Spring.
- datasource: comme son nom l’indique, permet de spécifier où et comment accéder à la base de données. Ici, j’utilise MySQL comme base de données.
- transactionManager: spécifie le gestionnaire de transactions.
Passons ensuite à la création d’un PAO (Persistence Anemic Object) qui n’est plus d’autre qu’un conteneur de données (JavaBean).
public class Person extends BaseBean {
private String firstName;
private String lastName;
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Person() {
super();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Rien à expliquer ladedans je crois
Passons maintenant aux choses sérieuses, i.e. la définition de notre DAO.
Je commence par spécifier l’interface du DAO:
public interface IGenericDao<T extends BaseBean> {
List<T> select();
void insert(T e);
void update(T e);
void delete(T e);
}
C’est une interface paramétrée qui définit les opérations classiques de persistance (listing, insertion, mise à jour et suppression).
Et on va l’implémenter en utilisant Spring JDBC:
@Repository
public class PersonDao implements
IGenericDao<Person> {
}
Rien d’intéressant pour l’instant à part l’annotation @Repository que j’ai appliqué à PersonDao pour le déclarer en tant que Spring bean etout en précisant son rôle (accès aux données).
On va utiliser SimpleJdbcTemplate offert par Spring pour communiquer avec la base de données.
J’ajoutes donc un champ de ce type dans mon Dao:
protected SimpleJdbcTemplate jdbcTemplate;
le constructeur de SimpleJdbcTemplate prend un datasource comme paramètre, et justement, on vient d’en déclarer un dans applicationContext. Pas la peine de le récupérer à la main vu que Spring peut le faire automatiquement, il suffit juste de déclarer un champ de ce type comme une dépendance.
J’ajoute donc un mutateur (setter) prenant un DataSource comme paramètre tout en l’annotatnt avec @Autowired pour indiquer qu’il est automatiquement injecté par type:
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
Puisque je n’ai pas besoin du DataSource dans ma classe, je n’en garde pas une référence et je me contente de le passer en paramètre au constructeur du SimpleJdbcTemplate.
On va maintenant implémenter l’opération select qui récupère tous les lignes de la base de données. Pour ce faire, on utilise le SimpleJdbcTemplate.query qui permet d’exécuter une requête de type select sur une base de données. Cette méthode existe en plusieurs variantes et gouts, mais je vais utiliser la variante suivante:
List<T> query(String sql, ParameterizedRowMapper<T> mapper, Object ... args)
Le premier paramètre est la requête SQL, le second est un objet qui permet de transformer un ResultSet en une instance d’objet (fait le lien entre les colonnes de la table et un objet), et args est la liste d’arguments à injecter dans la requête.
On commence par réaliser le mapper. Il est conseillé de le définir en tant que static inner class :
private static ParameterizedRowMapper<Person> mapper = new ParameterizedRowMapper<Person>() {
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee person = new Person();
person.setId(rs.getLong("id"));
person.setFirstName(rs.getString("first_name"));
person.setLastName(rs.getString("last_name"));
return person;
}
};
Il fut juste noter que j’ai utilisé le ParameterizedRowMapper aqu lieu de l’ancien RowMapper. Comme son nom l’indqiue, ParameterizedRowMapper est paramétré par le type du PAO, ce qui permet donc d’eviter des casts inutiles en tirant profit des Generics de Java 5.
On revient alors à la méthode select dont voici l’implémentation:
public List<Person> select() {
return jdbcTemplate.query("select * from persons", mapper);
}
Voilou ! aussi simple que ça.
On passe maintenant à l’opération delete. Pourla réaliser, j’utilise SimpleJdbcTemplate.update qui comme query, permet d’exécuter des requêtes sur la base de données, à la différence que updatepeut modifier des données (ajout, suppression, modification).
int update(String sql, Object ... args)
public void delete(Person e) {
jdbcTemplate.update("delete from persons where id = ?", e.getId());
}
Ce bout de code illustre l’intérêt du paramètre Object … args. Il sert donc à passer des paramètres à la requête SQL, comme ici où on passe l’identifiant de la personne pour remplir le paramètre ? présent dans la requête.
Encore, Spring tire profit de Java 5 avec les VarArgs, ce qui nous évite le fastidieux new Object[] {e.getId()}.
On passe maitnenant à insert:
public void insert(Pointage e) {
jdbcTemplate
.update(
"insert into persons(id, first_name, last_name, args) values(?, ?, ?)",
e.getId(), e.getFirstName(), e.getLastName());
;
}
Comme on modifie la base de données, on passe encore par SimpleJdbctemplate.update et à part la requête SQL, la méthode insert est similaire à delete.
De même pour update:
public void update(Pointage e) {
jdbcTemplate
.update(
"update persons(id, first_name, last_name, args) set id=?, first_name=?, last_name=?",
e.getId(), e.getFirstName(), e.getLastName());
}
Et voilà !
Disons maintenant qu’on désire utiliser ce DAO dans une classe de la couche Service PersonService. Ceci revient à faire:
@Service
public class PersonService {
private PersonDao personDao;
@Resource
public void setDao(@Qualifier("personDao") IGenericDao<Person> personDao) {
this.personDao=personDao;
}
public void useThisDao(){
Person p = new Person();
p.setId(1L);
p.setFirstName("djo");
p.setLastName("mos");
personDao.insert(p);
p.setFirstName("unAutreNom");
personDao.update(p);
personDao.delete(p);
}
}
On commence par annoter la classe par @Service qui, comme @Respository, indique que PresonService est un Spring Bean mais aussi qu’elle fait partie de la couche Service.
On déclare un champ de type PersonDao ainsi que sont Setter.
On annote le setter par @Resource pour indiquer à Spring que c’est une dépendance pour qu’il la satisfait. Mais là, on risque d’avoir une confusion car dans projet réel, on aura probablement plusieurs DAOs qui implémentent IGenericDao, et Spring ne saura pas lequel injecter vu qu’il sont tous du type IGenericDao.
C’est pour ça que j’utilise l’annotation @Qualifier sur le paramètre du setter pour spécifier le nom du Spring Bean à injecter (ici, personDao).
Si vous vous demandez où et comment on a spécifié ce nom, la réponse est que c’est fait implicitement par l’annotation @Repository appliqué à PersonDao. Par défaut, ce bean sera nommé par le nom court de la classe avec la première lettre mise en miniscule.
Voilou: c’etait donc une rapide introduction à l’API Sprng JDBC mais aussi aux principes d’IoC de Spring à la sauce 2.5 (plus de XML, place aux annotations !).
Bonjour,
Je trouve assez complet, cet article couvre toute la couche business, en utilisant les nouveautés de Spring, et en donnant un nouveau gout à JDBC.
Merci djo.mos
You’re welcome
En fait, je crains surtout que les concepts de l’IoC ne fassent eclipser la partie JDBC …
Hello Djo.mos!
Article vraiment intéressant!