Documente Academic
Documente Profesional
Documente Cultură
NET - Partie 2 -
Les ides exprimes dans ce document ont pour origine un livre lu au cours de l't 2004, un magnifique travail de Rod Johnson : J2EE Development without EJB aux ditions Wrox.
web3tier-part2
1/105
1 Introduction
Nous poursuivons l'article [Variations autour d'une architecture web trois couches - Partie 1] disponible l'url [http://tahe.developpez.com/dotnet/web3tier-part1/]. Rappelons que cet article prsentait une application simplifie d'achats de produits sur le web et que celle-ci tait un simple prtexte pour tudier un exemple d'architecture web trois couches, couches intgres et configures avec la version .NET de Spring. Nous commencerons par rappeler ce qui a t fait et notamment l'architecture trois couches [web, domain, dao] utilise. Dans la solution propose, la couche [dao] tait une couche de test : la source des donnes tait implmente par un objet [ArrayList]. Nous nous attardons dans cet article sur la couche [dao], en prsentant diverses implmentations possibles de celle-ci lorsque les donnes sont dans un SGBD. Outils utiliss :
Visual Studio.net pour le dveloppement - voir l'annexe de la partie 1 de l'article Serveur web Cassini pour l'excution - voir l'annexe de la partie 1 de l'article Nunit pour les tests unitaires - voir l'annexe de la partie 1 de l'article Spring pour l'intgration et la configuration des couches de l'application web - voir l'annexe de la partie 1 de l'article le SGBD Firebird - voir annexe paragraphe 10.1, page 68. le SGBD MSDE (Microsoft Data Engine) - voir annexe paragraphe 10.5, page 83. IBExpert, personal edition pour administrer graphiquement le SGBD Firebird - voir annexe paragraphe 10.2, page 69. EMS MS SQL Manager pour administrer graphiquement le SGBD MSDE - voir annexe paragraphe 10.7, page 89. Ibatis SqlMap pour la couche d'accs aux donnes du SGBD - voir paragraphe 8.2, page 51
Dans une chelle dbutant-intermdiaire-avanc, ce document est dans la partie [intermdiaire-avanc]. Sa comprhension ncessite divers pr-requis. Certains d'entre-eux peuvent tre acquis dans des documents que j'ai crits. Dans ce cas, je les cite. Il est bien vident que ce n'est qu'une suggestion et que le lecteur peut utiliser ses documents favoris.
langage VB.net : [http://tahe.developpez.com/dotnet/vbnet/] programmation web en VB.net : [http://tahe.developpez.com/dotnet/aspnet/vol1 et http://.../vol2] utilisation de l'aspect IoC de Spring : [http://tahe.developpez.com/dotnet/springioc] documentation Ibatis SqlMap : [http://prdownloads.sourceforge.net/ibatisnet/DevGuide.pdf?download] documentation Firebird : [http://firebird.sourceforge.net/pdfmanual/Firebird-1.5-QuickStart.pdf] documentation Spring.net : [http://www.springframework.net/documentation.html]
web3tier-part2
2/105
- la vue "LISTE" qui prsente une liste des articles en - la vue [INFOS] qui donne des informations supplmentaires sur un vente produit :
utilisateur
Donnes
les trois couches ont t rendues indpendantes grce l'utilisation d'interfaces l'intgration des diffrentes couches a t ralise avec Spring chaque couche fait l'objet d'espaces de noms spars : web (couche UI), domain (couche mtier) et dao (couche d'accs aux donnes). 3/105
web3tier-part2
L'application respecte une architecture MVC (Modle - Vue - Contrleur). Si nous reprenons le schma en couches ci-dessus, l'architecture MVC s'y intgre de la faon suivante : Couche interface utilisateur [web] Couche mtier [domain] Couche d'accs aux donnes [dao]
1
utilisateur
Contrleur 4 3 Vues
2 Modle
Donnes
SPRING
Le traitement d'une demande d'un client se droule selon les tapes suivantes : 1. le client fait une demande au contrleur. Ce contrleur est ici une page .aspx laquelle on fait jouer un rle particulier. Elle voit passer toutes les demandes des clients. C'est la porte d'entre de l'application. C'est le C de MVC. 2. le contrleur traite cette demande. Pour ce faire, il peut avoir besoin de l'aide de la couche mtier, ce qu'on appelle le modle M dans la structure MVC. 3. le contrleur reoit une rponse de la couche mtier. La demande du client a t traite. Celle-ci peut appeler plusieurs rponses possibles. Un exemple classique est une page d'erreurs si la demande n'a pu tre traite correctement une page de confirmation sinon 4. le contrleur choisit la rponse (= vue) envoyer au client. Celle-ci est le plus souvent une page contenant des lments dynamiques. Le contrleur fournit ceux-ci la vue. 5. la vue est envoye au client. C'est le V de MVC.
2.3 Le modle
Le modle M du MVC est ici constitu des lments suivants : 1. 2. 3. les classes mtier les classes d'accs aux donnes la base de donnes
cl primaire identifiant un article de faon unique nom de l'article son prix son stock actuel le stock au-dessous duquel une commande de rapprovisionnement doit tre faite
web3tier-part2
4/105
istia.st.articles.dao : contient les classes d'accs aux donnes de la couche [dao] istia.st.articles.domain : contient les classes mtier de la couche [domain]
Chacun de ces espaces de noms est contenu au sein d'un fichier " assembly " qui lui est propre :
assembly
webarticles-dao
contenu
- [IArticlesDao]: l'interface d'accs la couche [dao] C'est la seule interface que voit la couche [domain]. Elle n'en voit pas d'autre. - [Article] : classe dfinissant un article - [ArticlesDaoArrayList] : classe d'implmentation de l'interface [IArticlesDao] avec une classe [ArrayList]
rle
couche d'accs aux donnes - se trouve entirement dans la couche [dao] de l'architecture 3-tier de l'application web
webarticles-domain
- [IArticlesDomain]: l'interface d'accs la couche [domain]. C'est la seule interface que voit la couche web. Elle n'en voit pas d'autre. - [AchatsArticles] : une classe implmentant [IArticlesDomain] - [Achat] : classe reprsentant l'achat d'un client - [Panier] : classe reprsentant l'ensemble des achats d'un client
reprsente le modle des achats sur le web - se trouve entirement dans la couche [domain] de l'architecture 3tier de l'application web
web3tier-part2
le fichier de configuration [web.config] le dossier [bin] qui contient : les DLL des trois couches [webarticles-dao.dll], [webarticles-domain.dll], [webarticles-web.dll] les fichiers ncessaires Spring [Spring-Core.*], [log4net.dll] le dossier [vues] qui contient le code de prsentation des diffrentes vues. la prsence des fichiers de code .vb est inutile puisque leur version compile est dans les DLL.
2.4.2 Tests
Nous configurons le serveur web [Cassini] de la faon suivante :
avec : Physical Path : D:\data\serge\travail\2004-2005\aspnet\webarticles-010405\runtime\ Virtual Path : /webarticles Avec un navigateur nous demandons l'url [http://localhost/webarticles/main.aspx]
Rappelons que la couche [dao] est implmente par une classe qui stocke les articles dans un objet [ArrayList]. Cette classe cre une liste initiale de quatre articles. A partir de la vue ci-dessus, nous utilisons les liens du menu pour faire des oprations. En voici quelques unes. La colonne de gauche reprsente la demande du client et la colonne de droite la rponse qui lui est faite.
web3tier-part2
6/105
web3tier-part2
7/105
web3tier-part2
8/105
web3tier-part2
9/105
la couche [domain] ne s'adresse pas une classe concrte mais une interface [IArticlesDao] grce Spring, nous avons pu cacher la couche [domain] le nom de la classe d'implmentation de l'interface [IArticlesDao].
10/105
End Property ' stock actuel article Public Property stockactuel() As Integer Get Return _stockactuel End Get Set(ByVal Value As Integer) If Value < 0 Then Throw New Exception("Le champ stockActuel [" + Value.ToString + "] est invalide") End If Me._stockactuel = Value End Set End Property ' stock minimum article Public Property stockminimum() As Integer Get Return _stockminimum End Get Set(ByVal Value As Integer) If Value < 0 Then Throw New Exception("Le champ stockMinimum [" + Value.ToString + "] est invalide") End If Me._stockminimum = Value End Set End Property ' constructeur par dfaut Public Sub New() End Sub ' constructeur avec proprits Public Sub New(ByVal id As Integer, ByVal nom As String, ByVal prix As Double, ByVal stockactuel As Integer, ByVal stockminimum As Integer) Me.id = id Me.nom = nom Me.prix = prix Me.stockactuel = stockactuel Me.stockminimum = stockminimum End Sub ' mthode d'identification de l'article Public Overrides Function ToString() As String Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]" End Function End Class End Namespace
Cette classe offre : 1. 2. 3. 4. un constructeur permettant de fixer les 5 informations d'un article : [id, nom, prix, stockactuel, stockminimum] des proprits publiques permettant de lire et crire les 5 informations. une vrification des donnes insres dans l'article. En cas de donnes errones, une exception est lance. une mthode toString qui permet d'obtenir la valeur d'un article sous forme de chane de caractres. C'est souvent utile pour le dbogage d'une application.
11/105
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer End Interface End Namespace
rend tous les articles de la source de donnes vide la source de donnes rend l'objet [Article] identifi par son numro permet d'ajouter un article la source de donnes permet de modifier un article de la source de donnes permet de supprimer un article de la source de donnes permet de modifier le stock d'un article de la source de donnes
L'interface met disposition des programmes clients un certain nombre de mthodes dfinies uniquement par leurs signatures. Elle ne s'occupe pas de la faon dont ces mthodes seront rellement implmentes. Cela amne de la souplesse dans une application. Le programme client fait ses appels sur une interface et non pas sur une implmentation prcise de celle-ci. IntProg. Client erface Implmentation 2 Implmentation 1
Le choix d'une implmentation prcise se fait au moyen d'un fichier de configuration Spring..
4.1 Le code
4.1.1 Le squelette
La classe [ArticlesDaoPlainODBC] implmente l'interface [IArticlesDao] de la faon suivante :
1. Imports System 2. Imports System.Collections 3. Imports System.Data.Odbc 4. 5. Namespace istia.st.articles.dao 6. 7. Public Class ArticlesDaoPlainODBC 8. Implements istia.st.articles.dao.IArticlesDao 9. 10. ' champs privs 11. Private connexion As OdbcConnection = Nothing 12. Private DSN As String 13. Private insertCommand As OdbcCommand 14. Private updatecommand As OdbcCommand 15. Private deleteSomeCommand As OdbcCommand 16. Private selectSomeCommand As OdbcCommand 17. Private updateStockCommand As OdbcCommand 18. Private deleteAllCommand As OdbcCommand 19. Private selectAllCommand As OdbcCommand 20. 21. ' constructeur 22. Public Sub New(ByVal DSN As String, ByVal uid As String, ByVal password As String) 23. ' DSN : nom de la source ODBC 24. ' uid : identit de l'utilisateur 25. ' password : son mot de passe 26.... 27. End Sub 28. web3tier-part2
12/105
29. Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle 30.... 31. End Function 32. 33. Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle 34.... 35. End Function 36. 37. Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles 38.... 39. End Sub 40. 41. Public Function getAllArticles() As System.Collections.IList Implements IArticlesDao.getAllArticles 42.... 43. End Function 44. 45. Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById 46.... 47. End Function 48. 49. Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle 50.... 51. End Function 52. 53. Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle 54..... 55. End Function 56. 57. Private Function executeQuery(ByVal query As OdbcCommand) As IList 58. ' excution d'une requte SELECT 59..... 60. End Function 61. 62. Private Function executeUpdate(ByVal sqlCommand As OdbcCommand) As Integer 63..... 64. End Class 65. 66.End Namespace
Commentaires :
ligne 3, on importe l'espace de noms contenant les classes .NET d'accs aux sources ODBC ligne 11 - mmorisera la connexion la source ODBC ligne 12 - mmorisera le nom DSN de la source de donnes lignes 13-19 - variables prives de type [OdbcCommand] dfinissant les requtes SQL utilises par les diffrentes mthodes de la classe lignes 22-27 - le constructeur. Il reoit les lments qui lui permettent de construire l'objet [OdbcConnection] qui va relier le code la source de donnes ODBC lignes 29-31 - la mthode d'ajout d'un article lignes 33-35 - la mthode pour changer le stock d'un article lignes 37-39 - la mthode qui supprime tous les articles de la source de donnes ODBC lignes 41-43 - la mthode qui obtient la liste de tous les articles de la source ODBC lignes 45-47 - la mthode qui permet d'obtenir un article particulier lignes 49-51 - la mthode qui permet de modifier certains champs d'un article dont on a le numro lignes 53-55 - la mthode qui permet de supprimer un article dont on a le numro lignes 57-60 - mthode utilitaire permettant d'excuter un [SELECT] sur la source de donnes et d'en rendre le rsultat lignes 62-64 - mthode utilitaire permettant d'excuter un [INSERT, UPDATE, DELETE] sur la source de donnes et d'en rendre le rsultat
4.1.2 Le constructeur
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. ' constructeur Public Sub New(ByVal DSN As String, ByVal uid As String, ByVal password As String) ' DSN : nom de la source ODBC ' uid : identit de l'utilisateur ' password : son mot de passe 'on rcupre le nom de la base pass en argument Me.DSN = DSN Dim connectString As String = String.Format("DSN={0};UID={1};PASSWORD={2}", DSN, uid, password) 'on instancie la connexion connexion = New OdbcConnection(connectString) ' on prpare les requtes SQL
web3tier-part2
13/105
13. insertCommand = New OdbcCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (?,?,?,?,?)", connexion) 14. updatecommand = New OdbcCommand("update ARTICLES set nom=?, prix=?, stockactuel=?, stockminimum=? where id=?", connexion) 15. deleteSomeCommand = New OdbcCommand("delete from ARTICLES where id=?", connexion) 16. selectSomeCommand = New OdbcCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES where id=?", connexion) 17. updateStockCommand = New OdbcCommand("update ARTICLES set stockactuel=stockactuel+? where id=? and (stockactuel+?)>=0", connexion) 18. selectAllCommand = New OdbcCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES", connexion) 19. deleteAllCommand = New OdbcCommand("delete from ARTICLES", connexion) 20. End Sub
Commentaires :
ligne 2 - le constructeur reoit les trois informations dont il a besoin pour se connecter une source ODBC : le nom DSN de la source, l'identit avec laquelle on doit se connecter, le mot de passe associ. ligne 8 - on mmorise le nom DSN de la source afin de pouvoir le redonner dans les messages d'erreurs. ligne 9 - l'objet [OdbcConnection] est instanci. Une connexion instancie n'est pas une connexion ouverte. C'est la mthode [open] qui fait l'ouverture. lignes 12-19 - on prpare les requtes SQL dans les objets [OdbcCommand]. Cela nous vitera de les reconstruire chaque fois qu'on en a besoin. Les paramtres formels ? des requtes seront remplacs au moment de l'excution de la requte par des valeurs relles.
Commentaires :
la mthode [executeQuery] est une mthode utilitaire qui : excute une requte [SELECT id, nom, prix, stockactuel, stockminimum from ARTICLES ...] sur la source de donnes rend le rsultat sous la forme d'une liste d'objets [Article] ligne 1 - l'unique paramtre de la mthode est l'objet [OdbcCommand] contenant la requte [Select] excuter. ligne 7 - la connexion est ouverte. Elle sera ferme ligne 29 qu'il y ait eu erreur ou non. ligne 9 - l'objet [OdbcDataReader] ncessaire pour exploiter le rsultat du [Select] est instanci lignes 13-23 - chaque ligne rsultat du [Select] est mise dans un objet [Article] qui va rejoindre les autres articles dans un objet [ArrayList] la liste des articles est rendue ligne 25 aucune exception n'est gre. Elle devra l'tre par le code appelant cette mthode.
14/105
2. ' excution d'une requte de mise jour 3. Try 4. 'on cre une connexion la BDD 5. connexion.Open() 6. 'on excute la requte 7. Return sqlCommand.ExecuteNonQuery() 8. Finally 9. ' libration des ressources 10. If Not connexion Is Nothing Then connexion.Close() 11. End Try 12. End Function 13. End Class
Commentaires :
la mthode reoit un objet [OdbcCommand] qui contient une requte SQL de type [Insert, Update, Delete]. la connexion est ouverte ligne 5. Elle sera referme ligne 10 qu'il y ait eu une exception ou non. la requte de mise jour est excute ligne 7. On en rend immdiatement le rsultat qui est le nombre de lignes de la table ARTICLES modifies par la requte.
Commentaires :
ligne 1 - la mthode reoit l'article ajouter la source de donnes ODBC. Elle rend le nombre de lignes affectes par cette opration, c.a.d. 1 ou 0 lignes 3 et 20 - la mthode est synchronise. Ce sera le cas de toutes les mthodes d'accs aux donnes. Cela entrane qu'un seul thread la fois pourra travailler sur la source de donnes. C'est probablement trop conservateur. Il existe de meilleures alternatives, notamment celle d'inclure ces oprations dans des transactions. Dans ce cas, c'est le SGBD qui gre les accs concurrents. Nous n'avons pas voulu introduire la notion de transaction ds maintenant. Spring nous offre la possibilit de les introduire dans la couche [domain]. Nous aurons peut-tre l'occasion d'y revenir dans un autre article. lignes 5-12, on donne des valeurs aux paramtres formels de la requte de l'objet [insertCommand] initialis par le constructeur. Rappelons celle-ci :
insertCommand = New OdbcCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (?,?,?,?,?)", connexion)
les 5 valeurs ncessaires la requte sont fournies par les lignes 7-11. lignes 13-19, la requte est excute. Si elle se passe bien, on en retourne le rsultat. Sinon, on lance une exception gnrique avec un message d'erreur explicite
web3tier-part2
15/105
8. .Add(New OdbcParameter("prix", unArticle.prix)) 9. .Add(New OdbcParameter("stockactuel", unArticle.stockactuel)) 10. .Add(New OdbcParameter("stockminimum", unArticle.stockminimum)) 11. .Add(New OdbcParameter("id", unArticle.id)) 12. End With 13. ' on l'excute 14. Try 15. 'on excute la requte d'insertion 16. Return executeUpdate(updatecommand) 17. Catch ex As Exception 18. 'erreur de requte 19. Throw New Exception("Erreur lors de la modification de l'article [" + unArticle.ToString + "]", ex) 20. End Try 21. End SyncLock 22. End Function
Commentaires :
ligne 1 - la mthode reoit l'article modifier dans la source de donnes ODBC. Elle rend le nombre de lignes affectes par cette opration, c.a.d. 1 ou 0 les commentaires de la mthode [ajouteArticle] peuvent tre repris ici
Commentaires :
ligne 1 - la mthode reoit le n de l'article supprimer dans la source de donnes ODBC. Elle rend le nombre de lignes affectes par cette opration, c.a.d. 1 ou 0 les commentaires de la mthode [ajouteArticle] peuvent tre repris ici
Commentaires :
ligne 1 - la mthode ne reoit aucun paramtre. Elle rend la liste de tous les articles de la source de donnes ODBC la requte [Select] rclamant tous les articles est demande la mthode [executeQuery] - ligne 6 la liste obtenue est rendue ligne 8 lignes 9-12, on gre une ventuelle exception 16/105
web3tier-part2
Commentaires :
ligne 1 - la mthode reoit en paramtre le n de l'article dsir. Elle rend celui-ci s'il est trouv dans la source ODBC, sinon elle rend la rfrence [nothing]. la requte [Select] rclamant l'article est initialise lignes 5-8 elle est excute ligne 12 - on obtient une liste d'articles si cette liste est vide, on rend la rfrence [nothing] ligne 14 sinon l'unique article de la liste est rendu ligne 16 lignes 17-20, on gre une ventuelle exception
Commentaires :
ligne 1 - la mthode ne reoit aucun paramtre et elle ne rend rien ligne 6 - la requte de suppression de tous les articles est excute lignes 7-10, on gre une ventuelle exception
17/105
15. 'erreur de requte 16. Throw New Exception(String.Format("Erreur lors du changement de stock [idArticle={0}, mouvement={1}] : [{2}]", idArticle, mouvement, ex.Message)) 17. End Try 18. End SyncLock 19. End Function
Commentaires :
ligne 1 - la mthode reoit en paramtres le n de l'article dont il faut modifier le stock ainsi que l'incrment de celui-ci (en positif ou ngatif). Elle rend le nombre de lignes modifies par l'opration c.a.d. 0 ou 1. lignes 5-10, la requte [updateStockCommand] est initialise. Rappelons le texte de la requte SQL :
updateStockCommand = New OdbcCommand("update ARTICLES set stockactuel=stockactuel+? where id=? and (stockactuel+?)>=0", connexion)
on remarquera que le stock n'est modifi que si une fois modifi il reste >=0. la requte de mise jour du stock de l'article est excute ligne 13 et le rsultat rendu lignes 14-18, on gre une ventuelle exception
web3tier-part2
18/105
L'administrateur de cette base sera l'utilisateur [SYSDBA] avec le mot de passe [masterkey]. Nous crons quelques articles :
Nous crons maintenant la source ODBC Firebird suivante (cf paragraphe 10.3, page 76) : Comme suggr gauche, il est important de vrifier que la source a t correctement configure avec le bouton [Test Connection] :
nom DSN : odbc-firebird-articles identit de connexion : SYSDBA mot de passe associ : masterkey
Namespace istia.st.articles.tests <TestFixture()> _ Public Class NunitTestArticlesArrayList ' l'objet tester Private articlesDao As IArticlesDao <SetUp()> _ Public Sub init() ' on rcupre une instance du fabricant d'objets Spring Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open)) ' on demande l'instanciation de l'objet articlesdao articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao) web3tier-part2
19/105
On voit que dans la mthode d'attribut <Setup()>, on demande Spring une rfrence sur le singleton nomm [articlesdao] de type [IArticlesDao] donc du type de l'interface. Le singleton [articlesdao] tait dfini par le fichier de configuration [spring-config.xml] suivant :
<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" "http://www.springframework.net/dtd/spring-objects.dtd"> <objects> <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao"/> </objects>
Montrons que la classe de test initiale nous permet de tester notre nouvelle couche [dao] sans modification ni recompilation.
Crons dans le dossier Visual Studio de notre nouvelle couche [dao] le dossier [tests] ( droite ci-dessous) par recopie du dossier [bin] du projet de tests de la couche [dao] initiale ( gauche ci-dessous). Au besoin, le lecteur est invit revoir le projet de test de la version premire de la couche [dao] dans la premire partie de l'article.
dans le dossier [tests] remplaons la DLL [webarticles-dao.dll] issue de l'ancienne couche [dao] par la DLL [webarticles-dao.dll] issue de la nouvelle couche [dao] modifions le fichier de configuration [spring-config.xml] afin d'instancier la nouvelle classe [ArticlesDaoPlainODBC] :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" 3. "http://www.springframework.net/dtd/spring-objects.dtd"> 4. 5. <objects> 6. <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, webarticles-dao"> 7. <constructor-arg index="0"> 8. <value>odbc-firebird-articles</value> 9. </constructor-arg> 10. <constructor-arg index="1"> 11. <value>SYSDBA</value> 12. </constructor-arg> 13. <constructor-arg index="2"> 14. <value>masterkey</value> 15. </constructor-arg> 16. </object> 17.</objects>
Commentaires :
ligne 6, l'objet [articlesdao] est maintenant associ une instance de la classe [ ArticlesDaoPlainODBC] cette classe a un constructeur trois arguments : le nom de la source DSN - ligne 8 l'identit avec laquelle on fera l'accs la base - ligne 11 le mot de passe associ cette identit - ligne 14
Nous reprenons l les informations de la source ODBC-Firebird que nous avons cre prcdemment.
4.3.3 Tests
Nous sommes maintenant prts pour les tests. Al'aide de l'application [Nunit-Gui], nous chargeons la DLL [test-webarticles-dao.dll] du dossier [tests] ci-dessus et excutons le test [testGetAllArticles] :
web3tier-part2
20/105
En regardant la copie d'cran ci-dessus, on pourra regretter le nom [NUnitTestArticlesDaoArrayList] donn initialement la classe de test. Cela prte confusion. C'est bien la classe [ArticlesDaoPlainODBC] qui est ici teste. La copie d'cran montre que nous avons rcupr correctement les articles que nous avions placs dans la table [ARTICLES]. Maintenant, faisons la totalit des tests :
Dans la fentre de gauche, on voit la liste des mthodes testes. La couleur du point qui prcde le nom de chaque mthode indique la russite (vert) ou l'chec (rouge) de la mthode. Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis.
4.3.4 Conclusion
Nous venons de montrer que :
parce que la classe de test NUnit rfrenait non pas une classe mais une interface parce que le nom exact de la classe d'instanciation de l'interface tait fourni dans un fichier de configuration et non dans le code parce que Spring s'occupait d'instancier la classe et d'en donner une rfrence au code de test
alors le code de test crit pour la couche [dao] initiale restait valide pour une nouvelle implmentation de cette mme couche. Nous n'avons pas eu besoin d'avoir accs au code de la classe de test. Nous n'avons utilis que sa version compile, celle gnre lors du test de la couche [dao] initiale. Nous allons faire des conclusions analogues lorsqu'il va falloir intger la nouvelle couche [dao] dans l'application [webarticles].
web3tier-part2
21/105
Le lecteur est invit revoir ventuellement le paragraphe 2.4, page 5 qui dtaille mes modalits du dploiement de l'application [webarticles]. Nous apportons les modifications suivantes au contenu du dossier [runtime] :
dans le dossier [bin], la DLL de l'ancienne couche [dao] est remplace par la DLL de la nouvelle couche [dao] dans [runtime], le fichier de configuration [web.config] est remplac par un fichier qui prend en compte la nouvelle classe d'implmentation de la couche [dao] : dossier [bin] aprs :
L'ancien fichier [web.config] a t renomm [web.config-1] Le nouveau fichier de configuration [web.config] est le suivant :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <configuration> 3. <configSections> 4. <sectionGroup name="spring"> 5. <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> 6. <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> 7. </sectionGroup> 8. </configSections> 9. <spring> 10. <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> 11. <resource uri="config://spring/objects" /> web3tier-part2
22/105
12. </context> 13. <objects> 14. <object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, webarticles-dao"> 15. <constructor-arg index="0"> 16. <value>odbc-firebird-articles</value> 17. </constructor-arg> 18. <constructor-arg index="1"> 19. <value>SYSDBA</value> 20. </constructor-arg> 21. <constructor-arg index="2"> 22. <value>masterkey</value> 23. </constructor-arg> 24. </object> 25. <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> 26. <constructor-arg index="0"> 27. <ref object="articlesDao" /> 28. </constructor-arg> 29. </object> 30. </objects> 31. </spring> 32. <appSettings> 33. <add key="urlMain" value="/webarticles/main.aspx" /> 34. <add key="urlInfos" value="vues/infos.aspx" /> 35. <add key="urlErreurs" value="vues/erreurs.aspx" /> 36. <add key="urlListe" value="vues/liste.aspx" /> 37. <add key="urlPanier" value="vues/panier.aspx" /> 38. <add key="urlPanierVide" value="vues/paniervide.aspx" /> 39. </appSettings> 40.</configuration>
Commentaires :
les lignes 14-24 associent au singleton [articlesDao] une instance de la nouvelle classe [ArticlesDaoPlainODBC]. C'est la seule modification. Nous l'avons dj rencontre lors des tests de la nouvelle couche [dao].
Nous sommes prts pour les tests. Nous configurons le serveur web [Cassini] de la mme faon que dans le paragraphe 2.4, page 5. Nous initialisons la table des articles [Firebird] avec les valeurs suivantes :
Assurez-vous que le serveur web Cassini ainsi que le SGBD [Firebird] sont lancs. Avec un navigateur nous demandons l'url [http://localhost/webarticles/main.aspx] :
web3tier-part2
23/105
Les articles [parapluie] et [bottes] ont t achets et leurs stocks dcrments de la quantit achete. L'article [chapeau] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
4.4.2 Conclusion
Qu'avons-nous fait ?
nous avons repris la version de dploiement de l'ancienne version nous avons remplac la DLL de la couche [dao] par une nouvelle version. Les DLL des couches [web] et [domain] sont restes inchanges. nous avons modifi le fichier de configuration [web.config] afin qu'il prenne en compte la nouvelle classe d'implmentation de la couche [dao]
Tout cela est propre et offre une grande facilit d'volution l'application web. Ces caractristiques importantes nous sont apportes par deux choix d'architecture :
l'accs aux couches via des interfaces l'intgration et la configuration des couches par Spring.
5.1 Le code
La classe [ArticlesDaoSqlServer] est trs proche de la classe [ArticlesDaoPlainODBC] tudie prcdemment. Aussi n'indiqueronsnous que les changements apports la version prcdente :
web3tier-part2
24/105
les classes ncessaires sont dans l'espace de noms [System.Data.SqlClient] au lieu de l'espace de noms [System.Data.Odbc] la connexion de type [OdbcConnection] a maintenant le type [SqlConnection] les objets [OdbcCommand] ont maintenant le type [SqlCommand] la syntaxe des requtes SQL paramtres changent. La requte d'insertion devient ainsi :
insertCommand = New SqlCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (@id,@nom,@prix,@sa,@sm)", connexion)
25/105
connexion As SqlConnection = Nothing databaseName As String insertCommand As SqlCommand updatecommand As SqlCommand deleteSomeCommand As SqlCommand selectSomeCommand As SqlCommand updateStockCommand As SqlCommand deleteAllCommand As SqlCommand selectAllCommand As SqlCommand
' constructeur Public Sub New(ByVal serveur As String, ByVal databaseName As String, ByVal uid As String, ByVal password As String) ' serveur : nom de l'instance SQL server atteindre ' databaseName : nom de la base de donnes atteindre ' uid : identit de l'utilisateur ' password : son mot de passe 'on rcupre le nom de la base pass en argument Me.databaseName = databaseName 'on instancie la connexion Dim connectString As String = String.Format("Data Source={0};Initial Catalog={1};UID={2};PASSWORD={3}", serveur, databaseName, uid, password) connexion = New SqlConnection(connectString) ' on prpare les requtes SQL insertCommand = New SqlCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (@id,@nom,@prix,@sa,@sm)", connexion) updatecommand = New SqlCommand("update ARTICLES set nom=@nom, prix=@prix, stockactuel=@sa, stockminimum=@sm where id=@id", connexion) deleteSomeCommand = New SqlCommand("delete from ARTICLES where id=@id", connexion) selectSomeCommand = New SqlCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES where id=@id", connexion) updateStockCommand = New SqlCommand("update ARTICLES set stockactuel=stockactuel+@mvt where id=@id and (stockactuel+@mvt)>=0", connexion) selectAllCommand = New SqlCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES", connexion) deleteAllCommand = New SqlCommand("delete from ARTICLES", connexion) End Sub Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle ' section exclusive SyncLock Me ' on prpare la requte d'insertion With insertCommand.Parameters .Clear() .Add(New SqlParameter("@id", unArticle.id)) .Add(New SqlParameter("@nom", unArticle.nom)) .Add(New SqlParameter("@prix", unArticle.prix)) .Add(New SqlParameter("@sa", unArticle.stockactuel)) .Add(New SqlParameter("@sm", unArticle.stockminimum)) End With Try 'on l'excute Return executeUpdate(insertCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur l'ajout de l'article [{0}] : {1}", unArticle.ToString, ex.Message)) End Try End SyncLock End Function Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle ' section exclusive SyncLock Me ' on prpare la requte de mise jour du stock With updateStockCommand.Parameters .Clear() .Add(New SqlParameter("@mvt", mouvement)) .Add(New SqlParameter("@id", idArticle)) End With 'on l'excute Try Return executeUpdate(updateStockCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors du changement de stock [idArticle={0}, mouvement={1}] : [{2}]", idArticle, mouvement, ex.Message)) End Try End SyncLock End Function Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles ' section exclusive SyncLock Me web3tier-part2
26/105
Try 'on excute la requte d'insertion executeUpdate(deleteAllCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression des articles : {0}", ex.Message)) End Try End SyncLock End Sub Public Function getAllArticles() As System.Collections.IList Implements IArticlesDao.getAllArticles ' section exclusive SyncLock Me Try 'on excute la requte select Dim articles As IList = executeQuery(selectAllCommand) 'on retourne le liste Return articles Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de l'obtention des articles [select id,nom,prix,stockactuel,stockminimum from articles]: {0}", ex.Message)) End Try End SyncLock End Function Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById ' section exclusive SyncLock Me ' on prpare la requte select With selectSomeCommand.Parameters .Clear() .Add(New SqlParameter("@id", idArticle)) End With 'on l'excute Try 'on excute la requte Dim articles As IList = executeQuery(selectSomeCommand) 'on test si l'on a trouv l'article If articles.Count = 0 Then Return Nothing 'on retourne l'article Return CType(articles.Item(0), Article) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la recherche de l'article [{0} : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle ' section exclusive SyncLock Me ' on prpare la requte update With updatecommand.Parameters .Clear() .Add(New SqlParameter("@nom", unArticle.nom)) .Add(New SqlParameter("@prix", unArticle.prix)) .Add(New SqlParameter("@sa", unArticle.stockactuel)) .Add(New SqlParameter("@sm", unArticle.stockminimum)) .Add(New SqlParameter("@id", unArticle.id)) End With ' on l'excute Try 'on excute la requte d'insertion Return executeUpdate(updatecommand) Catch ex As Exception 'erreur de requte Throw New Exception("Erreur lors de la modification de l'article [" + unArticle.ToString + "]", ex) End Try End SyncLock End Function Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle ' section exclusive SyncLock Me ' on prpare la requte delete With deleteSomeCommand.Parameters .Clear() .Add(New SqlParameter("@id", idArticle)) End With 'on l'excute web3tier-part2
27/105
Try 'on excute la requte de suppression Return executeUpdate(deleteSomeCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression de l'article [id={0}] : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Private Function executeQuery(ByVal query As SqlCommand) As IList ' excution d'une requte SELECT ' dclaration de l'objet permettant l'accs toutes les lignes de la table rsultat Dim myReader As SqlDataReader = Nothing Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte myReader = query.ExecuteReader() 'on dclare une liste d'articles pour la retourner par la suite Dim articles As IList = New ArrayList Dim unArticle As Article While myReader.Read() 'on prpare un article avec les valeurs du reader unArticle = New Article unArticle.id = myReader.GetInt32(0) unArticle.nom = myReader.GetString(1) unArticle.prix = myReader.GetDouble(2) unArticle.stockactuel = myReader.GetInt32(3) unArticle.stockminimum = myReader.GetInt32(4) 'on ajoute l'article la liste articles.Add(unArticle) End While 'on retourne le rsultat Return articles Finally ' libration des ressources If Not myReader Is Nothing And Not myReader.IsClosed Then myReader.Close() If Not connexion Is Nothing Then connexion.Close() End Try End Function Private Function executeUpdate(ByVal updateCommand As SqlCommand) As Integer ' excution d'une requte de mise jour Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte Return updateCommand.ExecuteNonQuery() Finally ' libration des ressources If Not connexion Is Nothing Then connexion.Close() End Try End Function End Class End Namespace
Le lecteur est invit lire ce code la lumire des commentaires de la classe [ArticlesDaoPlainODBC] faits prcdemment.
web3tier-part2
28/105
web3tier-part2
29/105
La base est proprit de l'utilisateur [mdparticles] de mot de passe [admarticles]. La commande Transact-SQL de cration de la table [ARTICLES] est la suivante :
CREATE TABLE [ARTICLES] ( [id] int NOT NULL, [nom] varchar(20) COLLATE French_CI_AS NOT NULL, [prix] float(53) NOT NULL, [stockactuel] int NOT NULL, [stockminimum] int NOT NULL, CONSTRAINT [ARTICLES_uq] UNIQUE ([nom]), PRIMARY KEY ([id]), CONSTRAINT [ARTICLES_ck_id] CHECK ([id] > 0), CONSTRAINT [ARTICLES_ck_nom] CHECK ([nom] <> ''), CONSTRAINT [ARTICLES_ck_prix] CHECK ([prix] >= 0), CONSTRAINT [ARTICLES_ck_stockactuel] CHECK ([stockactuel] >= 0), CONSTRAINT [ARTICLES_ck_stockminimum] CHECK ([stockminimum] >= 0) ) ON [PRIMARY] GO
nous crons dans le dossier Visual Studio du projet [dao-sqlserver] le dossier [tests] ( droite) par recopie du dossier [tests] du projet [dao-odbc] ( gauche) :
dans le dossier [tests] du projet [dao-sqlserver], nous remplaons la DLL [webarticles-dao.dll] par la DLL [webarticles-dao.dll] issue de la gnration du projet [dao-sqlserver] nous modifions le fichier de configuration [spring-config.xml] afin d'instancier la nouvelle classe [ArticlesDaoSqlServer] :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <!-3. <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" web3tier-part2
30/105
4. "http://www.springframework.net/dtd/spring-objects.dtd"> 5. --> 6. <objects> 7. <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoSqlServer, webarticles-dao"> 8. <constructor-arg index="0"> 9. <value>portable1_tahe\msde140405</value> 10. </constructor-arg> 11. <constructor-arg index="1"> 12. <value>dbarticles</value> 13. </constructor-arg> 14. <constructor-arg index="2"> 15. <value>admarticles</value> 16. </constructor-arg> 17. <constructor-arg index="3"> 18. <value>mdparticles</value> 19. </constructor-arg> 20. </object> 21.</objects>
Commentaires :
ligne 7, l'objet [articlesdao] est maintenant associ une instance de la classe [ ArticlesDaoSqlServeur] cette classe a un constructeur quatre arguments : le nom de l'instance MSDE utilise - ligne 9 le nom de la base de donnes - ligne 12 l'identit avec laquelle on fera l'accs la base - ligne 15 le mot de passe associ cette identit - ligne 18
Nous reprenons l les informations de la source MSDE que nous avons cre prcdemment.
5.3.3 Tests
Nous sommes prts pour les tests. Al'aide de l'application [Nunit-Gui], nous chargeons la DLL [test-webarticles-dao.dll] du dossier [tests] ci-dessus et excutons le test [testGetAllArticles] :
Malgr le nom [NUnitTestArticlesDaoArrayList] donn initialement la classe de test et qui a t conserv puisque nous utilisons la DLL [tests-webarticles-dao.dll] issue de cette classe, c'est bien la classe [ArticlesDaoSqlserver] qui est ici teste. La copie d'cran montre que nous avons rcupr correctement les articles que nous avions placs dans la table [ARTICLES]. Maintenant, faisons la totalit des tests :
web3tier-part2
31/105
Dans la fentre de gauche, on voit la liste des mthodes testes. La couleur du point qui prcde le nom de chaque mthode indique la russite (vert) ou l'chec (rouge) de la mthode. Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis.
dans le dossier [bin], la DLL de l'ancienne couche [dao] est remplace par la DLL de la nouvelle couche [dao] implmente par la classe [ArticlesDaoSqlServer] dans [runtime], le fichier de configuration [web.config] est remplac par un fichier qui prend en compte la nouvelle classe d'implmentation :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <configuration> 3. <configSections> 4. <sectionGroup name="spring"> 5. <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> 6. <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> 7. </sectionGroup> 8. </configSections> 9. <spring> 10. <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> 11. <resource uri="config://spring/objects" /> 12. </context> 13. <objects> 14.<objects> 15. <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoSqlServer, webarticles-dao"> 16. <constructor-arg index="0"> 17. <value>portable1_tahe\msde140405</value> 18. </constructor-arg> 19. <constructor-arg index="1"> 20. <value>dbarticles</value> 21. </constructor-arg> 22. <constructor-arg index="2"> 23. <value>admarticles</value> 24. </constructor-arg> 25. <constructor-arg index="3"> 26. <value>mdparticles</value> 27. </constructor-arg> 28. </object> 29. <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> 30. <constructor-arg index="0"> 31. <ref object="articlesDao" /> 32. </constructor-arg> 33. </object> 34. </objects> 35. </spring> 36. <appSettings> 37. <add key="urlMain" value="/webarticles/main.aspx" /> 38. <add key="urlInfos" value="vues/infos.aspx" /> 39. <add key="urlErreurs" value="vues/erreurs.aspx" /> 40. <add key="urlListe" value="vues/liste.aspx" /> 41. <add key="urlPanier" value="vues/panier.aspx" /> 42. <add key="urlPanierVide" value="vues/paniervide.aspx" /> 43. </appSettings> 44.</configuration> web3tier-part2
32/105
Commentaires :
les lignes 15-33 associent au singleton [articlesDao] une instance de la nouvelle classe [ArticlesDaoSqlServer]. C'est la seule modification. Nous l'avons dj rencontre lors des tests de la nouvelle couche [dao]
Nous sommes prts pour les tests. Nous gardons la mme configuration du serveur web [Cassini] qu'auparavant. Nous initialisons la table des articles [MSDE] avec les valeurs suivantes :
Assurez-vous que le serveur web Cassini ainsi que le SGBD MSDE (ici l'instance portable1_tahe\msde140405) sont lancs. Avec un navigateur nous demandons l'url [http://localhost/webarticles/main.aspx] :
web3tier-part2
33/105
Les articles [ballon foot] et [raquette tennis] ont t achets et leurs stocks dcrments de la quantit achete. L'article [rollers] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
pour ajouter une nouvelle connexion, cliquer droit sur [Connexion de donnes] et prendre l'option [Ajouter une connexion]. On obtient alors un assistant avec lequel on peut dfinir les caractristiques de la connexion :
le panneau [Fournisseur] donne la liste des pilotes OLEDB disponibles. Nous allons utiliser pour la nouvelle couche [dao], un pilote [Microsoft Jet 4.0 OLE DB Provider] qui donne accs aux bases ACCESS. quittons momentanment Visual Studio pour crer la base ACCESS [articles.mdb] ayant l'unique table suivante :
web3tier-part2
34/105
la structure de la table est la suivante : numrique - entier - cl primaire texte - 20 caractres numrique - rel double numrique - entier numrique - entier
revenons Visual Studio et crons une nouvelle connexion comme expliqu prcdemment :
avec le bouton [1], dsigner la base ACCESS qui vient d'tre cre puis terminer la dfinition de la connexion avec le bouton [Terminer]. La connexion cre apparat dsormais dans la liste des connexions disponibles :
un double clic sur la table [ARTICLES] nous donne accs son contenu :
on peut alors ajouter, modifier, supprimer des lignes dans la table. slectionner dans l'explorateur de serveurs, la nouvelle connexion pour avoir accs sa feuille de proprits :
web3tier-part2
35/105
la chane de connexion est utile connatre. Elle nous servira pour nous connecter la base :
Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=D:\data\serge\databases\access\articles\articles.mdb;Mode=Share Deny None;Extended Properties="";Jet OLEDB:System database="";Jet OLEDB:Registry Path="";Jet OLEDB:Engine Type=5;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\data\serge\databases\access\articles\articles.mdb;
les classes ncessaires sont dans l'espace de noms [System.Data.OleDb] au lieu de l'espace de noms [System.Data.Odbc] la connexion de type [OdbcConnection] a maintenant le type [OleDbConnection] les objets [OdbcCommand] ont maintenant le type [OleDbCommand]
...
36/105
insertCommand = New OleDbCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (?,?,?,?,?)", connexion) updatecommand = New OleDbCommand("update ARTICLES set nom=?, prix=?, stockactuel=?, stockminimum=? where id=?", connexion) deleteSomeCommand = New OleDbCommand("delete from ARTICLES where id=?", connexion) selectSomeCommand = New OleDbCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES where id=?", connexion) updateStockCommand = New OleDbCommand("update ARTICLES set stockactuel=stockactuel+? where id=? and (stockactuel+?)>=0", connexion) selectAllCommand = New OleDbCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES", connexion) deleteAllCommand = New OleDbCommand("delete from ARTICLES", connexion) End Sub Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle ' section exclusive SyncLock Me ' on prpare la requte d'insertion With insertCommand.Parameters .Clear() .Add(New OleDbParameter("id", unArticle.id)) .Add(New OleDbParameter("nom", unArticle.nom)) .Add(New OleDbParameter("prix", unArticle.prix)) .Add(New OleDbParameter("stockactuel", unArticle.stockactuel)) .Add(New OleDbParameter("stockminimum", unArticle.stockminimum)) End With Try 'on l'excute Return executeUpdate(insertCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur l'ajout de l'article [{0}] : {1}", unArticle.ToString, ex.Message)) End Try End SyncLock End Function Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle ' section exclusive SyncLock Me ' on prpare la requte de mise jour du stock With updateStockCommand.Parameters .Clear() .Add(New OleDbParameter("mvt1", mouvement)) .Add(New OleDbParameter("id", idArticle)) .Add(New OleDbParameter("mvt2", mouvement)) End With 'on l'excute Try Return executeUpdate(updateStockCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors du changement de stock [idArticle={0}, mouvement={1}] : [{2}]", idArticle, mouvement, ex.Message)) End Try End SyncLock End Function Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles ' section exclusive SyncLock Me Try 'on excute la requte d'insertion executeUpdate(deleteAllCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression des articles : {0}", ex.Message)) End Try End SyncLock End Sub Public Function getAllArticles() As System.Collections.IList Implements IArticlesDao.getAllArticles ' section exclusive SyncLock Me Try 'on excute la requte select Dim articles As IList = executeQuery(selectAllCommand) 'on retourne le liste Return articles Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de l'obtention des articles [select id,nom,prix,stockactuel,stockminimum from articles]: {0}", ex.Message)) End Try web3tier-part2
37/105
End SyncLock End Function Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById ' section exclusive SyncLock Me ' on prpare la requte select With selectSomeCommand.Parameters .Clear() .Add(New OleDbParameter("id", idArticle)) End With 'on l'excute Try 'on excute la requte Dim articles As IList = executeQuery(selectSomeCommand) 'on test si l'on a trouv l'article If articles.Count = 0 Then Return Nothing 'on retourne l'article Return CType(articles.Item(0), Article) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la recherche de l'article [{0} : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle ' section exclusive SyncLock Me ' on prpare la requte update With updatecommand.Parameters .Clear() .Add(New OleDbParameter("nom", unArticle.nom)) .Add(New OleDbParameter("prix", unArticle.prix)) .Add(New OleDbParameter("stockactuel", unArticle.stockactuel)) .Add(New OleDbParameter("stockminimum", unArticle.stockactuel)) .Add(New OleDbParameter("id", unArticle.id)) End With ' on l'excute Try 'on excute la requte d'insertion Return executeUpdate(updatecommand) Catch ex As Exception 'erreur de requte Throw New Exception("Erreur lors de la modification de l'article [" + unArticle.ToString + "]", ex) End Try End SyncLock End Function Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle ' section exclusive SyncLock Me ' on prpare la requte delete With deleteSomeCommand.Parameters .Clear() .Add(New OleDbParameter("id", idArticle)) End With 'on l'excute Try 'on excute la requte de suppression Return executeUpdate(deleteSomeCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression de l'article [id={0}] : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Private Function executeQuery(ByVal query As OleDbCommand) As IList ' excution d'une requte SELECT ' dclaration de l'objet permettant l'accs toutes les lignes de la table rsultat Dim myReader As OleDbDataReader = Nothing Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte myReader = query.ExecuteReader() 'on dclare une liste d'articles pour la retourner par la suite Dim articles As IList = New ArrayList Dim unArticle As Article While myReader.Read() web3tier-part2
38/105
'on prpare un article avec les valeurs du reader unArticle = New Article unArticle.id = myReader.GetInt32(0) unArticle.nom = myReader.GetString(1) unArticle.prix = myReader.GetDouble(2) unArticle.stockactuel = myReader.GetInt32(3) unArticle.stockminimum = myReader.GetInt32(4) 'on ajoute l'article la liste articles.Add(unArticle) End While 'on retourne le rsultat Return articles Finally ' libration des ressources If Not myReader Is Nothing And Not myReader.IsClosed Then myReader.Close() If Not connexion Is Nothing Then connexion.Close() End Try End Function Private Function executeUpdate(ByVal sqlCommand As OleDbCommand) As Integer ' excution d'une requte de mise jour Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte Return sqlCommand.ExecuteNonQuery() Finally ' libration des ressources If Not connexion Is Nothing Then connexion.Close() End Try End Function End Class End Namespace
Le lecteur est invit lire ce code la lumire des commentaires de la classe [ArticlesDaoPlainODBC] faits prcdemment.
39/105
nous crons dans le dossier Visual Studio du projet [dao-oledb] le dossier [tests] ( droite) par recopie du dossier [tests] du projet [dao-odbc] ( gauche) :
dans le dossier [tests] du projet [dao-oledb] nous remplaons la DLL [webarticles-dao.dll] par la DLL [webarticles-dao.dll] issue de la gnration du projet [dao-oledb] nous modifions le fichier de configuration [spring-config.xml] afin d'instancier la nouvelle classe [ArticlesDaoOleDb] :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <!-3. <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" 4. "http://www.springframework.net/dtd/spring-objects.dtd"> 5. --> 6. <objects> 7. <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoOleDb, webarticles-dao"> 8. <constructor-arg index="0"> 9. <value>Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\data\serge\databases\access\articles\articles.mdb;</value> 10. </constructor-arg> 11. </object> 12. </objects>
Commentaires :
ligne 7, l'objet [articlesdao] est maintenant associ une instance de la classe [ ArticlesDaoOleDb] cette classe a un constructeur un argument : la chane de connexion la base OleDb ACCESS - ligne 9
6.4.2 Tests
Nous sommes prts pour les tests. A l'aide de l'application [Nunit-Gui], nous chargeons la DLL [test-webarticles-dao.dll] du dossier [tests] ci-dessus et excutons le test [testGetAllArticles] :
Malgr le nom [NUnitTestArticlesDaoArrayList] donn initialement la classe de test, c'est bien la classe [ArticlesDaoOleDb] qui est ici teste. La copie d'cran montre que nous avons rcupr correctement les articles que nous avions placs dans la table [ARTICLES]. Maintenant, faisons la totalit des tests :
web3tier-part2
40/105
Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis (couleur verte).
dans le dossier [bin], la DLL de l'ancienne couche [dao] est remplace par la DLL de la nouvelle couche [dao] implmente par la classe [ArticlesDaoOleDb] dans [runtime], le fichier de configuration [web.config] est remplac par un fichier qui prend en compte la nouvelle classe d'implmentation :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <configuration> 3. <configSections> 4. <sectionGroup name="spring"> 5. <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> 6. <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> 7. </sectionGroup> 8. </configSections> 9. <spring> 10. <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> 11. <resource uri="config://spring/objects" /> 12. </context> 13. <objects> 14. <object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoOleDb, webarticles-dao"> 15. <constructor-arg index="0"> 16. <value>Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\data\serge\databases\access\articles\articles.mdb;</value> 17. </constructor-arg> 18. </object> 19. <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> 20. <constructor-arg index="0"> 21. <ref object="articlesDao" /> 22. </constructor-arg> 23. </object> 24. </objects> 25. </spring> 26. <appSettings> 27. <add key="urlMain" value="/webarticles/main.aspx" /> 28. <add key="urlInfos" value="vues/infos.aspx" /> 29. <add key="urlErreurs" value="vues/erreurs.aspx" /> 30. <add key="urlListe" value="vues/liste.aspx" /> 31. <add key="urlPanier" value="vues/panier.aspx" /> 32. <add key="urlPanierVide" value="vues/paniervide.aspx" /> 33. </appSettings> 34.</configuration>
Commentaires :
les lignes 14-18 associent au singleton [articlesDao] une instance de la nouvelle classe [ArticlesDaoOleDb]. C'est la seule modification.
Nous gardons la mme configuration du serveur web [Cassini] qu'auparavant. Nous initialisons la table des articles avec les valeurs suivantes :
web3tier-part2
41/105
Assurez-vous que la base d'articles n'est pas utilise par un programme tel Visual Studio ou ACCESS. Avec un navigateur nous demandons l'url [http://localhost/webarticles/main.aspx] :
Les articles [pantalon] et [jupe] ont t achets et leurs stocks dcrments de la quantit achete. L'article [manteau] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
Le lien [firebird-net-provider] est le lien utiliser pour tlcharger les classes .Net d'accs au SGBD Firebird. L'installation du paquetage donne naissance un dossier analogue au suivant : web3tier-part2 42/105
[FirebirdSql.Data.Firebird.dll] : l'assembly contenant les classes .Net d'accs au SGBD Firebird [FirebirdNETProviderSDK.chm] : la documentation sur ces classes
Par la suite, pour qu'un projet Viusal Studio puisse utiliser ces classes, on fera deux choses :
on mettra l'assembly [FirebirdSql.Data.Firebird.dll] dans le dossier [bin] du projet on ajoutera ce mme assembly aux rfrences du projet
les classes ncessaires sont dans l'espace de noms [FirebirdSql.Data.Firebird] au lieu de l'espace de noms [System.Data.SqlClient] la connexion de type [SqlConnection] a maintenant le type [FbConnection] les objets [SqlCommand] ont maintenant le type [FbCommand] les objets [SqlParameter] ont maintenant le type [FbParameter]
Le constructeur de la classe admet quatre paramtres, avec lesquels il construit la chane de connexion la base :
' constructeur Public Sub New(ByVal serveur As String, ByVal databaseName As String, ByVal uid As String, ByVal password As String) ' serveur : nom de la machine hte du SGBD ' databaseName : chemin d'accs la base de donnes ' uid : identit de l'utilisateur qui se connecte ' password : son mot de passe ... End Sub
43/105
serveur : nom de la machine hte du SGBD Firebird databaseName : chemin d'accs la base de donnes exploiter uid : identit de l'utilisateur qui se connecte la base password : son mot de passe
'on rcupre le nom de la base pass en argument Me.databasePath = databasePath 'on instancie la connexion Dim connectString As String = String.Format("DataSource={0};Database={1};User={2};Password={3}", serveur, databasePath, uid, password) connexion = New FbConnection(connectString) ' on prpare les requtes SQL insertCommand = New FbCommand("insert into ARTICLES(id, nom, prix, stockactuel, stockminimum) values (@id,@nom,@prix,@sa,@sm)", connexion) updatecommand = New FbCommand("update ARTICLES set nom=@nom, prix=@prix, stockactuel=@sa, stockminimum=@sm where id=@id", connexion) deleteSomeCommand = New FbCommand("delete from ARTICLES where id=@id", connexion) selectSomeCommand = New FbCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES where id=@id", connexion) updateStockCommand = New FbCommand("update ARTICLES set stockactuel=stockactuel+@mvt where id=@id and (stockactuel+@mvt)>=0", connexion) selectAllCommand = New FbCommand("select id, nom, prix, stockactuel, stockminimum from ARTICLES", connexion) deleteAllCommand = New FbCommand("delete from ARTICLES", connexion) End Sub Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle ' section exclusive SyncLock Me ' on prpare la requte d'insertion With insertCommand.Parameters .Clear() .Add(New FbParameter("@id", unArticle.id)) .Add(New FbParameter("@nom", unArticle.nom)) .Add(New FbParameter("@prix", unArticle.prix)) .Add(New FbParameter("@sa", unArticle.stockactuel)) .Add(New FbParameter("@sm", unArticle.stockminimum)) End With Try 'on l'excute Return executeUpdate(insertCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur l'ajout de l'article [{0}] : {1}", unArticle.ToString, ex.Message)) End Try End SyncLock End Function Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle ' section exclusive SyncLock Me ' on prpare la requte de mise jour du stock With updateStockCommand.Parameters .Clear() .Add(New FbParameter("@mvt", mouvement)) .Add(New FbParameter("@id", idArticle)) End With 'on l'excute Try Return executeUpdate(updateStockCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors du changement de stock [idArticle={0}, mouvement={1}] : [{2}]", idArticle, mouvement, ex.Message)) End Try End SyncLock End Function Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles ' section exclusive SyncLock Me Try 'on excute la requte d'insertion executeUpdate(deleteAllCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression des articles : {0}", ex.Message)) End Try End SyncLock End Sub Public Function getAllArticles() As System.Collections.IList Implements IArticlesDao.getAllArticles ' section exclusive web3tier-part2
44/105
SyncLock Me Try 'on excute la requte select Dim articles As IList = executeQuery(selectAllCommand) 'on retourne le liste Return articles Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de l'obtention des articles [select id,nom,prix,stockactuel,stockminimum from articles]: {0}", ex.Message)) End Try End SyncLock End Function Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById ' section exclusive SyncLock Me ' on prpare la requte select With selectSomeCommand.Parameters .Clear() .Add(New FbParameter("@id", idArticle)) End With 'on l'excute Try 'on excute la requte Dim articles As IList = executeQuery(selectSomeCommand) 'on test si l'on a trouv l'article If articles.Count = 0 Then Return Nothing 'on retourne l'article Return CType(articles.Item(0), Article) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la recherche de l'article [{0} : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle ' section exclusive SyncLock Me ' on prpare la requte update With updatecommand.Parameters .Clear() .Add(New FbParameter("@nom", unArticle.nom)) .Add(New FbParameter("@prix", unArticle.prix)) .Add(New FbParameter("@sa", unArticle.stockactuel)) .Add(New FbParameter("@sm", unArticle.stockminimum)) .Add(New FbParameter("@id", unArticle.id)) End With ' on l'excute Try 'on excute la requte d'insertion Return executeUpdate(updatecommand) Catch ex As Exception 'erreur de requte Throw New Exception("Erreur lors de la modification de l'article [" + unArticle.ToString + "]", ex) End Try End SyncLock End Function Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle ' section exclusive SyncLock Me ' on prpare la requte delete With deleteSomeCommand.Parameters .Clear() .Add(New FbParameter("@id", idArticle)) End With 'on l'excute Try 'on excute la requte de suppression Return executeUpdate(deleteSomeCommand) Catch ex As Exception 'erreur de requte Throw New Exception(String.Format("Erreur lors de la suppression de l'article [id={0}] : {1}", idArticle, ex.Message)) End Try End SyncLock End Function Private Function executeQuery(ByVal query As FbCommand) As IList ' excution d'une requte SELECT web3tier-part2
45/105
' dclaration de l'objet permettant l'accs toutes les lignes de la table rsultat Dim myReader As FbDataReader = Nothing Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte myReader = query.ExecuteReader() 'on dclare une liste d'articles pour la retourner par la suite Dim articles As IList = New ArrayList Dim unArticle As Article While myReader.Read() 'on prpare un article avec les valeurs du reader unArticle = New Article unArticle.id = myReader.GetInt32(0) unArticle.nom = myReader.GetString(1) unArticle.prix = myReader.GetDouble(2) unArticle.stockactuel = myReader.GetInt32(3) unArticle.stockminimum = myReader.GetInt32(4) 'on ajoute l'article la liste articles.Add(unArticle) End While 'on retourne le rsultat Return articles Finally ' libration des ressources If Not myReader Is Nothing And Not myReader.IsClosed Then myReader.Close() If Not connexion Is Nothing Then connexion.Close() End Try End Function Private Function executeUpdate(ByVal updateCommand As FbCommand) As Integer ' excution d'une requte de mise jour Try 'on cre une connexion la BDD connexion.Open() 'on excute la requte Return updateCommand.ExecuteNonQuery() Finally ' libration des ressources If Not connexion Is Nothing Then connexion.Close() End Try End Function End Class End Namespace
Le lecteur est invit lire ce code la lumire des commentaires de la classe [ArticlesDaoSqlServer] faits prcdemment.
On notera la prsence de l'assembly [FirebirdSql.Data.Firebird.dll] dans les rfrences du projet. Cette DLL a t place dans le dossier [bin] du projet. Le projet est configur pour gnrer une DLL appele [webarticles-dao.dll] :
web3tier-part2
46/105
nous crons dans le dossier Visual Studio du projet [dao-firebird-provider] le dossier [tests] ( droite) par recopie du dossier [bin] du projet de tests de la couche [dao-odbc] ( gauche) :
dans le dossier [tests] nous remplaons la DLL [webarticles-dao.dll] par la DLL [webarticles-dao.dll] issue de la gnration du projet [dao-firebird-provider] nous modifions le fichier de configuration [spring-config.xml] afin d'instancier la nouvelle classe [ArticlesDaoFirebirdProvider] :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <!-3. <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" 4. "http://www.springframework.net/dtd/spring-objects.dtd"> 5. --> 6. <objects> 7. <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoFirebirdProvider, webarticles-dao"> 8. <constructor-arg index="0"> 9. <value>localhost</value> 10. </constructor-arg> 11. <constructor-arg index="1"> 12. <value>D:\data\serge\databases\firebird\dbarticles2.gdb</value> 13. </constructor-arg> 14. <constructor-arg index="2"> 15. <value>sysdba</value> 16. </constructor-arg> 17. <constructor-arg index="3"> 18. <value>masterkey</value> 19. </constructor-arg> 20. </object> 21.</objects>
Commentaires :
ligne 7, l'objet [articlesdao] est maintenant associ une instance de la classe [ArticlesDaoFirebirdProvider] cette classe a un constructeur quatre arguments la machine hte du SGBD - ligne 9 le chemin d'accs la base de donnes Firebird - ligne 12 le login de l'utilisateur qui se connecte - ligne 15 47/105
web3tier-part2
7.4.2 Tests
La table [ARTICLES] de la source de donnes est remplie avec les articles suivants (utiliser IBExpert) :
Nous sommes prts pour les tests. A l'aide de l'application [Nunit-Gui], nous chargeons la DLL [test-webarticles-dao.dll] du dossier [tests] ci-dessus et excutons le test [testGetAllArticles] :
Malgr le nom [NUnitTestArticlesDaoArrayList] donn initialement la classe de test, c'est bien la classe [ArticlesDaoFirebirdProvider] qui est ici teste. La copie d'cran montre que nous avons rcupr correctement les articles que nous avions placs dans la table [ARTICLES]. Maintenant, faisons la totalit des tests :
Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis (couleur verte). Ce qu'il ne peut pas voir, c'est que les tests se sont drouls nettement plus rapidement qu'avec la base d'articles accde via un pilote ODBC de notre premire implmentation.
dans le dossier [bin], la DLL de l'ancienne couche [dao] est remplace par la DLL de la nouvelle couche [dao] implmente par la classe [ArticlesDaoFirebirdProvider]. Nous y plaons galement la DLL ncessaire Firebird [FirebirdSql.Data.Firebird.dll] : 48/105
web3tier-part2
dans [runtime], le fichier de configuration [web.config] est remplac par un fichier qui prend en compte la nouvelle classe d'implmentation :
1. <?xml version="1.0" encoding="iso-8859-1" ?> 2. <configuration> 3. <configSections> 4. <sectionGroup name="spring"> 5. <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> 6. <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> 7. </sectionGroup> 8. </configSections> 9. <spring> 10. <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> 11. <resource uri="config://spring/objects" /> 12. </context> 13. <objects> 14. <object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoFirebirdProvider, webarticles-dao"> 15. <constructor-arg index="0"> 16. <value>localhost</value> 17. </constructor-arg> 18. <constructor-arg index="1"> 19. <value>D:\data\serge\databases\firebird\dbarticles2.gdb</value> 20. </constructor-arg> 21. <constructor-arg index="2"> 22. <value>sysdba</value> 23. </constructor-arg> 24. <constructor-arg index="3"> 25. <value>masterkey</value> 26. </constructor-arg> 27. </object> 28. <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> 29. <constructor-arg index="0"> 30. <ref object="articlesDao" /> 31. </constructor-arg> 32. </object> 33. </objects> 34. </spring> 35. <appSettings> 36. <add key="urlMain" value="/webarticles/main.aspx" /> 37. <add key="urlInfos" value="vues/infos.aspx" /> 38. <add key="urlErreurs" value="vues/erreurs.aspx" /> 39. <add key="urlListe" value="vues/liste.aspx" /> 40. <add key="urlPanier" value="vues/panier.aspx" /> 41. <add key="urlPanierVide" value="vues/paniervide.aspx" /> 42. </appSettings> 43.</configuration>
Commentaires :
les lignes 14-27 associent au singleton [articlesDao] une instance de la nouvelle classe [ArticlesDaoFirebirdProvider]. C'est la seule modification.
Nous sommes prts pour les tests. Nous configurons le serveur web [Cassini] comme dans les tests prcdents. Nous initialisons la table des articles avec les valeurs suivantes :
web3tier-part2
49/105
Les articles [crayon bille] et [ramette 50 feuilles] ont t achets et leurs stocks dcrments de la quantit achete. L'article [stylo plume] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
web3tier-part2
50/105
Nous avons crit quatre implmentations diffrentes de la couche [dao] de notre application [webarticles]. A chaque fois nous avons pu intgrer la nouvelle couche [dao] l'application [webarticles] sans recompilation des deux autres couches [web] et [domain]. Ceci a t obtenu, rappelons-le, par deux choix d'architecture :
l'accs aux couches via des interfaces l'intgration des couches par Spring
Nous souhaitons aller un peu plus loin. Bien que diffrentes, nos quatre implmentations de la couche [dao] offrent des similitudes frappantes. Une fois la premire implmentation crite, les trois autres ont t obtenues quasiment par copier-coller et substitution de certains mots cls par d'autres mots cls. La logique, elle, n'a pas t modifie. On peut se demander s'il ne serait pas possible d'avoir une implmentation qui nous affranchirait des diffrents modes d'accs aux donnes. Nous en avons utilis quatre :
accs via un pilote ODBC une source de donnes ODBC accs direct une base SQL Server accs via un pilote Ole Db une source de donnes Ole Db accs direct une base Firebird
L'outil Ibatis SqlMap [[http://www.ibatis.com/] rend possible le dveloppement de couches d'accs aux donnes qui soient indpendantes de la nature relle de la source de donnes. L'accs aux donnes est assur l'aide :
de fichiers de configuration dans lesquels sont places les informations qui dfinissent la source de donnes et les oprations que l'on veut faire dessus une bibliothque de classes qui s'appuient sur ces informations pour accder aux donnes
L'outil Ibatis SqlMap a t dvelopp initialement pour la plate-forme Java. Son portage vers la plate-forme .NET est rcent et semble-t-il partiellement bogu (avis personnel qui demanderait une vrification pousse). Nanmoins l'outil ayant fait ses preuves sur la plate-forme Java, il semble intressant d'en prsenter la version .NET.
On choisira le lien [Stable Binaries] qui nous emmne chez [SourceForge.net]. Suivre le processus de tlchargement jusqu'au bout. On obtient un zip contenant les fichiers suivants :
Dans un projet Visual Studio utilisant Ibatis SqlMap, il faut faire deux choses :
mettre les fichiers ci-dessus dans le dossier [bin] du projet ajouter au projet une rfrence chacun de ces fichiers
51/105
2. sqlmap.config : dfinit les caractristiques de la connexion tablir 3. fichiers de mapping : dfinissent les oprations faire sur les donnes La logique de ces fichiers est la suivante : pour accder aux donnes, il va nous falloir une connexion. Pour reprsenter celle-ci, nous avons dj rencontr plusieurs classes : OdbcConnection, SqlConnection, OleDbConnection, FbConnection. Il va galement nous falloir un objet [Command] pour mettre des requtes SQL : OdbcCommand, SqlCommand, OleDbCommand, FbCommand. Etc.. Dans le fichier [providers.config], nous dfinissons l'ensemble des classes dont nous avons besoin. le fichier [sqlmap.config] dfinit essentiellement la chane de connexion la base qui contient les donnes. La connexion la base sera ouverte par instanciation de la classe [Connection] dfinie dans [providers.config], au constructeur duquel sera passe la chane de connexion dfinie dans [sqlmap.config]. les fichiers de mapping dfinissent : des associations entre lignes de tables de donnes et classe .NET dont les instances contiendront ces lignes les oprations SQL excuter. Celles-ci sont identifies par un nom. Le code .NET excute ces oprations via leur nom, ce qui a pour consquence d'liminer tout code SQL du code .NET.
8.4.1 providers.config
Le fichier [providers.config] pour une source ODBC est celui-ci :
1. <?xml version="1.0" encoding="utf-8" ?> 2. 3. <providers> 4. <clear/> 5. <provider 6. name="Odbc1.1" 7. enabled="true" 8. assemblyName="System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 9. connectionClass="System.Data.Odbc.OdbcConnection" 10. commandClass="System.Data.Odbc.OdbcCommand" 11. parameterClass="System.Data.Odbc.OdbcParameter" 12. parameterDbTypeClass="System.Data.Odbc.OdbcType" 13. parameterDbTypeProperty="OdbcType" 14. dataAdapterClass="System.Data.Odbc.OdbcDataAdapter" 15. commandBuilderClass="System.Data.Odbc.OdbcCommandBuilder" 16. usePositionalParameters = "true" 17. useParameterPrefixInSql = "false" 18. useParameterPrefixInParameter = "false" 19. parameterPrefix = "@" 20. /> 21.</providers>
Commentaires :
un fichier [providers.config] est distribu avec le paquetage de [SqlMap]. Il propose plusieurs fournisseurs d'accs (provider) standard. Le code ci-dessus provient directement de ce fichier. un <provider> a un nom - ligne 6 - peut tre quelconque un <provider> peut tre activ [enabled=true] ou non [enabled=false]. S'il est activ, la DLL rfrence ligne 8 doit tre accessible. Un fichier [providers.config] peut avoir plusieurs balises <provider>. ligne 8 - nom de l'assembly qui contient les classes dfinies lignes 9-15 ligne 9 - classe utiliser pour crer une connexion ligne 10 - classe utiliser pour crer un objet [Command] d'mission de commandes SQL ligne 11 - classe utiliser pour grer les paramtres d'une commande SQL paramtre ligne 12 - classe d'numration des types de donnes possibles pour les champs d'une table ligne 13 - nom de la proprit d'un objet [Parameter] qui contient le type de la valeur de ce paramtre ligne 14 - nom de la classe [Adapter] permettant de crer des objets [DataSet] partir de la source de donnes ligne 15 - nom de la classe [CommandBuilder] qui associe un objet [Adapter] permet de gnrer automatiquement les proprits [InsertCommand, DeleteCommand, UpdateCommand] de celui-ci partir de sa proprit [SelectCommand] lignes 16 - 19 - on dfinit comment sont gres les commandes SQL paramtres. Selon les cas, il faut crire par exemple :
ou bien
web3tier-part2
52/105
Dans le premier cas, on parle de paramtres positionnels formels. Les valeurs effectives de ceux-ci doivent tre fournies dans l'ordre des paramtres formels. Dans le second cas, on a affaire des paramtres nomms. On fournit une valeur un tel paramtre en prcisant son nom. L'ordre n'a plus d'importance. ligne 16 - on indique que les sources ODBC utilisent des paramtres positionnels lignes 17-19 - concernent les paramtres nomms. Ici, il n'y en a pas. Ces informations permettent SqlMap de savoir par exemple, quelle classe il doit instancier pour crer une connexion. Ici ce sera la classe [OdbcConnection] (ligne 9).
8.4.2 sqlmap.config
Le fichier [providers.config] dfinit les classes utiliser pour accder une source ODBC. Il n'indique aucune source ODBC. C'est le fichier [sqlmap.config] qui le fait :
1. <?xml version="1.0" encoding="utf-8" ?> 2. <sqlMapConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Schemas\SqlMapConfig.xsd"> 3. <properties resource="properties.xml"/> 4. <settings> 5. <setting useStatementNamespaces="false" /> 6. <setting cacheModelsEnabled="false" /> 7. </settings> 8. <!-- ==== source de donnes ========= --> 9. <database> 10. <provider name="${provider}"/> 11. <dataSource name="sqlmaparticles" connectionString="${connectionString}"/> 12. <transactionManager type="ADO/SWC" /> 13. </database> 14. <sqlMaps> 15. <sqlMap resource="articles.xml" /> 16. </sqlMaps> 17.</sqlMapConfig>
Commentaires :
ligne 3 - on dfinit un fichier de proprits [properties.xml]. Celui-ci dfinit des couples (cl, valeur). Les cls peuvent tre quelconques. La valeur associe une cl C est obtenue par la notation ${C} dans [sqlmap.config]. Voici le fichier [properties.xml] qui sera associ au fichier [sqlmap.config] prcdent :
1. 2. 3. 4. 5. <?xml version="1.0" encoding="utf-8" ?> <settings> <add key="provider" value="Odbc1.1" /> <add key="connectionString" value="DSN=odbc-firebird-articles;UID=SYSDBA;PASSWORD=masterkey" /> </settings>
ligne 3 - la cl [provider] est dfinie. Sa valeur est le nom de la balise <provider> utiliser dans [providers.config] ligne 4 - la cl [connectionString] est dfinie. Sa valeur est la chane de connexion utiliser pour ouvrir une connexion sur la source de donnes ODBC Firebird. lignes 4-7 - des paramtres de configuration : ligne 5 - les requtes SQL seront identifies par un nom qui lui mme peut faire partie d'un espace de noms. [useStatementNamespaces="false"] indique qu'on n'utilisera pas d'espaces de noms. ligne 6 - SqlMap possde diffrentes statgies de cache pour minimiser les accs la source de donnes. [cacheModelsEnabled="false"] indique qu'on n'en utilisera aucune. lignes 9-13 - on dfinit les caractristiques de la source de donnes : ligne 10 - nom du <provider> de [providers.config] utiliser ligne 11 - chane de connexion la source de donnes ligne 12 - gestionnaire de transactions. Ici nous ne l'avons pas utilis mais avons laiss nanmoins la ligne car elle tait dans le fichier de distribution standard. lignes 14-16 - liste des fichiers dfinissant les oprations SQL effectuer sur la source de donnes. ligne 15 - dfinit le fichier de mapping [articles.xml]
8.4.3 articles.xml
Ce fichier sert deux fonctions :
web3tier-part2
53/105
dfinir un mapping objet des tables de la source de donnes. Dans les cas les plus simples, cela revient associer une classe une ligne d'une table. dfinir des oprations SQL paramtres et les nommer.
Commentaires :
lignes 4-11 - on dfinit un mapping entre une ligne de la table [ARTICLES] de la source de donnes et la classe [istia.st.articles.dao.Article]. A chaque colonne (column) de la table est associe une proprit (property) de la classe [Article]. Ce mapping permet [SqlMap] de construire le rsultat d'une opration SQL SELECT. Chaque ligne rsultat du SELECT sera place dans un objet [Article] selon les rgles du mapping. ligne 5 - le mapping fait l'objet d'une balise <resultMap> et est nomm avec l'attribut [id="article"]. La classe associe est dsigne par l'attribut [class="istia.st.articles.dao.Article"]. lignes 14-44 - on dfinit les oprations SQL dont on a besoin lignes 16-18 - on dfinit une opration SELECT appele [getAllArticles] ligne 16 - l'opration SELECT est nomme [name= "getAllArticles "] et le mapping utiliser est dfini par l'attribut [resultMap="article"]. On fait donc rfrence ici au mapping des lignes 5-11 ligne 17 - texte de la commande SQL excuter lignes 20-22 - on dfinit la commande SQL-Delete [clearAllArticles] destine vider la table des articles. lignes 24-27 - on dfinit la commande SQL-Insert [insertArticle] destine ajouter un nouvel article dans la table des articles. C'est une requte paramtre par les lments (#id#, #nom#, #prix#, #stockactuel#, #stockminimum#). Les valeurs de ces cinq lments viendront d'un objet [Article] pass en paramtre : [parameterClass="istia.st.articles.dao.Article"]. L'objet paramtre doit avoir les proprits (id, nom, prix, stockactuel, stockminimum) rfrences par la commande SQL paramtre. lignes 29-31 - on dfinit la commande SQL Delete [deleteArticle] destine supprimer un article dont on connat le numro #value#. Ce numro sera pass en paramtre : [parameterClass="int"]. C'est une rgle gnrale. Lorsque le paramtre est unique, il est rfrenc par le mot cl #value# dans le texte de la commande SQL. 54/105
web3tier-part2
lignes 33-35 - on dfinit la commande SQL-Update [modifyArticle] destine modifier un article dont on connat le numro. Comme pour la commande [insertArticle], les cinq informations ncessaires viendront des proprits d'un objet [istia.st.articles.dao.Article]. lignes 37-39 - on dfinit la commande SQL-Select [getArticleById] qui permet d'obtenir la ligne d'un article dont on connat le numro. lignes 41-43 - on dfinit la commande SQL-Update [changerStockArticle] qui modifie le champ [stockactuel] d'un article dont on connat le numro. Les deux informations ncessaires, le n #id# de l'article et l'incrment #mouvement# du stock seront trouves dans un dictionnaire : [parameterClass="Hashtable"]. Celui-ci devra avoir deux cls : id et mouvement. Ce seront les valeurs associes ces deux cls qui seront utilises dans la commande SQL.
dans le cas d'un test Nunit, les fichiers de configuration de [SqlMap] seront placs dans le mme dossier que les binaires tests. dans le cas d'une application web, ils seront placs la racine de l'application.
Les applications utilisant les classes de SqlMap doivent importer l'espaces de noms [IBatisNet.DataMapper] :
Imports IBatisNet.DataMapper
Toutes les oprations SQL se font au travers d'un singleton de type [Mapper], une classe dde l'espace de noms [IBatisNet.DataMapper ]. Le singleton est obtenu de la faon suivante :
Dim mappeur As SqlMapper = Mapper.Instance
la mthode [QueryForList] permet d'obtenir le rsultat d'une commande SELECT dans une liste le premier paramtre est le nom de la commande SQL excuter (cf articles.xml) le second paramtre est le paramtre transmettre la requte SQL. Doit correspondre l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass=Nothing]. Aussi passe-t-on ici un pointeur nul. le rsultat est de type IList. Les objets de cette liste sont indiqus par l'attribut [resultMap] de la commande SQL-select : [resultMap="article"]. "article" est un nom de mapping :
La classe associe ce mapping est [istia.st.articles.dao.Article]. Au final, la variable [articles] dfinie plus haut est une liste d'objets [ istia.st.articles.dao.Article]. Nous avons donc obtenu la totalit de la table [ARTICLES] en une instruction. Si la table [ARTICLES] est vide, on obtient un objet [IList] avec 0 lment. Pour excuter la commande SqlMap [getArticleById], on crira :
dim unArticle as Article=CType(mappeur.QueryForObject("getArticleById", idArticle), Article)
la mthode [QueryForObject] permet d'obtenir le rsultat d'une commande SELECT ne rendant qu'une ligne le premier paramtre est le nom de la commande SqlMap excuter le second paramtre est le paramtre transmettre la requte SQL. Doit correspondre l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass="int"]. Aussi passe-t-on ici un entier reprsentant le n de l'article cherch. web3tier-part2 55/105
le rsultat est de type Object. Si le SELECT n'a rendu aucune ligne, on a le pointeur nul (nothing) comme rsultat.
la mthode [Insert] permet d'excuter des commandes SQL INSERT le premier paramtre est le nom de la commande SqlMap excuter le second paramtre est le paramtre transmettre celle-ci. Doit correspondre l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass="istia.st.articles.dao.Article"]. Aussi passe-t-on ici un objet de type [istia.st.articles.dao.Article].
la mthode [Delete] permet d'excuter des commandes SQL DELETE le premier paramtre est le nom de la commande SQL excuter le second paramtre est le paramtre transmettre celle-ci. Doit correspondre l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass="int"]. Aussi passe-t-on ici le n de l'article supprimer. le rsultat de la mthode [Delete] est le nombre de lignes dtruites
la mthode [Update] permet d'excuter des commandes SQL UPDATE le premier paramtre est le nom de la commande SqlMap excuter le second paramtre est le paramtre transmettre celle-ci. Doit correspondre l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass="istia.st.articles.dao.Article"]. Aussi passe-t-on ici un objet de type [istia.st.articles.dao.Article]. le rsultat de la mthode [Update] est le nombre de lignes modifies.
le second paramtre correspond l'attribut [parameterClass] de la commande SqlMap. Dans [articles.xml], on a [parameterClass="Hashtable"]. La commande SQL paramtre [changerStockArticle] utilise les paramtres [id, mouvement].Aussi passe-t-on ici un dictionnaire ayant ces deux cls.
56/105
SyncLock Me Try Return mappeur.QueryForList("getAllArticles", Nothing) Catch ex As Exception Throw New Exception("Echec de l'obtention de tous les articles : [" + ex.ToString + "]") End Try End SyncLock End Function ' ajout d'un article Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle SyncLock Me Try ' unArticle : article ajouter ' insertion mappeur.Insert("insertArticle", unArticle) Return 1 Catch ex As Exception Throw New Exception("Echec de l'ajout de l'article [" + unArticle.ToString + "] : [" + ex.ToString + "]") End Try End SyncLock End Function ' supprime un article Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle SyncLock Me Try ' id : id de l'article supprimer ' suppression Return mappeur.Delete("deleteArticle", idArticle) Catch ex As Exception Throw New Exception("Erreur lors de la suppression de l'article d'id [" + idArticle.ToString + "] : [" + ex.ToString + "]") End Try End SyncLock End Function ' modifie un article Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle SyncLock Me Try ' mise jour Return mappeur.Update("modifyArticle", unArticle) Catch ex As Exception Throw New Exception("Erreur lors de la mise jour de l'article [" + unArticle.ToString + "] : [" + ex.ToString + "]") End Try End SyncLock End Function ' recherche d'un article Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById SyncLock Me Try ' id : id de l'article recherch Return CType(mappeur.QueryForObject("getArticleById", idArticle), Article) Catch ex As Exception Throw New Exception("Erreur lors de la recherche de l'article d'id [" + idArticle.ToString + "] : [" + ex.ToString + "]") End Try End SyncLock End Function ' suppression de tous les articles Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles SyncLock Me Try mappeur.Delete("clearAllArticles", Nothing) Catch ex As Exception Throw New Exception("Erreur lors de l'effacement de la table des articles : [" + ex.ToString + "]") End Try End SyncLock End Sub ' on change le stock d'un article Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle SyncLock Me Try ' id : id de l'article dont on change le stock ' mouvement : mouvement du stock web3tier-part2
57/105
Dim paramtres As New Hashtable(2) paramtres("id") = idArticle paramtres("mouvement") = mouvement ' mise jour Return mappeur.Update("changerStockArticle", paramtres) Catch ex As Exception Throw New Exception(String.Format("Erreur lors du changement de stock [{0},{1}] : {2}", idArticle, mouvement, ex.ToString)) End Try End SyncLock End Function End Class End Namespace
Le lecteur est invit lire ce code la lumire des explications donnes pour l'API de SqlMap. On notera avec intrt que l'utilisation de [SqlMap] a rduit fortement la quantit de code crire.
On notera la prsence des "assembly" ncessaires SqlMap dans les rfrences du projet. Ces DLL ont t places dans le dossier [bin] du projet. Le projet est configur pour gnrer une DLL appele [webarticles-dao.dll] :
nous crons dans le dossier Visual Studio du projet [dao-sqlmap] le dossier [test1] ( droite) par recopie du dossier [tests] du projet [dao-odbc] ( gauche) :
web3tier-part2
58/105
1. 2. 3. 4. 5. 6. 7. 8.
dans le dossier [tests] nous remplaons la DLL [webarticles-dao.dll] par la DLL [webarticles-dao.dll] issue de la gnration du projet [dao-sqlmap]. nous ajoutons les DLL ncessaires SqlMap ainsi que les fichiers de configuration tudis [providers.config, sqlmap.config, properties.xml, articles.xml]. nous modifions le fichier de configuration [spring-config.xml] afin d'instancier la nouvelle classe [ArticlesDaoSqlMap] :
<?xml version="1.0" encoding="iso-8859-1" ?> <!-<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" "http://www.springframework.net/dtd/spring-objects.dtd"> --> <objects> <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoSqlMap, webarticles-dao"/> </objects>
Commentaires :
ligne 7, l'objet [articlesdao] est maintenant associ une instance de la classe [ArticlesDaoSqlMap] cette classe n'a pas de constructeur. C'est le constructeur par dfaut qui sera utilis.
8.8.2 Tests
La table [ARTICLES] de la source de donnes Firebird est remplie avec les articles suivants :
Nous sommes prts pour les tests. A l'aide de l'application [Nunit-Gui], nous chargeons la DLL [test-webarticles-dao.dll] du dossier [test1] ci-dessus et excutons le test [testGetAllArticles] :
web3tier-part2
59/105
Malgr le nom [NUnitTestArticlesDaoArrayList] donn initialement la classe de test, c'est bien la classe [ArticlesDaoSqlMap] qui est ici teste. La copie d'cran montre que nous avons rcupr correctement les articles que nous avions placs dans la table [ARTICLES]. Maintenant, faisons la totalit des tests :
Le lecteur qui visualise ce document sur cran pourra voir que certains tests ont t russis (couleur verte) mais que d'autres ont chou (couleur rouge). Les tests qui ont chou sont les tests [testArticleAbsent] et [testChangerStockArticle]. Aprs de longues recherches, il semble que les causes de ces checs soient les suivantes :
dans [testArticleAbsent], on demande de modifier un article qui n'existe pas. On utilise pour cela la mthode [modifieArticle] qui rend le nombre de lignes modifies donc 0 ou 1. Ici, on devrait avoir 0. Au lieu de cela, on a une exception de type [IBatisNet.Common.Exceptions.ConcurrentException]. dans [changerStockArticle] on a une opration de nouveau de type [update]. Il s'agit de dcrmenter un stock d'une quantit plus grande que le stock. On utilise pour cela la mthode [changerStockArticle] qui rend le nombre de lignes modifies donc 0 ou 1. La commande SQL a t crite pour viter une mise jour (cf commande SQL "changerStockArticle" dans articles.xml) qui rendrait le stock ngatif. On s'attend ici obtenir 0 comme rsultat de la mthode [changerStockArticle]. De nouveau, on a une exception de type [IBatisNet.Common.Exceptions.ConcurrentException].
Les sources d'erreurs possibles sont nombreuses : 1. le code de la classe [ArticlesDaoSqlMap] est erron. C'est possible. Cependant, il vient d'un portage d'une classe Java qui avait fonctionn correctement avec la version Java de SqlMap. 2. la version .NET de SqlMap est bogue 3. le pilote ODBC de Firebird est bogu 4. ... En l'absence de certitudes, nous allons contourner l'obstacle en interceptant la fameuse exception [ IBatisNet.Common.Exceptions.ConcurrentException]. Le nouveau code de la classe [ArticlesDaoSqlMap] devient le suivant :
1. .... 2. Namespace istia.st.articles.dao 3. 4. Public Class ArticlesDaoSqlMap 5. Implements IArticlesDao 6. 7. ' champs privs 8. Dim mappeur As SqlMapper = Mapper.Instance 9. web3tier-part2
60/105
10. ' liste de tous les articles 11. Public Function getAllArticles() As IList Implements IArticlesDao.getAllArticles 12.... 13. End Function 14. 15. ' ajout d'un article 16. Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle 17.... 18. End Function 19. 20. ' supprime un article 21. Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle 22. SyncLock Me 23. Try 24. ' id : id de l'article supprimer 25. ' suppression 26. Return mappeur.Delete("deleteArticle", idArticle) 27. Catch ex As Exception 28. If ex.GetType.Equals(GetType(IBatisNet.Common.Exceptions.ConcurrentException)) Then Return 0 29. Throw New Exception("Erreur lors de la suppression de l'article d'id [" + idArticle.ToString + "] : [" + ex.ToString + "]") 30. End Try 31. End SyncLock 32. End Function 33. 34. ' modifie un article 35. Public Function modifieArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.modifieArticle 36. SyncLock Me 37. Try 38. ' mise jour 39. Return mappeur.Update("modifyArticle", unArticle) 40. Catch ex As Exception 41. If ex.GetType.Equals(GetType(IBatisNet.Common.Exceptions.ConcurrentException)) Then Return 0 42. Throw New Exception("Erreur lors de la mise jour de l'article [" + unArticle.ToString + "] : [" + ex.ToString + "]") 43. End Try 44. End SyncLock 45. End Function 46. 47. ' recherche d'un article 48. Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById 49.... 50. End Function 51. 52. ' suppression de tous les articles 53. Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles 54..... 55. End Sub 56. 57. ' on change le stock d'un article 58. Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle 59. SyncLock Me 60. Try 61. ' id : id de l'article dont on change le stock 62. ' mouvement : mouvement du stock 63. Dim paramtres As New Hashtable(2) 64. paramtres("id") = idArticle 65. paramtres("mouvement") = mouvement 66. ' mise jour 67. Return mappeur.Update("changerStockArticle", paramtres) 68. Catch ex As Exception 69. If ex.GetType.Equals(GetType(IBatisNet.Common.Exceptions.ConcurrentException)) Then Return 0 70. Throw New Exception(String.Format("Erreur lors du changement de stock [{0},{1}] : {2}", idArticle, mouvement, ex.ToString)) 71. End Try 72. End SyncLock 73. End Function 74. End Class 75.End Namespace
Les modifications sont aux lignes : 28, 41, 69. Pour les oprations SQL de type [UPDATE, DELETE], s'il se produit une exception de type [IBatisNet.Common.Exceptions.ConcurrentException], on rend 0 comme rsultat, indiquant par l qu'aucune ligne n'a t modifie ou supprime. Ceci fait, la DLL du projet est rgnre, place dans le dossier [test1] et les tests NUnit relancs :
web3tier-part2
61/105
Cette fois-ci c'est bon. Nous travaillerons dsormais avec cette DLL.
dans le dossier [bin], la DLL de l'ancienne couche [dao] est remplace par la DLL de la nouvelle couche [dao] implmente par la classe [ArticlesDaoSqlMap]. Nous y ajoutons les DLL ncessaires Firebird et SqlMap :
dans [runtime], on place les fichiers de configuration de SqlMap [providers.config, sqlmap.config, properties.xml, articles.xml] :
dans [runtime], le fichier de configuration [web.config] est remplac par un fichier qui prend en compte la nouvelle classe d'implmentation :
62/105
3. <configSections> 4. <sectionGroup name="spring"> 5. <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> 6. <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> 7. </sectionGroup> 8. </configSections> 9. <spring> 10. <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> 11. <resource uri="config://spring/objects" /> 12. </context> 13. <objects> 14. <object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoSqlMap, webarticles-dao"/> 15. <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> 16. <constructor-arg index="0"> 17. <ref object="articlesDao" /> 18. </constructor-arg> 19. </object> 20. </objects> 21. </spring> 22. <appSettings> 23. <add key="urlMain" value="/webarticles/main.aspx" /> 24. <add key="urlInfos" value="vues/infos.aspx" /> 25. <add key="urlErreurs" value="vues/erreurs.aspx" /> 26. <add key="urlListe" value="vues/liste.aspx" /> 27. <add key="urlPanier" value="vues/panier.aspx" /> 28. <add key="urlPanierVide" value="vues/paniervide.aspx" /> 29. </appSettings> 30.</configuration>
Commentaires :
la lignes 14 associent au singleton [articlesDao] une instance de la nouvelle classe [ArticlesDaoSqlMap]. C'est la seule modification.
Nous sommes prts pour les tests. Nous configurons le serveur web [Cassini] comme dans les tests prcdents. Nous initialisons la table des articles avec les valeurs suivantes :
web3tier-part2
63/105
Les articles [couteau] et [cuiller] ont t achets et leurs stocks dcrments de la quantit achete. L'article [fourchette] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
le contenu du dossier [bin] ne change pas dans [runtime], les fichiers de configuration de SqlMap [providers.config, properties.xml] changent. Les fichiers de configuration [sqlmap.config, articles.xml] ne changent pas. le fichier [providers.config] configure un nouveau <provider> :
<?xml version="1.0" encoding="utf-8" ?> <providers> <clear/> <provider name="sqlServer1.1" assemblyName="System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" connectionClass="System.Data.SqlClient.SqlConnection" commandClass="System.Data.SqlClient.SqlCommand" parameterClass="System.Data.SqlClient.SqlParameter" parameterDbTypeClass="System.Data.SqlDbType" parameterDbTypeProperty="SqlDbType" dataAdapterClass="System.Data.SqlClient.SqlDataAdapter" commandBuilderClass="System.Data.SqlClient.SqlCommandBuilder" usePositionalParameters = "false" useParameterPrefixInSql = "true" useParameterPrefixInParameter = "true" parameterPrefix="@" /> </providers>
Ce <provider> utilise les classes .NET d'accs aux sources de donnes SQL Server. Il est intgr en standard dans le fichier [providers.config] modle distribu avec SqlMap.
le fichier [properties.xml] dfinit le <provider> de la source MSDE ainsi que la chane de connexion de celle-ci :
<?xml version="1.0" encoding="utf-8" ?> <settings> <add key="provider" value="sqlServer1.1" /> web3tier-part2
64/105
Nous sommes prts pour les tests. Le serveur web [Cassini] garde sa configuration habituelle. Nous initialisons la table des articles de la source MSDE avec [EMS MS SQL Manager] :
web3tier-part2
65/105
Les articles [ballon foot] et [raquette tennis] ont t achets et leurs stocks dcrments de la quantit achete. L'article [rollers] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
le contenu du dossier [bin] ne change pas dans [runtime], les fichiers de configuration de SqlMap [providers.config, properties.xml] changent. Les fichiers de configuration [sqlmap.config, articles.xml] ne changent pas. le fichier [providers.config] configure un nouveau <provider> :
<?xml version="1.0" encoding="utf-8" ?> <providers> <clear/> <provider name="OleDb1.1" enabled="true" assemblyName="System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" connectionClass="System.Data.OleDb.OleDbConnection" commandClass="System.Data.OleDb.OleDbCommand" parameterClass="System.Data.OleDb.OleDbParameter" parameterDbTypeClass="System.Data.OleDb.OleDbType" parameterDbTypeProperty="OleDbType" dataAdapterClass="System.Data.OleDb.OleDbDataAdapter" commandBuilderClass="System.Data.OleDb.OleDbCommandBuilder" usePositionalParameters = "true" useParameterPrefixInSql = "false" useParameterPrefixInParameter = "false" parameterPrefix = "" /> </providers>
Ce <provider> utilise les classes .NET d'accs aux sources de donnes OleDb. Il est intgr en standard dans le fichier [providers.config] modle distribu avec SqlMap.
le fichier [properties.xml] dfinit le <provider> de la source OleDb ainsi que la chane de connexion de celle-ci :
<?xml version="1.0" encoding="utf-8" ?> <settings> <add key="provider" value="OleDb1.1" /> <add key="connectionString" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\data\serge\databases\access\articles\articles.mdb;"/> </settings>
Nous sommes prts pour les tests. Le serveur web [Cassini] garde sa configuration habituelle. Nous initialisons la table des articles de la source ACCESS de la faon suivante :
web3tier-part2
66/105
Les articles [pantalon] et [jupe] ont t achets et leurs stocks dcrments de la quantit achete. L'article [manteau] n'a pu tre achet car la quantit demande excdait la quantit en stock. Nous invitons le lecteur faire des tests complmentaires.
9 Conclusion
Nous terminons ici ce long article-tutoriel. Qu'avons-nous fait ? nous avons implment la couche [dao] d'une application web trois couches de quatre faons diffrentes : 1. en utilisant les classes d'accs .NET aux sources ODBC 2. en utilisant les classes d'accs .NET aux sources SQL Server 3. en utilisant les classes d'accs .NET aux sources OleDb 4. en utilisant les classes d'accs d'une tierce partie pour accder une base Firebird chaque fois, nous avons intgr la nouvelle couche [dao] l'application [webarticles] trois couches [web, domain, dao] sans recompilation aucune des couches [web, domain] nous avons enfin prsent l'outil [SqlMap] qui nous a permis de crer une couche [dao] capable de s'adapter diffrentes sources de donnes de faon transparente pour le code. C'est ainsi qu'avec cette nouvelle couche, nous avons pu utiliser successivement les sources de donnes des implmentations 1 3 prcdentes. Ceci a t fait de faon transparente l'aide de fichiers de configuration. nous avons montr la grande souplesse qu'apportaient les outils Spring et SqlMap aux applications web trois couches.
web3tier-part2
67/105
10 Annexes
10.1 O trouver le SGBD Firebird ?
Le site principal de Firebird est [http://firebird.sourceforge.net/]. La page de tlchargements offre les liens suivants (avril 2005) :
le SGBD pour Windows une bibliothque de classes pour les applications .NET qui permet d'accder au SGBD sans passer par un pilote ODBC. le pilote ODBC de Firebird
Faire l'installation de ces lments. Le SGBD est install dans un dossier dont le contenu est analogue au suivant :
web3tier-part2
68/105
fbguard.exe isql.exe
permet de lancer/arrter le SGBD client ligne permettant de grer des bases de donnes
On notera que par dfaut, l'administrateur du SGBD s'appelle [SYSDBA] et son mot de passe est [masterkey]. Des menus ont t installs dans [Dmarrer] :
L'option [Firebird Guardian] permet de lancer/arrter le SGBD. Aprs le lancement, l'icne du SGBD reste dans la barre des tches de windows :
Pour crer et exploiter des bases de donnes Firebird avec le client ligne [isql.exe], il est ncessaire de lire la documentation livre avec le produit dans le dossier [doc]. Une faon plus rapide de travailler avec Firebird est d'utiliser un client graphique. Un tel client est IB-Expert dcrit au paragraphe suivant.
web3tier-part2
69/105
On choisira la version libre [Personal Edition]. Une fois celle-ci tlcharge et installe, on dispose d'un dossier analogue au suivant :
L'excutable est [ibexpert.exe]. Un raccourci est normalement disponible dans le menu [Dmarrer] :
web3tier-part2
70/105
Server Database
peut tre [local] ou [remote]. Ici notre serveur est sur la mme machine que [IBExpert]. On choisit donc [local] utiliser le bouton de type [dossier] du combo pour dsigner le fichier de la base. Firebird met toute la base dans un unique fichier. C'est l'un de ses atouts. On transporte la base d'un poste l'autre par simple copie du fichier. Le suffixe [.gdb] est ajout automatiquement. SYSDBA est l'administrateur par dfaut des distributions actuelles de Firebird masterkey est le mot de passe de l'administrateur SYSDBA des distributions actuelles de Firebird le dialecte SQL utiliser si la case est coche, IBExpert prsentera un lien vers la base cre aprs avoir cr celle-ci
c'est que vous n'avez pas lanc Firebird. Lancez-le. On obtient une nouvelle fentre :
Server version
[IBExpert] est capable de grer diffrents SGBD drivs d'Interbase. Prendre la version de Firebird que vous avez installe
Une fois cette nouvelle fentre valide par [Register], on a le rsultat suivant :
web3tier-part2
71/105
Pour avoir accs la base cre, il suffit de double-cliquer sur son lien. IBExpert expose alors une arborescence donnant accs aux proprits de la base :
Crons une table. On clique droit sur [Tables] et on prend l'option [New Table]. On obtient la fentre de dfinition des proprits de la table :
Commenons par donner le nom [ARTICLES] la table en utilisant la zone de saisie [1] :
2
Utilisons la zone de saisie [2] pour dfinir une cl primaire [ID] :
Un champ est fait cl primaire par un double-clic sur la zone [PK] (Primary Key) du champ. Ajoutons des champs avec le bouton situ au-dessus de [3] :
web3tier-part2
72/105
Tant qu'on n'a pas " compil " notre dfinition, la table n'est pas cre. Utilisons le bouton [Compile] ci-dessus pour terminer la dfinition de la table. IBExpert prpare les requtes SQL de gnration de la table et demande confirmation :
De faon intressante, IBExpert affiche les requtes SQL qu'il a excutes. Cela permet un apprentissage la fois du langage SQL mais galement du dialecte SQL ventuellement propritaire utilis. Le bouton [Commit] permet de valider la transaction en cours, [Rollback] de l'annuler. Ici on l'accepte par [Commit]. Ceci fait, IBExpert ajoute la table cre, l'arborescence de notre base de donnes :
web3tier-part2
73/105
Le panneau [Constraints] nous permet d'ajouter de nouvelles contraintes d'intgrit la table. Ouvrons-le :
On retrouve la contrainte de cl primaire que nous avons cre. On peut ajouter d'autres contraintes :
des cls trangres [Foreign Keys] des contraintes d'intgrit de champs [Checks] des contraintes d'unicit de champs [Uniques]
Indiquons que :
les champs [ID, PRIX, STOCKACTUEL, STOKMINIMUM] doivent tre >0 le champ [NOM] doit tre non vide et unique
Ouvrons le panneau [Checks] et cliquons droit dans son espace de dfinition des contraintes pour ajouter une nouvelle contrainte :
On notera ci-dessus, que la contrainte [NOM<>''] utilise deux apostrophes et non des guillemets. Compilons ces contraintes avec le bouton [Compile] ci-dessus :
web3tier-part2
74/105
L encore, IBExpert fait preuve de pdagogie en indiquant les requtes SQL qu'il a excutes. Passons maintenant au panneau [Constraints/Uniques] pour indiquer que le nom doit tre unique :
Dfinissons la contrainte :
Celui-ci donne le code SQL de gnration de la table avec toutes ses contraintes. On peut sauvegarder ce code dans un script afin de le rejouer ultrieurement :
SET SQL DIALECT 3; SET NAMES NONE; CREATE TABLE ARTICLES ( ID INTEGER NOT NULL, NOM VARCHAR(20) NOT NULL, PRIX DOUBLE PRECISION NOT NULL, STOCKACTUEL INTEGER NOT NULL, STOCKMINIMUM INTEGER NOT NULL ); ALTER TABLE ARTICLES ADD CONSTRAINT CHK_ID check (ID>0); ALTER TABLE ARTICLES ADD CONSTRAINT CHK_PRIX check (PRIX>0); ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKACTUEL check (STOCKACTUEL>0); ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKMINIMUM check (STOCKMINIMUM>0); ALTER TABLE ARTICLES ADD CONSTRAINT CHK_NOM check (NOM<>''); ALTER TABLE ARTICLES ADD CONSTRAINT UNQ_NOM UNIQUE (NOM); ALTER TABLE ARTICLES ADD CONSTRAINT PK_ARTICLES PRIMARY KEY (ID);
Il est maintenant temps de mettre des donnes dans la table [ARTICLES]. Pour cela, utilisons son panneau [Data] :
web3tier-part2
75/105
Les donnes sont entres par un double-clic sur les champs de saisie de chaque ligne de la table. Une nouvelle ligne est ajoute avec le bouton [+], une ligne supprime avec le bouton [-]. Ces oprations se font dans une transaction qui est valide par le bouton [Commit Transaction]. Sans cette validation, les donnes seront perdues. IBExpert permet d'mettre des requtes SQL par l'option [Tools/SQL Editor] ou [F12]. On a alors accs un diteur de requtes SQL volu avec lequel on peut jouer des requtes. Elles sont mmorises et on peut ainsi revenir sur une requte dj joue. Voici un exemple :
On excute la requte SQL avec le bouton [Execute] ci-dessus. On obtient le rsultat suivant :
On arrtera l nos dmonstrations. Le couple IBExpert-Firebird s'avre excellent pour l'apprentissage des bases de donnes.
lancer l'outil [Dmarrer -> Paramtres -> Outil de configuration -> Outils d'administration -> Sources de donnes ODBC] :
ajoutons [Add] une nouvelle source de donnes systme (panneau [System DSN]) qu'on associera la base Firebird que nous avons cre dans le paragraphe prcdent : 76/105
web3tier-part2
il nous faut tout d'abord prciser le pilote ODBC utiliser. Ci-dessus, nous choisissons le pilote pour Firebird puis nous faisons [Terminer]. L'assistant du pilote ODBC de Firebird prend alors la main :
le nom DSN de la source ODBC - peut tre quelconque le nom de la BD Firebird exploiter - utiliser [Browse] pour dsigner le fichier .gbd correspondant identifiant utiliser pour se connecter la base le mot de passe associ cet identifiant
Le bouton [Test connection] permet de vrifier la validit des informations que nous avons donnes. Avant de l'utiliser, lancer le SGBD [Firebird] :
77/105
utilisons l'option [Donnes -> Donnes externes -> Crer une requte] ci-dessus. Nous obtenons la premire fentre d'un assistant de dfinition de la source de donnes. Le panneau [Bases de donnes] liste les sources ODBC actuellement dfinies sur la machine :
choisissons la source ODBC [odbc-firebird-articles] que nous venons de crer et passons l'tape suivante avec [OK] :
cette fentre liste les tables et colonnes disponibles dans la source ODBC. Nous prenons toute la table :
cette tape nous permet de filtrer les donnes. Ici nous ne filtrons rien et passons l'tape suivante : 78/105
web3tier-part2
cette tape nous permet de trier les donnes. Nous ne le faisons pas et passons l'tape suivante :
la dernire tape nous demande ce qu'on veut faire des donnes. Ici, nous les renvoyons vers Excel :
ici, Excel demande o on veut mettre les donnes rcupres. On les met dans la feuille active partir de la cellule A1. Les donnes sont alors rcupres dans la feuille Excel :
Il y a d'autres faons de tester la validit d'une source ODBC. On pourra par exemple utilliser la suite gratuite OpenOffice disponible l'url [http://www.openoffice.org]. Voici un exemple avec un texte OpenOffice :
une icne sur le ct gauche de la fentre d'OpenOffice donne accs aux sources de donnes. L'interface change alors pour introduire une zone de gestion des sources de donnes :
web3tier-part2
79/105
une source de donnes est prdfinie, la source [Bibliography]. Un clic droit sur la zone des sources de donnes nous permet d'en crer une nouvelle avec l'option [Grer les sources de donnes] :
un assistant [Gestion des sources de donnes] permet de crer des sources de donnes. Un clic droit sur la zone des sources de donnes nous permet d'en crer une nouvelle avec l'option [Nouvelle source de donnes] :
un nom quelconque. Ici on a repris le nom de la source ODBC OpenOffice sait grer diffrents types de BD via JDBC, ODBC ou directement (MySQL, Dbase, ...). Pour notre exemple, il faut choisir ODBC le bouton droite du champ de saisie nous donne accs la liste des sources ODBC de la machine. Nous choisissons la source [odbc-firebird-articles]
nous passons au panneau [ODBC] pour y dfinir l'utilisateur sous l'identit duquel se fera la connexion :
on passe au panneau [Tables]. Le mot de passe est demand. Ici c'est [masterkey] :
on fait [OK]. La liste des tables de la source ODBC est alors prsente :
on peut dfinir les tables qui seront prsentes au document [OpenOffice]. Ici nous choisissons la table [ARTICLES] et nous faisons [OK]. La dfinition de la source de donnes est termine. Elle apparat alors dans la liste des sources de donnes du document actif :
web3tier-part2
80/105
on peut avec la souris faire glisser la table [ARTICLES] ci-dessus dans le texte [OpenOffice] :
cliquer droit sur [Connexion de donnes] et prendre l'option [Ajouter une connexion] :
dans le panneau [Provider], indiquer qu'on veut utiliser une source ODBC (cf ci-dessus), puis passer au panneau [Connection] :
web3tier-part2
81/105
Use data source name [demo-odbc-firebird] User name [SYSDBA] Password [masterkey]
choisir la source ODBC dans le combo. Celle qui vient d'tre cre doit apparatre. Au besoin, utiliser [Refresh] pour rafrachir la liste des sources ODBC. identifiant utiliser pour se connecter la base le mot de passe associ cet identifiant
valider l'assistant par [OK]. La source de donnes apparat alors dans la fentre [Explorateur de serveurs] de Visual Studio :
web3tier-part2
82/105
si nous cliquons droit sur le lien [Firebird Server D:\temp\... ] et prenons l'option [Proprits], nous avons accs aux proprits de la connexion :
la chane de connexion [ConnectString] est une proprit intressante connatre car le code .Net en a besoin pour ouvrir une connexion la base. Ici cette chane de connexion est :
Beaucoup d'lments de cette chane de connexion ont des valeurs par dfaut. On pourra se contenter de la chane de connexion suivante :
"DSN=demo-odbc-firebird;UID=SYSDBA;PASSWORD=masterkey"
web3tier-part2
83/105
Tlcharger le fichier d'installation puis installer le SGBD en double-cliquant sur l'excutable tlcharg. Une fentre demande le dossier d'installation. Le titre est trompeur. Il s'agit d'un dossier temporaire qui pourra tre supprim ensuite :
On lira attentivement le fichier [ReadmeMSDE2000A.htm]. Le programme d'installation est [setup.exe] ci-dessus. Il se lance en ligne de commande afin qu'on puisse lui passer des paramtres. Les principaux sont les suivants : Paramtre Description SAPWD="MotDePasseRenforc" Spcifie un mot de passe renforc assigner au login administrateur sa. INSTANCENAME="NomInstance" Dfinit le nom de l'instance. Si INSTANCENAME n'est pas spcifi, le programme d'installation installe une instance par dfaut. D'autres paramtres souvent utiliss pour personnaliser une installation sont : Paramtre Description DISABLENETWORKPROTOCOLS=n Spcifie si l'instance acceptera les connexions rseau partir d'applications excutes sur d'autres ordinateurs. Par dfaut, ou si vous spcifiez DISABLENTWORKPROTOCOL=1, le programme d'installation configure l'instance pour qu'elle refuse les connexions rseau. Spcifiez DISABLENETWORKPROTOCOLS=0 pour activer les connexions rseau. SECURITYMODE=SQL Spcifie que l'instance doit tre installe en mode mixte, c'est--dire que l'instance prend en charge l'authentification Windows et l'authentification SQL pour les connexions DATADIR="chemin_dossier_donnes" Spcifie le dossier dans lequel le programme d'installation installe les bases de donnes systme, les journaux d'erreurs et les scripts d'installation. La valeur spcifie pour chemin_dossier_donnes doit se terminer par une barre oblique inverse (\). Pour une instance par dfaut, le programme d'installation ajoute MSSQL\ la valeur spcifie. Pour une instance nomme, le programme d'installation ajoute MSSQL$NomInstance\, o NomInstance est la valeur spcifie grce au paramtre INSTANCENAME. Le programme d'installation cre trois dossiers l'emplacement spcifi : un dossier Data, un dossier Log et un dossier Script. TARGETDIR="chemin_dossier_excutables" Spcifie le dossier dans lequel le programme d'installation installe les fichiers excutables de MSDE 2000. La valeur spcifie pour chemin_dossier_excutables doit se terminer par une barre oblique inverse (\). Pour une instance par dfaut, le programme d'installation ajoute MSSQL\Binn la valeur spcifie. Pour une instance nomme, le programme d'installation ajoute MSSQL$NomInstance\Binn, o NomInstance est la valeur spcifie grce au paramtre INSTANCENAME.
web3tier-part2
84/105
Aprs avoir lu les recommandations d'installation ci-dessus, nous nous plaons dans le dossier o les fichiers d'installation ont t extraits et nous mettons la commande DOS suivante (utilisation du SGBD sans rseau) :
dos>setup INSTANCENAME="MSDE140405" SECURITYMODE=SQL SAPWD="azerty"
INSTANCENAME="MSDE140405" - ce sera le nom de notre instance MSDE. On peut en installer plusieurs. SECURITYMODE=SQL - le SGBD fonctionnera en mode d'authentification mixte. Ainsi pourra-t-on se connecter MSDE de deux faons : avec un compte administrateur windows avec un compte MSDE - un login et mot de passe sont alors demands. Ce sera le mode utiliser dans un programme qui se connecte une base du SGBD. SAPWD="azerty" - ce sera le mot de passe de l'utilisateur sa du SGBD. L'utilisateur [sa] a les droits d'administration sur le SGBD.
Le programme d'installation est minimaliste et se termine sans rien dire... On peut cependant voir que le SGBD a t install via l'option [Menu Dmarrer -> Panneau de configuration -> Ajouter et supprimer des programmes] :
Dans le dossier [LOG] du dossier d'installation, on trouve le fichier de logs de la phase d'intallation du SGBD. On y trouve une information importante : le nom de l'instance MSDE :
2005-04-14 08:14:29.37 spid4 Le nom du serveur est PORTABLE1_TAHE\MSDE140405.
Il est important de connatre ce nom car tous les clients du SGBD en auront besoin. En l'absence de ces logs, on peut retrouver le nom d'un serveur MSDE qui est [machine_windows\nom_instance_MSDE]. Le nom de la machine est disponible plusieurs endroits. Par exemple :
cliquez droit sur [poste de travail] sur le bureau, prenez l'option [proprits], puis le panneau [Nom de l'ordinateur] :
On ne sait toujours pas comment lancer le serveur MSDE. Un raccourci a normalement t plac dans [Dmarrer/Dmarrage].
85/105
MSSQL$MSDE140405 est le dossier de l'intance MSDE que nous venons d'installer. MSSQL est le dossier d'une prcdente intance MSDE. Parce qu'elle n'a pas de nom, on l'appelle l'instance par dfaut. le dossier [80] est un dossier commun aux diffrentes instances de MSDE installes. La cible [sqlmangr.exe] du raccourci qui lance une instance de MSDE est dans le dossier [ 80\Tools\Binn].
Lanons MSDE via le raccourci de [Dmarrer -> Programmes Double-cliquons sur cette icne : -> Dmarrage]. Il ne se passe quasiment rien si ce n'est qu'une icne s'est installe dans la barre d'tat :
Le serveur MSDE propos ici est le serveur par dfaut Si tout se passe bien, l'instance [MSDE140405] doit tre lance : [PORTABLE1_TAHE] prsent sur la machine. Rappelons que le serveur MSDE que nous avons install s'appelle [PORTABLE1_TAHE\MSDE140405]. Nous changeons le nom du serveur dans le champ appropri :
On peut faire une premire vrification. Dans le mme dossier que celui o se trouve [sqlmangr.exe], on trouve un client console [osql.exe] qui permet de se connecter un serveur MSDE et d'mettre des commandes SQL. Nous avons lors de l'installation attribu le mot de passe [azerty] l'administrateur [sa] de notre serveur MSDE. Grce au client console, nous allons nous connecter sur le serveur nouvellement install. Si on excute la commande [osql -?] la liste des paramtres possibles est affiche :
C:\Program Files\Microsoft SQL Server\80\Tools\Binn>osql -? utilisation : osql [-U ID de connexion] [-P mot de passe] [-S serveur] [-H nom de l'hte] [-E connexion approuve] [-d utiliser le nom de la base de donnes] [-l limite du temps de connexion] [-t limite du temps de requte] [-h en-ttes] [-s sparateur de colonnes] web3tier-part2
86/105
[-w [-a [-e [-I [-L [-c [-q [-Q [-n [-m [-r [-V [-i [-o [-p
largeur de colonne] taille du paquet] entre d'cho] Activer les identificateurs marqus] liste des serveurs] fin de cmd] [-D nom ODBC DSN] "requte cmdline"] "requte cmdline" et quitter] supprimer la numrotation] niveau d'erreur] msgs vers stderr] severitylevel] fichier d'entre] fichier de sortie] imprimer les statistiques] [-b abandon du lot d'instruction aprs erreur]
[-X[1] dsactive les commandes [et quitte avec un avertissement]] [-O utiliser le comportement Old ISQL dsactive les lments suivants] <EOF> traitement par lot d'instructions Mise l'chelle automatique de la largeur de la console Messages larges niveau d'erreur par dfaut de -1 au lieu de 1 [-? description de la syntaxe]
Lanons le serveur [MSDE140405] comme indiqu plus haut, puis dans un fentre dos, utilisons [osql] pour nous connecter au serveur [portable1_tahe\msde140405] sous l'identit [sa, azerty] :
C:\Program Files\Microsoft SQL Server\80\Tools\Binn>OSQL.EXE -U sa -S portable1_tahe\msde140405 -P azerty 1>
Le prompt [1>] indique que [osql] attend une commande. Nous sommes bien connects. Pour utiliser correctement [osql], il faut consulter la documentation de MSDE. Il en existe en diffrents formats (pdf, htmlhelp, ...). Cette documentation est trs volumineuse. On prfrera en gnral utiliser un client graphique pour travailler avec une base MSDE. C'est ce qui est propos un peu plus loin. Pour quitter [osql], on utilise la commande [exit] :
1> exit
Nous allons voir maintenant comment crer des bases dans le serveur MSDE nouvellement install. Auparavant, nous prsentons rapidement un outil [MSDE Manager] permettant de modifier le mode d'authentification d'un serveur MSDE. En effet, si on installe un tel serveur en prenant les options d'installation par dfaut, le mode d'authentification du serveur est de type [authentification windows]. Ce type d'authentification autorise uniquement des utlisateurs identifis sur la machine windows (ventuellement via un domaine). Pour un programme VB.NET qui veut se connecter une base pour en exploiter le contenu, ce mode s'avre peu pratique. C'est pire pour les applications Java qui accdent au SGBD via un pilote JDBC. On prfrera alors l'authentification mixte qui en plus de l'autentification prcdente accepte des couples (login, mot de passe) dclars dans le SGBD. L'outil [MSDE Manager] permet de faire cette opration.
La version d'essai a une courte dure de vie. Cela convient car nous ne l'utiliserons que pour une unique action bien prcise. Nous tlchargeons et installons le produit. Un raccourci est plac sur le bureau. Nous l'utilisons pour lancer MSDE Manager. Passes les premires fentres, nous arrivons celle-ci :
web3tier-part2
87/105
lancez le serveur MSDE140405 vous devez tre connect sur la machine windows en tant qu'administrateur cliquez droit sur le lien [SQL Server Group] et prenez l'option [New SQL Server Registration] :
portable1_tahe\msde140405 - nom de l'instance MSDE laquelle vous voulez vous connecter Windows authentification - ce mode est toujours disponible et permet un administrateur de la machine windows de se connecter au serveur MSDE slectionner l'unique groupe de serveurs prsent [SQL Server Group]
Une fois [OK] cliqu, l'arborescence des proprits du serveur MSDE140405 est affiche :
web3tier-part2
88/105
Nous pourrions commencer crer des bases. Nous n'allons pas le faire car nous utiliserons un autre produit, clne du produit IBExpert dj tudi. Nous allons simplement changer le mode d'authentification de MSDE. Cliquons droit sur le serveur MSDE140405 ci-dessus et prenons l'option [Design] :
Le panneau [General] donne des informations sur le serveur MSDE auquel on est connect. La page [Security] est celle qui nous intresse :
Il faut s'assurer ici, que le mode d'authentification de MSDE est bien [SQL Server and Windows]. Ainsi pourra-t-on se connecter MSDE de deux faons :
avec un compte administrateur windows - c'est ce qui a t fait ici avec un compte MSDE - un login et mot de passe sont alors demands. Ce sera le mode utiliser dans un programme qui se connecte une base du SGBD.
Nous validons ce choix et nous quittons MSDE Manager. Nous n'en aurons plus besoin. Pour crer des bases MSDE, nous allons utiliser un autre outil : EMS MS SQL Manager.
web3tier-part2
89/105
Le site offre des gestionnaires d'administration pour de nombreux SGBD. Suivre le lien [MS SQL Manager] :
Ci-dessus, nous choisissons la version allge du produit. Le tlcharger et l'installer. On dispose d'un dossier analogue au suivant :
L'excutable est [MsManager.exe]. Un raccourci est normalement disponible dans le menu [Dmarrer] :
web3tier-part2
90/105
Commenons par enregistrer le serveur MSDE sur lequel on veut travailler avec l'option [Database/Register Host] : tape 1 : tape 2 :
Commentaires :
tape 1 - comme il a t dit, MSDE accepte deux modes d'authentification : windows et SQL Server. En mode [windows], ce sont les comptes de la machine windows qui sont utiliss. En mode [SQL Server], ce sont les comptes du SGBD qui sont utiliss. [SQL Server] peut travailler en mode [Windows] ou en mode mixte [Windows, SQL Server]. Le mode d'authentification [Windows] existe toujours. Le mode d'authentification mixte n'est lui pas toujours actif. Nous avons vu comment l'activer avec MSDE Manager. Ci-dessus, la connexion s'est faite avec un compte administrateur. tape 2 - l'authentification russie, les bases par dfaut de MSDE sont proposes. Ci-dessus, elles ont t toutes slectionnes.
tapes 3-4 :
Commentaires :
tape 3 : des options d'administration des bases choisies peuvent tre slectionnes. Ici, les options proposes par dfaut ont t conserves. tape 4 : nous enregistrons le serveur MSDE avec ele bouton [Register]
web3tier-part2
91/105
Utilisons l'option [Database/Create Database] pour crer une base de donnes : tape 1 : tape 2 : [Create]
Host : le nom du serveur MSDE sur lequel on veut crer la base. Ici [portable1_tahe\msde140405]
tape 2 : Lorsqu'apparat cette page d'informations, la base [dbarticles] a t cre. On peut s'en assurer avec le bouton [Test Connect]. Dans le champ [Database alias] on peut mettre ce que l'on veut. Ici on a indiqu :
le nom de la base le nom du serveur MSDE sur lequel elle se trouve l'utilisateur [admarticles] qui sera propritaire de cette base et son mot de passe [mdparticles]. Ce utilisateur n'a pas encore t cr mais le sera prochainement.
tape 3 :
avec le bouton [Register] nous enregistrons la nouvelle base dans [MS SQL Server ]. Aprs l'enregistrement, la base [admarticles] est prsente dans la liste des bases. Un double-clic dessus fait afficher l'arborescence de ses proprits.
web3tier-part2
92/105
on constate que deux logins sont dj dfinis : [BUILTIN\Administrateurs] : ce login utilise une authentification windows. Il reprsente les administrateurs de la machine windows sur laquelle se trouve le serveur MSDE sa : ce login utilise une authentification SQL. C'est par dfaut l'administrateur du serveur MSDE. On rappelle qu'ici, par paramtrage l'installation du SGBD MSDE, son mot de passe est [azerty].
une feuille de saisies apparat o nous dfinissons les caractristiques du nouveau login :
Login Name : admarticles Password : mdparticles une fois le bouton [OK] press, MS Manager nous prsente les requtes SQL qu'l va excuter :
Le langage SQL prsent ci-dessus est Transact-SQL, le langage SQL de MSDE. Nous demandons l'excution de ce code par [OK]
dans la fentre de proprits de la base [dbarticles], cliquons droit sur [users] afin de crer un utilisateur avec des droits sur la base [dbarticles] :
web3tier-part2
93/105
dans le combo [Login] on a la liste des logins existants. On choisit le login [admarticles]. dans [Name] on indique un nom d'utilisateur. Plusieurs utilisateurs peuvent tre associs au mme login. Aussi dans MSDE, la cration d'un utilisateur passe-t-elle d'abord par la cration d'un login. Le panneau [User] devient le suivant :
passons maintenant au panneau [Member Of] qui va nous permettre de dfinir les droits de notre utilisateur :
je ne suis pas un utilisateur habituel de MSDE et j'ignore la signification exacte de chacun des rles proposs dans la fentre de gauche. Le rle [db_owner] est tentant (owner=propritaire). On le choisit donc pour notre utilisateur [admarticles] :
nous validons nos choix par le bouton [Compile] ci-dessus. Les requtes SQL prsentes l'excution sont les suivantes :
web3tier-part2
94/105
nous les compilons par [OK]. Nous avons maintenant un utilisateur de la base [dbarticles] :
Crons maintenant une table. On clique droit sur [Tables] et on prend l'option [New Table]. On obtient la fentre de dfinition des proprits de la table :
Commenons par donner le nom [ARTICLES] la table en utilisant la zone de saisie [Table Name]. Passons ensuite au panneau [Fields] :
Tant qu'on n'a pas " compil " notre dfinition, la table n'est pas cre. Utilisons le bouton [Compile] ci-dessus pour terminer la dfinition de la table. [MS SQL Manager] prpare les requtes SQL de gnration de la table et demande confirmation :
web3tier-part2
95/105
De faon intressante, [MS SQL Manager] affiche les requtes SQL qu'il a excutes. Cela permet un apprentissage la fois du langage Transact-SQL. Le bouton [Commit] permet de valider la transaction en cours, [Rollback] de l'annuler. Ici on l'accepte par [Commit]. Ceci fait, [MS SQL Manager] ajoute la table cre l'arborescence de notre base de donnes :
Le panneau [Checks] nous permet d'ajouter de nouvelles contraintes d'intgrit la table. Pour la table [ARTICLES] nous allons crer les contraintes suivantes :
les champs [ID, PRIX, STOCKACTUEL, STOKMINIMUM] doivent tre >=0 le champ [NOM] doit tre non vide
Dans le panneau [Checks], cliquons droit sur sa zone vierge pour ajouter une nouvelle contrainte [New check] :
Name : nom de la contrainte Table : table sur laquelle s'exerce la contrainte Dfinition : expression de la contrainte La contrainte est compile par le bouton [Compile] ci-dessus.
on les valide avec le bouton [Commit] (non reprsent).Si on revient sur le panneau [Checks] de la table [ARTICLES], la nouvelle contrainte apparat : 96/105
web3tier-part2
nous dfinissons de mme les autres contraintes pour obtenir finalement la liste suivante :
Celui-ci donne le code Transact-SQL de gnration de la table avec toutes ses contraintes. On peut sauvegarder ce code dans un script afin de le rejouer ultrieurement :
CREATE TABLE [ARTICLES] ( [id] int NOT NULL, [nom] varchar(20) COLLATE French_CI_AS NOT NULL, [prix] float(53) NOT NULL, [stockactuel] int NOT NULL, [stockminimum] int NOT NULL, CONSTRAINT [ARTICLES_uq] UNIQUE ([nom]), PRIMARY KEY ([id]), CONSTRAINT [ARTICLES_ck_id] CHECK ([id] > 0), CONSTRAINT [ARTICLES_ck_nom] CHECK ([nom] <> ''), CONSTRAINT [ARTICLES_ck_prix] CHECK ([prix] >= 0), CONSTRAINT [ARTICLES_ck_stockactuel] CHECK ([stockactuel] >= 0), CONSTRAINT [ARTICLES_ck_stockminimum] CHECK ([stockminimum] >= 0) ) ON [PRIMARY] GO
Il est maintenant temps de mettre quelques donnes dans la table [ARTICLES]. Pour cela, utilisons son panneau [Data] :
Le bouton [+] permet d'ajouter une ligne, le bouton [-] d'en supprimer. Les donnes sont entres par simple saisie sur les champs de saisie de chaque ligne de la table. Une ligne est valide par le bouton [Post Edit] ci-dessous :
web3tier-part2
97/105
[MS SQL Manager] permet d'mettre des requtes SQL par l'option [Tools/Show SQL Editor] ou [F12]. On a alors accs un diteur de requtes SQL volu avec lequel on peut jouer des requtes. Elles sont mmorises et on peut ainsi revenir sur une requte dj joue. Voici un exemple :
On excute la requte SQL avec le bouton [Execute] ci-dessus. On obtient le rsultat suivant :
On arrtera l nos dmonstrations. Le couple [MS SQL Manager - MSDE], l'instar du couple [IBExpert - Firebird], s'avre lui aussi excellent pour l'apprentissage des bases de donnes.
lancer l'outil [Dmarrer -> Paramtres -> Outil de configuration -> Outils d'administration -> Sources de donnes ODBC] :
ajoutons [Add] une nouvelle source de donnes systme (panneau [System DSN]) qu'on associera la base MSDE que nous avons cre dans le paragraphe prcdent :
web3tier-part2
98/105
il nous faut tout d'abord prciser le pilote ODBC utiliser. Ci-dessus, nous choisissons le pilote pour [SQL Server] puis nous faisons [Terminer]. L'assistant du pilote ODBC de [SQL Server] prend alors la main :
nous remplissons les diffrents champs : le nom de la source ODBC - peut tre quelconque peut tre quelconque nom du serveur MSDE dtenant les donnes de la source ODBC
web3tier-part2
99/105
on indique qu'on se connectera la source de donnes ODBC avec un nom d'utilisateur dclar dans le serveur MSDE ID de connexion [admarticles] login utilisateur Mot de passe [mdparticles] mot de passe utilisateur
on remarquera que nous utilisons pour la premire fois l'utilisateur (admarticles, mdparticles) cr dans un paragraphe prcdent. De nouveau nous faisons [Suivant] pour obtenir la nouvelle feuille suivante :
nous remplissons les diffrents champs : nous slectionnons la base [dbarticles] comme base par dfaut pour l'utilisateur [admarticles]
nous acceptons les valeurs par dfaut et faisons [Terminer]. Un rsum des caractristiques de la source ODBC qui va tre cre est donn :
web3tier-part2
100/105
le bouton [Tester la source de donnes] nous donne une chance de vrifier la validit de nos informations. Vrifiez que MSDE est lanc puis testez la connexion :
nous sommes maintenant certains que le couple [admarticles, mdparticles] est reconnu.
Pour des tests complmentaires, le lecteur poura suivre la procdure explique au paragraphe 10.3.3, page 77.
cliquer droit sur [Connexion de donnes] et prendre l'option [Ajouter une connexion] :
dans le panneau [Provider], indiquer qu'on veut utiliser une source SQL Server, puis passer au panneau [Connection]. Noter qu'ici on ne passe pas par un pilote ODBC.
web3tier-part2
101/105
Nom de serveur [portable1_tahe\msde140405] Nom d'utilisateur [admarticles] Mot de passe [mdparticles] base de donnes [dbarticles]
nom du serveur MSDE auquel on se connecte identifiant utiliser pour se connecter la base le mot de passe associ cet identifiant la base de donnes avec laquelle on veut travailler
valider l'assistant par [OK]. Assez curieusement, une nouvelle fentre demande les caractristiques de la connexion :
on les redonne et on fait [OK]. La source de donnes apparat alors dans la fentre [Explorateur de serveurs] de Visual Studio :
web3tier-part2
102/105
si nous cliquons droit sur le lien [portable1_tahe\msde140405.dbarticles.admarticles] du panneau [Explorateur de serveurs] et prenons l'option [Proprits], nous avons accs aux proprits de la connexion :
la chane de connexion [ConnectString] est une proprit intressante connatre car le code .Net en a besoin pour ouvrir une connexion la base. Ici cette chane de connexion est :
Provider=SQLOLEDB.1;Persist Security Info=False;User ID=admarticles;Initial Catalog=dbarticles;Data Source=portable1_tahe\msde140405;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=PORTABLE1_TAHE;Use Encryption for Data=False;Tag with column collation when possible=False
Beaucoup d'lments de cette chane de connexion ont des valeurs par dfaut. On pourra se contenter de la chane de connexion suivante :
"Provider=SQLOLEDB.1;Persist Security Info=False;User ID=admarticles;Initial Catalog=dbarticles;Data Source=portable1_tahe\msde140405;PASSWORD=mdparticles"
web3tier-part2
103/105
104/105
7.3GNRATION DE L'ASSEMBLY DE LA COUCHE [DAO]........................................................................................................................ 46 7.4TESTS NUNIT DE LA COUCHE [DAO]............................................................................................................................................... 47 7.4.1LA CLASSE DE TEST NUNIT..........................................................................................................................................................47 7.4.2TESTS.......................................................................................................................................................................................48 7.5INTGRATION DE LA NOUVELLE COUCHE [DAO] DANS L'APPLICATION [WEBARTICLES]......................................................................... 48 8LA CLASSE D'IMPLMENTATION [ARTICLESDAOSQLMAP].................................................................................. 50 8.1LE PRODUIT IBATIS SQLMAP........................................................................................................................................................50 8.2O TROUVER IBATIS SQLMAP ?............................................................................................................................................... 51 8.3LES FICHIERS DE CONFIGURATION D'IBATIS SQLMAP...................................................................................................................... 51 8.4LES FICHIERS DE CONFIGURATION DU PROJET [DAO-SQLMAP]........................................................................................................... 52 8.4.1PROVIDERS.CONFIG...................................................................................................................................................................... 52 8.4.2SQLMAP.CONFIG.......................................................................................................................................................................... 53 8.4.3ARTICLES.XML............................................................................................................................................................................ 53 8.4.4EMPLACEMENT DES FICHIERS DE CONFIGURATION............................................................................................................................. 55 8.5L'API DE SQLMAP.....................................................................................................................................................................55 8.6LE CODE DE LA CLASSE [ARTICLESDAOSQLMAP].......................................................................................................................... 56 8.7GNRATION DE L'ASSEMBLY DE LA COUCHE [DAO]........................................................................................................................ 58 8.8TESTS NUNIT DE LA COUCHE [DAO]............................................................................................................................................... 58 8.8.1LA CLASSE DE TEST NUNIT..........................................................................................................................................................58 8.8.2TESTS.......................................................................................................................................................................................59 8.9INTGRATION DE LA NOUVELLE COUCHE [DAO] DANS L'APPLICATION [WEBARTICLES]......................................................................... 62 8.9.1SOURCE DE DONNES ODBC....................................................................................................................................................... 62 8.9.2SOURCE DE DONNES MSDE....................................................................................................................................................... 64 8.9.3SOURCE DE DONNES OLEDB....................................................................................................................................................... 66 9CONCLUSION.......................................................................................................................................................................... 67 10ANNEXES................................................................................................................................................................................ 68 10.1O TROUVER LE SGBD FIREBIRD ?........................................................................................................................................... 68 10.2O TROUVER IB-EXPERT ?........................................................................................................................................................69 10.3INSTALLER ET UTILISER UN PILOTE ODBC POUR [FIREBIRD]........................................................................................................ 76 10.3.1INSTALLER LE PILOTE................................................................................................................................................................ 76 10.3.2CRER UNE SOURCE ODBC...................................................................................................................................................... 76 10.3.3TESTER LA SOURCE ODBC.......................................................................................................................................................77 10.4CHANE DE CONNEXION D'UNE SOURCE ODBC FIREBIRD..............................................................................................................81 10.5O TROUVER LE SGBD MSDE ?............................................................................................................................................. 83 10.6O TROUVER MSDE MANAGER ?............................................................................................................................................. 87 10.7O TROUVER EMS MS SQL MANAGER ?................................................................................................................................ 89 10.8CRER UNE SOURCE ODBC [MSDE]........................................................................................................................................98 10.9CHANE DE CONNEXION UNE BASE MSDE...............................................................................................................................101
web3tier-part2
105/105