Préface
Le templet JDBC de Spring est une encapsulation de base que Spring utilise pour JDBC. Il aide principalement les programmeurs à gérer les connexions de la base de données, et le reste des méthodes d'utilisation ne sont pas une grande différence par rapport à l'utilisation directement de JDBC.
Exigences commerciales
Tout le monde connaît l'utilisation de JDBC. Il s'agit principalement de démontrer les étapes pour utiliser le templet JDBC Spring à Springboot, nous avons donc conçu une exigence simple. Une opération de caillé d'un objet utilisateur. Un objet a deux propriétés, l'une est id et l'autre est nom. Stocké dans la table AUTH_USER dans MySQL.
Créer de nouveaux projets et ajouter des dépendances
Créez un projet Springboot vide dans Intellij Idea. Référence des étapes spécifiques
Le tutoriel graphique d'Intellij Idea pour la création de projets Spring-Boot. Selon les exigences de cet exemple, nous devons ajouter les trois dépendances suivantes
<Dependance> <ProupId> org.springframework.boot </proupId> <ArtifActid> printemp-boot-starter-web </ artifactid> </pedigency> <dependency> <proupId> org.springframework.boot </proupatid> <Artifactid> Spring-Boot-Starter-Jdbc </letifactid> </ dépendance> <GroupId> MySQL </rom grouped> <ArtefactId> MySQL-Connector-Java </ ArfactId> <DERNÉRATEUR> 6.0.6 </DERNIERSE> </DENDENCED>
Parce que nous voulons publier le service HTTP REST, nous ajoutons la dépendance de Spring-Boot-Starter-Web. Ici, nous voulons utiliser la méthode JDBC TEMPET pour accéder à la base de données, nous ajoutons donc la dépendance Spring-Boot-Starter-JDBC pour accéder à la base de données MySQL, nous ajoutons donc la dernière version du pilote JDBC de MySQL.
Préparez l'environnement de la base de données
Supposons que MySQL 5.7 est déjà installé sur le système d'exploitation Linux. Les opérations suivantes sont exécutées sur la ligne de commande du système d'exploitation, connectée au client de la ligne de commande de MySQL via l'utilisateur racine.
Créer une base de données et un tableau
Créer une base de données Springboot_jdbc; Créer une table auth_user (UUID bigInt pas null, name varchar (32), clé primaire (UUID)) Charset par défaut = utf8mb4;
Définir les autorisations utilisateur
Accorder tous les privilèges sur Springboot_Jdbc. * À 'Springboot' @ '%' identifié par 'Springboot'; Flush Privileges;
Configurer la source de données (pool de connexion)
La source de données de Springboot est automatiquement configurée. Dans Springboot 2.0, plusieurs configurations de source de données sont disponibles, et ils choisissent la source de données à utiliser réellement dans le dernier ordre de Hikaricp -> Tomcat Pooring -> Commons DBCP2.
Lorsque le projet ajoute la dépendance de Spring-Boot-Starter-JDBC, la dépendance de la source de données HIKARICP est déjà incluse, de sorte que la source de données de pool de connexion HikaricP est automatiquement configurée ici.
Ajouter la configuration suivante dans Appplications.
# Configuration de la source de données générale printemps.datasource.driver-class-name = com.mysql.cj.jdbc.driverspring.datasource.url = jdbc: mysql: //10.110.2.5: 3306 / spri ng-boot-jdbc? charset = utf8mb4 & usessl = falsspring.datasource.username = springbootspring.datasource.password = springboot # Hikari Data Source spécifique Configuration Spring.Datasource.hikari.Maximum-Pool-Size = 20Spring.Datasource.hikari.minimum-idle = 5
Parmi eux, la plupart des configurations de la source de données Hikari sont présentées dans la figure ci-dessous. Vous pouvez vérifier la signification de chaque configuration
Développement de programmes
Entité de la base de données des utilisateurs
Selon les exigences, l'entité de données utilisateur correspondante a deux attributs, l'un est ID et l'autre est le nom. Ceci est un objet pojo pur.
package com.yanggaochao.springboot.learn.springbootjdbClearn.domain.dao; / ** * Objet de l'entité utilisateur * * @author yang gaochao * @Since 2018-03-09 * / public class userdo {private long id; nom de chaîne privé; public long getID () {return id; } public void setid (long id) {this.id = id; } public String getName () {Nom de retour; } public void setName (string name) {this.name = name; }} Objet de retour de repos général de repos général
Habituellement, dans l'interface HTTP REST, nous voulons non seulement renvoyer directement le contenu de l'objet métier, mais également renvoyer des informations courantes, telles que le résultat de l'appel d'interface, le message texte personnalisé renvoyé lorsque l'appel échoue, etc., nous devons ensuite établir deux objets de retour de repos communs, en plus de renvoyer des résultats commerciaux d'interface communs et des messages texte, on comprend un contenu commercial séparé et un contenu contenant une collection qui contient un contenu commercial multiple. La définition spécifique est la suivante
Retour objet pour un contenu commercial séparé
Package com.yanggaochao.springboot.learn.springbootjdbClearn.domain.bo; / ** * Retour objet Single Retour * * @author Yang Gaochao * @Since 2018-03-09 * / public Class RestitemResult <T> {private String Result Result; Message de chaîne privé; Article T privé; public String getResult () {return result; } public void setResult (string result) {this.result = result; } public String getMessage () {return message; } public void setMessage (Message de chaîne) {this.Message = message; } public t getItem () {return item; } public void setItem (t item) {this.item = item; }} Collection de contenu commercial Renvoie un objet
Package com.yanggaochao.springboot.learn.springbootjdbClearn.domain.bo; Importer java.util.collection; / ** * L'objet de collection renvoie le résultat * * @author yang gaochao * @Since 2018-03-09 * / public class restCollectionResult <T> {private String result; Message de chaîne privé; Collection privée <T> Articles; public String getResult () {return result; } public void setResult (string result) {this.result = result; } public String getMessage () {return message; } public void setMessage (Message de chaîne) {this.Message = message; } Collection publique <T> getItems () {return items; } public void setItems (collection <T> items) {this.items = items; }} Développement de la couche de persistance des données
Définition de l'interface de la couche de persistance des données utilisateur
Package com.yanggaochao.springboot.learn.springbootjdbclearn.dao; Import com.yanggaochao.springboot.learn.springbootjdbclacearn.domain.dao.userdo; Importer java.util.list; / ** * interface de calculs utilisateur * * @author yang yang * @SinceCha 2018-03-09 * / Interface publique UserDao {/ ** * Enregistrez un nouvel utilisateur dans la base de données * * @param utilisateur pour enregistrer * @return si le gain musculaire est réussi * / boolean add (userdo user); / ** * Mettez à jour un utilisateur dans la base de données * * @param utilisateur utilisateur utilisateur pour mettre à jour * @return si la mise à jour est réussie * / booléen Update (utilisateur userDo); / ** * Supprimer un utilisateur spécifié * * @param id L'identité de l'utilisateur pour supprimer * @return si la suppression est réussie * / booléen delete (id long); / ** * Requête exacte d'un utilisateur spécifié * * @param id L'identité de l'utilisateur pour interroger * @return si elle peut être interrogée, renvoyer les informations de l'utilisateur, sinon renvoyez NULL * / userDo l'emplacement (ID long); / ** * Interrogez l'utilisateur par nom * * @param nom le nom à être flou * @return liste d'utilisateurs à remettre en question * / list <serdoD> matchname (nom de chaîne);} Implémentation de la couche de persistance des données utilisateur
package com.yanggaochao.springboot.learn.springbootjdbclearn.dao.mpl com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.userdo; import org.springframework.beans.factory.annotation.jdbcttemplate; org.springframework.jdbc.support.rowset.sqlrowset; import org.springframework.sterreotype.repository; importer java.util.arraylist; import java.util.list; / ** * user Object Database Access Class * * @author yang gaochao * @Since 2018-03-09 * / @ RepositoryPublic Class UserDaoJDBCTemplemplt implémente userDao {private final jdbctemplate jdbctemplate; @Autowired UserDaoJDBCTempleTImpl (JDBCTemplate JDBCTemplate) {this.jdbcTemplate = JDBCTemplate; } @Override public boolean add (userdo user) {String sql = "insérer dans auth_user (uuid, name) valeurs (?,?)"; return jdbctemplate.update (sql, user.getId (), user.getName ())> 0; } @Override public boolean update (userDo user) {String sql = "update auth_user set name =? Où uuid =?"; return jdbctemplate.update (sql, user.getName (), user.getId ())> 0; } @Override public boolean delete (long id) {String sql = "delete from auth_user où uuid =?"; return jdbctemplate.update (sql, id)> 0; } @Override public UserDo Locate (Long ID) {String SQL = "SELECT * FROM AUTH_USER WHERE UUID =?"; SQLROWSET RS = JDBCTemplate.QueryForrowSet (SQL, ID); if (Rs.Next ()) {return generateEnttity (rs); } return null; } @Override Public List <DerserDo> MatchName (nom de chaîne) {String Sql = "SELECT * FROM AUTH_USER où le nom comme?"; SQLROWSET RS = JDBCTemplate.QueryForrowSet (SQL, "%" + Name + "%"); List <userDo> users = new ArrayList <> (); while (rs.next ()) {users.add (generateAntity (rs)); } retourne les utilisateurs; } private userDo GenerateEntity (SQLROWSET RS) {userDo wechatpay = new Userdo (); wechatpay.setid (Rs.GetLong ("UUID")); wechatpay.setName (Rs.getString ("name")); retour wechatpay; }} Ici, nous utilisons d'abord Annotation @Repository pour indiquer qu'il s'agit d'une classe de la couche de persistance de données, et Springboot instanciera automatiquement cette classe. Ajoutez ensuite un @Autowired au constructeur. Lorsque Springboot instancie cette classe, il injectera automatiquement l'instance JDBCTemplet dans cette classe. Ici, l'instance JDBCTemplet est automatiquement configurée par Springboot en fonction de la configuration liée à la source de données dans Applications.Properties. Selon l'algorithme de Springboot pour configurer automatiquement les sources de données, la source de données à configurer ici est HikaricP.
Les autres sont comme le développement de JDBCTemplet de printemps ordinaire. En convertissant manuellement les objets et SQL de base de données par les programmeurs, les utilisateurs peuvent être ajoutés, modifiés, supprimés, correspondant floues, requête précise et autres fonctions.
Développement de la couche commerciale de données
Définition de l'interface de la couche de service de données
Package com.yanggaochao.springboot.learn.springbootjdbclearn.service; import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.userdo; Importer java.util.list; / ** * interface de service utilisateur * * @author yang yang * @vatery @author yang yang * @sincecen 2018-03-09 * / Interface publique UserService {userDo Add (UserDo User); UserDo Update (UserDo User); Boolean Delete (Long ID); UserDo Locate (Long ID); List <serdoDo> matchname (nom de chaîne);} Implémentation de la couche de service de données
package com.yanggaochao.springboot.learn.springbootjdbclearn.service.impl; import com.yanggaochao.springboot.learn.springbootjdbClearn.dao.userdao; com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.userdo; Import com.yanggaochao.springboot.learn.springbootjdbclearn.service.UserService; import org.springframeware org.springframework.sterreotype.service; importer java.util.date; import java.util.list; / ** * user de la couche commerciale Classe d'implémentation * * @author yang gaochao * @Since 2018-03-09 * / @ servicepublic class userServiceImpl @Autowired Public UserServiceImpl (UserDao UserDao) {this.userDao = userdao; } @Override public userDo Add (userDo User) {user.setid (new Date (). GetTime ()); if (userdao.add (user)) {return user; } return null; } @Override public UserDo Update (userDo User) {if (userdao.update (user)) {return location (user.getId ()); } return null; } @Override public boolean delete (long id) {return userdao.delete (id); } @Override public UserDo Location (Long ID) {return userdao.locate (id); } @Override Public List <DerserDo> MatchName (String Name) {return userdao.matchName (name); }} Ici, cette classe d'implémentation est déclarée comme une classe de niveau commercial via une annotation @Service. UserDao de la couche de persistance permet à Springboot d'instancier cette classe de couche commerciale via @Autowired et d'injecter automatiquement la classe de couche de persistance correspondante dans cette classe commerciale.
Ici, lors de l'ajout d'objets utilisateur, lors de la définition de l'identification de l'utilisateur, un nombre de millisecondes de temps actuel est simplement utilisé comme identification. Au cours du processus de développement réel, cet endroit doit utiliser un mécanisme mondial unique pour s'assurer que ce logo ne peut pas être répété.
Développement de la couche de service externe
package com.yanggaochao.springboot.learn.springbootjdbClearn.web; import com.yanggaochao.springboot.learn.springbootjdbClearn.domain.bo.restCollectionResult; com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.userdo; Import com.yanggaochao.springboot.learn.springbootjdbclearn.service.UserService; import org.springframeware org.springframework.web.bind.annotation. *; Importer java.util.list; / ** * interface de repos http utilisateur * * @author yang gaochao * @Since 2018-03-09 * / @ restController @ requestmapping ("api / v1 / user") public class userapi {@Autowired Userservice). @Requestmapping (value = "/ add", méthode = requestMethod.post) public restItemResult <serdoD> add (@Requestbody UserDo User) {RESTITEMRESULT <DUSERDO> result = new RestitemResult <> (); user = userService.add (utilisateur); if (user! = null) {result.setItem (user); résultat.setResult ("succès"); } else {result.setMessage ("nouvel utilisateur a échoué"); result.setResult ("échec"); } Retour Résultat; } @RequestMapping (value = "/ update", méthode = requestMethod.Post) public re-retitemResult <serDo> useding (@Requestbody UserDo User) {RestitemResult <SerserDo> result = new RestitemResult <> (); user = userService.update (utilisateur); if (user! = null) {result.setItem (user); résultat.setResult ("succès"); } else {result.setMessage ("userDo a échoué à modifier l'utilisateur"); result.setResult ("échec"); } Retour Résultat; } @RequestMapping (value = "/ delete / {uUID}", méthode = requestMethod.get) public restItemResult <serDeDo> Delete (@pathvariable long uUID) {resemitemResult <UserDo> result = new RestitemResult <> (); if (userService.Delete (uUID)) {result.SetResult ("Success"); } else {result.setMessage ("Supprimer l'échec de l'utilisateur"); result.setResult ("échec"); } Retour Résultat; } @RequestMapping (value = "/ locate / {UUID}", méthode = requestMethod.get) public restItemResult <serDoD> locate (@pathvariable long uUID) {resemitemResult <UserDo> result = new resemresult <> (); UserDo user = userService.Locate (UUID); if (user! = null) {result.setItem (user); résultat.setResult ("succès"); } else {result.setMessage ("Query User a échoué"); result.setResult ("échec"); } Retour Résultat; } @RequestMapping (value = "/ match / {name}", méthode = requestMethod.get) public restcollectionResult <DerserDo> correspond (@pathvariable String name) {restcollectionResult <serodro> result = new restCollectionResult <> (); List <serodro> users = userService.matchName (name); result.setItems (utilisateurs); résultat.setResult ("succès"); Résultat de retour; }} Ici @RestController est utilisé pour déclarer qu'il s'agit d'une classe d'interface de repos HTTP. L'itinéraire d'appel pour chaque interface est formé en combinant @RequestMapping sur la classe et @RequestMapping sur la méthode. La propriété de méthode dans @RequestMapping sur la méthode déclare la méthode appelée par http. @Requestbody Annotation convertit automatiquement l'objet JSON dans les données Post en un objet PoJo. @Pathvariable convertit automatiquement les données dans le chemin d'url HTTP en paramètres de la méthode de service.
Test d'interface HTTP REST
Tester le service HTTP REST est appelé via le httpclient d'Apache Commons.
HTTP Resst appelle les classes auxiliaires
package com.yanggaochao.springboot.learn.springbootjdbclEarn; import org.apache.commons.httpclient.defaulthttpmethodretryhandler; import org.apache.commons.httpclient.httpclient; import; org.apache.commons.httpclient.methods.getMethod; import org.apache.comons.httpclient.methods.getMethod; import org.apache.comons.httpclient.methods.stringrequentity; import org.apache.comons.httpcllient.pamestentity; import org.apache.comons.httpclit java.io.bufferedReader; import java.io.inputStreamReader; import java.io.inputStreamReader; import java.io.reader; import java.util.map; / ** * @Author yang gaochao * @Since 2018-03-09 * / Classe publique HTTPCl Http Request * * @param URL L'URL de HTTP pour accéder * @return Access http Le texte de réponse obtenu après * / public String httpGetRequest (String URL, map <String, String> Headers) {try {httpclient httppClient = new HttpClient (); GetMethod Method = new GetMethod (URL); Method.SetRequestHeader ("Content-Type", "Application / JSON; charSet = UTF-8"); Method.getParams (). SetParameter (httpMethodParams.retry_handler, new defaulthTTPMethoDretryHandler (3, false)); if (en-têtes! = null) {for (string key: headers.KeySet ()) {méthode.setRequestHeader (key, headers.get (key)); }} int statuscode = httpclient.execureMethod (méthode); if (statuscode == 200) {return parseInputStream (méthode.getResponseBodyAssTream ()); } else {System.out.println (url + "status =" + statuscode); }} catch (exception e) {e.printStackTrace (); } return null; } / ** * Utilisez la méthode Post pour initier une demande HTTP * * @param URL L'URL de HTTP pour accéder * @param données de données dans la demande * @return le texte de réponse obtenu après avoir accédé à HTTP * / PUBLIC STRING HTTPPOSTREQUEST (URL String, String Data, Map <String> En-têtes) {Try {HTTPCLILLAG HttpClient (); Méthode post-méthode = nouveau post-méthode (URL); Method.SetRequestHeader ("Content-Type", "Application / JSON; charSet = UTF-8"); Method.SetRequestHeader ("User-Agent", "Mozilla / 5.0 (Windows NT 6.1; Wow64) Applewebkit / 537.36 (Khtml, comme Gecko) Chrome / 34.0.1847.131 Safari / 537.36"); if (en-têtes! = null) {for (string key: headers.KeySet ()) {méthode.setRequestHeader (key, headers.get (key)); }} method.setRequestEntity (new StringReQuertentity (data, "json", "utf-8")); int statuscode = httpclient.execureMethod (méthode); if (statuscode == 200) {return parseInputStream (méthode.getResponseBodyAssTream ()); } else {System.out.println (URL + "Status =" + StatusCode + ParseInputStream (Method.GetResponseBodyAsstream ())); }} catch (exception e) {e.printStackTrace (); } return null; } / ** * Analyse des données de texte de java.io.reader * * @param rd java.io.reader objet * @throws exception lance une erreur lorsqu'une erreur se produit * / private String parseReader (Reader rd) lance une exception {BuffereDReader brd = new BuffereDader (RD); Ligne de chaîne; StringBuilder répondSeContext = new StringBuilder (); while ((line = brd.readline ())! = null) {ResponseEContext.append (line) .append ("/ n"); } //rd.close (); if (resensEcontext.length ()> 0) {ResponseSeContext.deleteCharat (ResponseContext.Length () - 1); } return répondSeContext.toString (); } / ** * Les données de texte d'analyse du flux d'entrée * * @param sont un flux d'entrée * @throws exception lance une exception lorsqu'une erreur se produit * / private String parseInputStream (InputStream IS) lance l'exception {return parseReader (new BuffereDader (new InputStreamReader (IS))); }} Ici, nous implémentons principalement la méthode d'appel du service HTTP REST à l'aide de méthodes GET et POST.
Cas de test
Utilisez JUnit pour exécuter des cas de test. Pour implémenter le test, nous avons ajouté la dépendance Maven suivante
<dependency> <ProupId> Commons-httpclient </proupId> <Artifactid> Commons-httpclient </letefactId> <De version> 3.1 </ Version> <code> Test </ Scope> </ Dependency> <Dedency> <ProupId> Org.Codehaus.JetTison </proupId> <ArtifActid> Jettison </ Artifactid> <ccope> Tester </ccope> </Dependency>
package com.yanggaochao.springboot.learn.springbootjdbClearn; import org.codehaus.jettison.json.jsonObject; import org.junit.after; import org.junit.before; import org.junit.test; import java.net.urlencoder; import java.util.ArayList; import; java.util.list; / ** * Description: * * @author yang gaochao * @Since 2018-03-09 * / public class userApitest {private String userAddurl = "http: // localhost: 3030 / security / api / v1 / user / add"; String privé userLocateUrl = "http: // localhost: 3030 / security / api / v1 / user / locate /"; chaîne privée UserDeleTeUrl = "http: // localhost: 3030 / security / api / v1 / user / delete /"; String privé userupdateurl = "http: // localhost: 3030 / security / api / v1 / user / update"; chaîne privée userMatchUrl = "http: // localhost: 3030 / security / api / v1 / user / match /"; JsonObject addUser = new JSONObject (); Long addUserId = null; List <long> userrids = new ArrayList <> (); @BeFore public void avant () lève l'exception {addUser.put ("name", "beaux moutons"); JSONObject addResultjson = new JSONObject (new httpClientHelper (). HttpPoStReQuest (userAddurl, addUser.ToString (), null)); affirmer ("succès" .equals (addResultJson.getString ("résultat"))); addUserId = addResultjson.getjsonObject ("item"). getLong ("id"); JsonObject user = new JSONObject (); user.put ("nom", "chèvre agréable"); addResultJson = new JSONObject (new httpclienthelper (). httpPoStRequest (userAddurl, user.toString (), null)); affirmer ("succès" .equals (addResultJson.getString ("résultat"))); userIdS.Add (addResultJson.getjsonObject ("item"). getLong ("id")); user.put ("Name", "Gray Wolf"); addResultJson = new JSONObject (new httpclienthelper (). httpPoStRequest (userAddurl, user.toString (), null)); affirmer ("succès" .equals (addResultJson.getString ("résultat"))); userIdS.Add (addResultJson.getjsonObject ("item"). getLong ("id")); } @Test public void TestupDateUser () lève une exception {jsonObject user = new JSONObject (); user.put ("nom", "Smad Sheep"); user.put ("id", addUserId); Nouveau httpClientHelper (). httpPostReQuest (userUpdAnArl, user.toString (), null); JSONObject locateResultjson = new JSONObject (new httpClientHelper (). HttpGetRequest (userLocateUrl + addUserId, null)); affirmer (user.getString ("name"). égaux (locateraSultjson.getjsonObject ("item"). getString ("name"))); } @Test public void testmatchUser () lève une exception {jsonObject MatchResultJSON = new JSONObject (new httpclienthelper (). HttpgetRequest (userMatchurl + urlencoder.encode ("mouton", "utf-8"), null)); affirmer (matchResultjson.has ("items") && matchResultjson.getjsonArray ("items"). Length () == 2); MatchResultJson = new JSONObject (new httpClientHelper (). httpgetRequest (userMatchurl + urlencoder.encode ("wolf", "utf-8"), null)); affirmer (matchResultjson.has ("items") && matchResultjson.getjsonArray ("items"). Length () == 1); } @After public void after () lève une exception {if (addUserId! = Null) {jsonObject Deleteresultjson = new JSONObject (new httpclienthelper (). HttpgetRequest (userDeleterl + addeserrid, null)); affirmer ("succès" .equals (DetelereSultJson.getString ("résultat"))); } pour (Long UserId: UserIdS) {JSONObject DeleterSultJSON = new JSONObject (nouveau httpClientHelper (). httpGetRequest (userDeleErl + userId, null)); affirmer ("succès" .equals (DetelereSultJson.getString ("résultat"))); }}} Ici, deux cas de test sont déclarés dans @Test, l'un teste la fonction de modification de l'utilisateur et l'autre teste la fonction de requête floue utilisateur. @Before déclare les préparatifs à faire avant d'exécuter chaque cas de test. Ici, nous insérons d'abord trois éléments de données dans la base de données, et en même temps, nous testons également la fonction de l'ajout de données et de requête précise. @After déclare le nettoyage après l'exécution de chaque cas de test. Ici, nous supprimons principalement les données précédemment insérées. La fonction de la suppression de l'utilisateur est testée de manière synchrone ici.
post-scriptum
Voici un exemple complet de Springboot à l'aide de JDBC Templet. Si vous avez de l'expérience en utilisant JDBC Templet au printemps, alors le principal objectif de réduire beaucoup de travail de configuration au printemps.
Le code impliqué dans cet article a été téléchargé sur github, et vous pouvez également le télécharger localement
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.