Orange septembre 2020 de Spring à Spring Boot

57
Orange de Spring à Spring Boot la persistance des données avec Spring Data septembre 2020

Transcript of Orange septembre 2020 de Spring à Spring Boot

OrangedeSpringàSpringBootlapersistancedesdonnéesavecSpringData

septembre2020

sommaire

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–lapersistance–lacouched'accèsauxdonnées–lemappingobjet/relationnel–JavaPersistenceAPI(JPA)–SpringData–miseenoeuvre–annexes

lapersistance

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

lapersistancelaguerredesmondesLapersistance,c'estlemécanismeresponsabledelasauvegardeetdelarestaurationdesdonnéesdansunespacenonvolatile(fichierstexte,XML,basesdedonnées...).

Destechnologiestrèsdifférentesexistentdansunprojet:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–leslangagesdeprogrammationobjet:-trèsrépanduscarilsontfaitleurspreuves-trèsoutillés

–lesbasesdedonnéesrelationnelles:-les+utiliséesetconviennentbienauxdonnéesstructurées-beaucoupdereculsurcestechnologies-puissantlangagederequêtes(SQL)-maislesénormesvolumétriessontproblématiques-maisnécessiteunescalabilitéverticaleencasderessourcesinsuffisantes

–lesbasesdedonnéesNoSQL(NotOnlySQL):-enforteexpansion-meilleuregestiondesfortesvolumétries-scalabilitéhorizontale+hautedisponibilité-plutôtutiliséespourdesdonnéesfaiblementstructurées,nonrelationnelles-chacunespécialiséedansundomaine(clé-valeur,document...).

lapersistancelaguerredesmondesCommentfairecommuniquercesmondes?

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–codageàlamaindesrequêtesSQL:-implémentationsdifférentesd'unebaseàl'autre(ex:codeSQLdifférent)-codedifficileàtester

–mappingobjet/relationnel(ORM:ObjectRelationalMapping):-miseenrelationdesobjetsavecdestables-simplificationdel'écrituredelacouched'accèsauxdonnées-abstractiondesrequêtes-utilisationdeframeworksdédiés.

lacouched'accèsauxdonnées

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

lacouched'accèsauxdonnéesL'implémentationdelapersistancedoitêtreisoléedanslacouched'accèsauxdonnées.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

lacouched'accèsauxdonnéesElles'appuiesurledesignpatternDAO(DataAccessObject)pourrendrelacouchemétierindépendantedelasourcededonnéessous-jacente(SQL,mappingO/R...):

LeDAOproposegénéralementlesméthodesCRUD(Create/Read/Update/Delete)suivantes:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–expositiond'uneinterfaceindépendantedelatechnologieutilisée–implémentationdelalogiquedepersistancedansunetechnologiedonnée–échangedeDTO(DataTransferObject,ie.lesconceptsmétiers)représentantlemodèleobjetpersistant.

–get:récupérationd'unobjetpersistantgrâceàsonidentifiant–create:enregistrementd'unnouvelobjetpersistant–update:miseàjourd'unobjetpersistant–delete:suppressiond'unobjetpersistant–findAll:récupérationdetouteslesinstancesd'uneclassepersistante–findBy:récupérationdesobjetspersistantsselondescritèresdesélectionoudetri.

lacouched'accèsauxdonnéesL'interfaceduDAOexposelesopérationsdisponiblespourchaqueobjetpersistant.

L'implémentationpermetderéalisercesopérationsdansplusieurstechnologies,oumêmedebouchonneràdesfinsdetestsunitaires.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

lemappingobjet/relationnel

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

lemappingobjet/relationnelIls'agitdedéfinirlescorrespondancesetrèglesdepassageentrelesobjetsetlestables,pourl'écriture,lasuppression,larecherche...

Danslecasgénéral,1instancedeclasse=1enregistrementdansunetable

Ilfautpourcelaprendreencomptelesdifférencesentreles2modèles:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–l'organisationdesdonnées

objets/attributs<=>tables/colonnes

–letypagedesdonnées

conversions,restrictions:String<=>varchar

–l'unicitédesobjets

identifant(référence)enmémoire<=>cléprimaire

–relationsentreobjets/données

héritage,associations<=>tablesdejointures,clésexternes

lemappingobjet/relationnelLesapportsd'unframeworkdemappingobjet/relationnelsont:

Maisilyaquelquesdésavantages:

Hibernateestleframeworkdemappingobjet/relationnelrecommandé.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–codeplusclair,moinsverbeux–pasdeconnaissanceSQLnécessaire(maisutilequandmême)–codeprojetindépendantdelaBDDsous-jacente–migrationfacilitéed'uneBDDàuneautre–gaindeproductivité–fonctionnalitésavancéesconfigurables:cache,transactions,pagination...–meilleuresperformances(encasdebonneconfiguration)–outillage:affichagedesrequêtesgénérées,reverseengineering...

–nouveauxconceptsàappréhender–importantsproblèmesdeperformanceencasdemauvaiseconfiguration–peuts'avérerlourdàmettreenplacepourdetouspetitsprojets.

lemappingobjet/relationnelVoicilesprincipalesfonctionsavancéesapportéesparl’ORM:

Lechoixentrelazyeteagerloadingesttrèsdépendantducasd'utilisation.C'estunsubtilmélangeentre:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–gestiondecachesd'objetspersistants:-permetdeminimiserlenombrederequêtesverslasourcededonnées

–gestiondelaconcurrenced'accès:-verrouillageoptimisteoupessimistedesdonnées

–optimisationduchargementdesobjetspersistants:-àlademande(lazyloading)pournepaschargerd'objetsinutiles-dugraphecomplet(eagerloading)d'objetspersistantspouréviterl'effetN+1Select.

–lenombrederequêtes–laquantitéd'informationsramenéesparrequête–l'utilisationquiestfaitedecesinformations.

JPA

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

JPAJPA(JavaPersistenceAPI)estunstandarddelaplateformeJEEquidéfinit:

LesobjetsprincipalementmanipuléssontlesEntity:classespersistantes(POJO)avecdesannotationsJPAdéfinissantlescorrespondancesentreobjets/attributsettables/champs.

TouslesDAOmanipulantchacundesobjetspersistentsontunegrandepartiedeleurcodequiestcommune:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–desrèglesdecorrespondanceentreobjetsJavaettablesdeBDDrelationnelles–lesinterfaces(l'API)àrespecter.

–seconnecteràlaBDD–ouvrirunetransaction–écrireunerequêtesurcetobjetpersistent–récupérerlerésultat–créeroumettreàjourlebeanJava–commiter/rollbackerlatransaction–...

JPALesframeworksdemappingO/R(Hibernate...):

Avantages:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–implémententlesinterfacesdel'APIJPA–ajoutentparfoisdesfonctionnalitéspropres.

–lecodecommunauxDAOestfourni(lacouched'abstraction)–encasdechangementdeframework(pourdesraisonsdeperformances,deroadmap...):

-changersimplementlalibrairieutilisée(pom.xmlMaven)paruneautrerespectantlestandardJPA-lecodeapplicatifs'appuyantsurJPAresteinchangé.

JPAparamétrageavecSpringBootPardéfaut,SpringBootoffreunparamétragedelapersistanceavecuneconfigurationrespectantlesbonnespratiquesenvigueur.

Pouraffinerceparamétrage,onpeutajouterdesinformationsdanslefichierapplication.properties(ouapplication.yml):

spring.jpa.show-sql:true

#PostgreSQLparametersspring.datasource.url=jdbc:postgresql://localhost:port/...spring.datasource.username=<myUsername>spring.datasource.password=<myPwd>spring.datasource.driver-class-name=org.postgresql.Driver

D'autresparamètressontdisponibles.

Note:endehorsd'uneapplicationSpringBoot,ilfautconfigurerlapersistencedansunfichierpersistence.xml,ycomprislespréconfigurationsquisontoffertesdebaseparSpringBoot(voirenannexes).

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

modèleobjet4classesavecunhéritageetuneassociation

JPAexempledemappingO/R

modèlerelationnel2tablesdontuneavecunecléétrangère

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

JPA1/fairecorrespondrelesclassesetlestables

Enbasededonnées,l'héritagepeutêtrereprésentédedifférentesfaçons:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–1classe<=>1tabledanslescasclassiques,maiscen'estpassystématique,parexemplepourreprésenterunhéritage.

–1tablepourlahiérarchiecomplète,avecunchampdiscriminantetdescolonnesvides–1tableparclasseconcrèteavecunion:lacléidartestpartagéeparles2tablesServiceetMateriel–1tablepourchaqueclassedelahiérarchiesibeaucoupd'attributs(etpeuencommun).

JPA1/fairecorrespondrelesclassesetlestablesAvecJPA,grâceauxannotations:

@Entity@Table(name="Catalogue")//ici,nonutilepublicclassCatalogueimplementsSerializable{

privateintidentifiant;privateStringnom;privateStringdescription;privateList<Article>articles;

//gettersandsetters...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–@Entity:indiquequ'ils'agitd'uneentité(classe)persistante–@Table(optionnel):déterminelenomdelatables'iln'estpaslemêmequeceluidelaclasse.

JPA2/fairecorrespondrelesattributssimplesetlescolonnesLesattributssimplessontceuxquineréférencentpasd'autresobjetspersistants.

Pourchacund'eux,desconversionsserontappliquéesentreletypeJavaetletypeSQL.

Pourlecasparticulierdelacléprimaire,ilfautindiquercommentelleestgénérée.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

JPA2/fairecorrespondrelesattributssimplesetlescolonnesAvecJPA,grâceauxannotations:

@EntitypublicclassCatalogueimplementsSerializable{

@Id@GeneratedValue(strategy=GenerationType.AUTO)@Column(name="idcat")privateintidentifiant;

@Column(name="nomcat",nullable=false,unique=true)privateStringnom;...}

Attention:nullable=false,unique=truenesontutilesquesiongénèreleschémadeBDDàpartirducode.Auruntime,cescontraintessurleschampssontvérifiéesparlaBDD,pasparl'application.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–@Id:indiquequel'attributjouelerôledecléprimaire(simple).SelonlaspécificationJPA,chaqueentitédoitavoirunidentifiantunique–@GeneratedValue:indiquelastratégiedegénérationdelacléprimaire(séquenceOracle...)–@Column(optionnel):déterminelenomdelacolonnes'iln'estpaslemêmequel'attributdelaclasse.

JPA3/définirlesattributsderelation

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–indiquerlamultiplicité(1-1,1-N,N-N...)–indiquersilarelationestuni-directionnelleoubi-directionnellesuivantlesparcourspossiblesdanslesdonnées–définirsinécessaireunetabledejointure–listerla(les)clé(s)étrangère(s)représentantlacibledesattributsderelation.

JPA3/définirlesattributsderelationAvecJPA,grâceauxannotations:

@EntitypublicclassCatalogueimplementsSerializable{...@OneToMany(mappedBy="catalogue")//navigationCatalogue->collectiond'ArticlesprivateList<Article>articles;...}

@EntitypublicclassArticleimplementsSerializable{...@ManyToOne//navigationArticle->Catalogue@JoinColumn(name="idcat")//inutilesilacléétrangèredelatablealemêmenomquel'attributprotectedCataloguecatalogue;...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–@OneToMany(1-N)–@ManyToOne(N-1)–@OneToOne(1-1)–@ManyToMany(N-N)

JPA3/définirlesattributsderelation:paramétrageCesannotationspeuventêtrecomplétéesdeparamètresimportants:

@EntitypublicclassCatalogueimplementsSerializable{...@OneToMany(mappedBy="catalogue",fetch=FetchType.LAZY,cascade=CascadeType.ALL,orphanRemoval=true)privateList<Article>articles;...}

@EntitypublicclassArticleimplementsSerializable{...@ManyToOne(fetch=FetchType.EAGER)@JoinColumn(name="idcat")protectedCataloguecatalogue;...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–cascade:propagationdesopérations(persist,delete...)encascadeauxentitésenrelation(pasdisponiblepour@ManyToOne)–orphanRemoval:uneentitépeut-elleexistersanspère?(disponibleseulementpour@OneToOneet@OneToMany)–fetch:chargementdelarelationàlademande(lazyloading,choixpardéfautpourlesrelationsXxxToMany)ouimmédiate(eagerloading,choixpardéfautpourlesrelationsXxsToOne).

JPA3/définirlesattributsderelation:conseilspourlesrelationsbidirectionnellesEncréationet/oumodification,Hibernate:

Leproblèmepeutsurvenirsurlesrelationsbidirectionnellesnotamment.Silagrapped'objetsenrelationestcréée/modifiéeetpersistée:

Pourévitercela:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–sechargedelacohérencedesdonnéesdanslabasededonnées–laisseàlachargedel'applicationlemaintiendelacohérenceenmémoiredesobjetsJava.

–en1fois,aucunproblèmedecohérence–enplusieursétapes,desincohérencespeuventapparaîtrecôtéJava.

–créerdesméthodes(addXxx(),removeXxx())surlaclassepersistantepropriétairedelarelation–ellessechargerontdemaintenirlacohérence(niveauJava)delarelationdansles2sens.

JPA3/définirlesattributsderelation:conseilspourlesrelationsbidirectionnelles

Exemple:relationbidirectionnelleentreunCatalogueetsesArticles:

@EntitypublicclassCatalogue{

@OneToMany(mappedBy="catalogue")privateList<Article>articles=newArrayList<Article>();

publicvoidaddArticle(Articlearticle){articles.add(article);//ajouterunArticleàlalisteci-dessusarticle.setCatalogue(this);//ETrenseigneràquelCatalogueappartientcetArticle}

publicvoidremoveArticle(Articlearticle){article.setCatalogue(null);//supprimerlelienArticle->Cataloguefor(inti=0;i<articles.size();i++){if(articles.get(i).getId()==article.getId()){articles.remove(i);//supprimerl'Article}}}...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

JPAsynchronisationaveclaBDDLasynchronisationenbasededonnéess'effectueàl'initiative:

Leparamétragedelasynchronisationestpossible:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–dumoteurdepersistanceavantl'exécutiond'unerequêtepourassurerlacohérencedesdonnées–dumoteurdepersistanceaumomentducommit–del'applicationvialaméthodeflush().

–FlushModeType.AUTO:commeindiquéci-dessus–FlushModeType.COMMIT:seulementlorsducommitouduflush.

JPAconcurrenced'accèsVerrouillagepessimiste:

Verrouillageoptimiste:

LaspécificationJPAsepositionneexplicitementenfaveurd'unverrouillageoptimistepourobtenirdemeilleuresperformances.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–ladonnéeestverrouillée(miseàjourimpossible)tantquecelle-ciestutilisée(enlecture,enécriture...)parunclient.Touslesautresutilisateursnepeuventquelalire.

–ladonnéen'estverrouilléequelorsdesamodificationeffectiveenbasededonnées(trèsrapide)–avanttoutemodification:

-lemoteurdepersistancecomparelesversionsdeladonnéeentrel'entitépersistante(objetJava)etl'enregistrementenbasededonnées-sileurversionestégale:miseàjoureffectuéeetlaversiondeladonnéeestincrémentéedes2côtés-sinon:ladonnéeaétémodifiéeentresonaccès(salecture)etsatentativedemodification.UneOptimisticLockExceptionestautomatiquementlevée.

JPAconcurrenced'accèsPourmettreenplaceunverrouillageoptimiste:

@EntitypublicclassCatalogueimplementsSerializable{...@VersionprivateIntegerversion;...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–danslatabledelabasededonnées:avoirunecolonneversion–dansl'entitépersistante:avoirunattributversion(doncmappésurlacolonneversion)annoté@Version,uniquementmanipuléparlemoteurdepersistance,quisechargedelacohérencedesdonnées:

JPAvalidationdesdonnéesBeanValidation(JSR303)apourobjectifdestandardiserladéclarationetlavérificationdescontraintessurunmodèleJava(avantl'appeldelaBDD),viadesannotations:@NotNull,@Null,@NotEmpty,@Size,@Min,@Max,@Pattern...

Encasd'échecdevalidation:

@NotEmpty(message="NotEmpty.catalogue.nom")//@NotEmpty=@NotNull+@NotBlankprivateStringnom;

Pouractiverlavalidationautomatiquedescontraintessurleschamps,ilfautajoutercettedépendancedanslefichierpom.xml:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–uneConstraintViolationExceptionestlevée.LatransactionestmarquéerollbackOnly(ie:devantêtreannulée).–ilestpossiblederenseignerunecléd'unfichier.propertiesafind'associerlemessageadéquat(etinternationalisé):

JPAgestiondescachesIlexiste3typesdecaches:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–cachedeniveau1:-lesentitésutiliséessontmisesencachepourladuréed'unetransaction-siellessontmodifiéesplusieursfoislorsdelamêmetransaction,unseulcommitenbaseserafaitàlafindelatransaction

–cachedeniveau2:-partagépartouteslestransactions-àutiliserpourlesdonnéesderéférencequin'évoluentpas(pasdesynchronisationàfaire)-annoterlesclasseséligiblesavecjavax.persistence.@Cacheable(true)

–cachederequêtes:-utilepourdesrequêtestrèsconsommatrices/volumineusesetsouventutilisées-miseencachedelarequête(+sesparamètres)etdesiddesentitésretournées,mais:-unaccèsàlabaseserafaitpouraccéderauxentités-àutiliserconjointementaveclecachedeniveau2,pourstockerlesentitésau-delàd'unetransaction.

JPAlangagesderequêtesIlestpossibled'écriredesrequêtesenplusieurslangages:

//récupérerlepaysetsacapitaleSelectcountryFromCountrycountryJOINFETCHcountry.capital;

List<Market>markets=query.selectFrom(market).where(market.name.eq("Entreprise")).uniqueResult(market);

CriteriaBuildercb=em.getCriteriaBuilder();CriteriaQuery<Country>cq=cb.createQuery(Country.class);Root<Country>country=cq.from(Country.class);cq.select(country);TypedQuery<Country>tq=em.createQuery(cq);List<Country>allCountries=tq.getResultList();

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–JPQL(JavaPersistenceQueryLanguage):

–QueryDSL(DomainSpecificLanguage):

–APICriteria:

SpringData

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

SpringDataSpringDataestcomposédenombreuxmodulesspécialiséspartechnologie:

SpringDataoffreuneapprocheDomainDrivenDesign(DDD)del'accèsauxdonnées,ens'appuyantsurledesignpatternRepository.

DomainDrivenDesign(DDD):approchepartantd'unemodélisationdudomainemétier:

PatternRepository:mappingentrelesdonnéesdumodèlemétieretlesdonnéesstockées,selonlestechnologiessous-jacentes(BDDrelationnelles...).

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–SpringDataJPA:interactionsaveclesBDDrelationnellesens'appuyantsurHibernate–SpringDataREST–SpringDataMongoDB–SpringDataKeyValue–...

–définitiondeconceptsmétierspartagéspartouslesacteursduprojet:développeurs,fonctionnels...–précisionssurlecomportementetlesrèglesd'interactionsentrecesconcepts–donneraucodeuneconceptionorientéemétier.

SpringDatarepositoriesSpringDataoffredesrepositoriesgénériquesetd'autresspécialisésenfonctiondelasourcededonnéessous-jacente(JPA,MongoDB...).

Ainsi,SpringDatafournituneinterfacegénériqueRepository<T,ID>,permettantdespécifier:

L'interfacegénériqueCrudRepository<T,ID>:

L'interfacegénériquePagingAndSortingRepository<T,ID>:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–letyped'entité(T)géréparcerepository–letypedel'identifiantdecetteentité(ID).

–étendRepository<T,ID>–définitlessignaturesdesméthodesCRUD(leurimplémentationseraautomatiquementgénéréeparleframework).

–étendCrudRepository<T,ID>–ajoutedesméthodesdepaginationetdetri.

SpringDatarepositories

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

SpringDatarepositoriesL'interfacegénériqueQueryByExampleExecutor<T>:

L'interfaceSpringDataJPAJpaRepository<T,ID>:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–ajoutedesméthodesderechercheàpartird'unExample<T>passéenparamètre–Example<T>estuneinstancedelaclassed'entitéquisertdepatternderecherche.

–étendPagingAndSortingRepository<T,ID>–étendQueryByExampleExecutor<T>–ajoutedesméthodesliéesauxbatchsoudeflushdesdonnéesenBDD.

SpringDatarepositories

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvre

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvreconfigurationMavenDanslefichierpom.xml,ilfautd'abordavoirladépendanceverslestarterspring-boot-starter-data-jpa:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>

Cettedépendancepermetl'utilisationdeslibrairiesliéesaudéveloppementJPA(notammentHibernateavecdespropriétésconfiguréespardéfaut).

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvreutilisationd'unebasededonnéesmémoireLeplussimplepourdébuterundéveloppementnécessitantl'usaged'unebasededonnées,estd'utiliserunebasededonnéesmémoire.Celaévited'installerunevraiebasededonnées.

Elleserachargéeenmémoireaudémarragedel'application,etseradétruiteàl'arrêtdecelle-ci.

Pardéfaut,SpringBootpeutauto-configurerunebasededonnéesH2,HSQLouDerby,dumomentqueladépendanceMavenestprésente.

LadépendanceMavensuivantepermetd'utiliserlabasemémoireH2:

<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency>

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvreutilisationd'unebasededonnéespersistanteParlasuite(enintégrationcontinue,enproduction...),ilfautbiensûrs'appuyersurunebasededonnéespersistante(MariaDB,MySQL,PostgreSQL,Oracle...).

Préciserdanslepom.xmlqueltypedebaseserautilisée(ici,MariaDB):

<dependency><groupId>org.mariadb.jdbc</groupId><artifactId>mariadb-java-client</artifactId><scope>runtime</scope></dependency>

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvreutilisationd'unebasededonnéespersistanteEnsuite,enconfigurant(aumoins)l'URLdelabasededonnéesdanslefichierapplication.properties/yml,SpringBootconsidèrequ'ilnedoitpasutiliserunebasededonnéesmémoire,maislabasespécifiée:

spring.datasource.url=jdbc:mysql://${TODO_BACK_DB_SERVICE_HOST}:3306/${MY_DATABASE}?autoReconnect=true&useSSL=falsespring.datasource.userName=${MY_USER}spring.datasource.password=${MY_PASSWORD}

spring.datasource.driver-class-name=org.mariadb.jdbc.Driverspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB10Dialect

Engénéral,ledriveràutiliserestfacultatif,carSpringBootsauraledéduireàpartirdel'URL.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvrecréationd'unRepositorystandardPouraccéderauxdonnées,ilsuffitdecréeruneinterfaceannotée@Repository,quiétendl'undesprécédentsrepositories(JpaRepository<T,ID>...).

Ainsi,onpeut:

findByXxx...(Xxxétantlenomd'unattributdelaclassepersistante)

And,Or,Like,Containing,StartingWith,Exists,OrderBy,GreaterThan,Between,After...

Remarque:l'utilisationd'Example<T>estuneautrefaçond'accéderauxdonnéessansdevoirajouterdenouvellesméthodes.Maisilfautquandmêmecréersonrepository.

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–bénéficierautomatiquementdesméthodeshéritées(ex:CRUD)–ajouterdenouvellessignaturesdeméthodesrespectantunnommageprécis:

–utilisercertainsmots-clésdanscesnomsdeméthodes:

–ajouterdesquerymethodsannotées@Query,enprécisantlarequêteJPQLàexécuter:

miseenoeuvrecréationd'unRepositorystandard

Exempled'unRepositorydegestiondeclassepersistantePerson:

@RepositorypublicinterfacePersonRepositoryextendsJpaRepository<Person,Long>{//JpaRepository<Person,Long>fournitlesméthodesCRUD.//Lesméthodessuivantesn'ontpasbesoind'implémentation.

//SELECTpFROMPersonpwherep.lastname='lastname'List<Person>findByLastname(Stringlastname);

//SELECTpFROMPersonpwherep.lastname='lastname'orderby...List<Person>findByLastname(Stringlastname,Sortsort);

//SELECTpFROMPersonpwherep.lastname='lastname'andp.firstname='firstname'List<Person>findByLastnameAndFirstname(Stringlastname,Stringfirstname);

//SELECTpFROMPersonpwherep.lastnameLIKE'lastname'List<Person>findByLastnameContaining(Stringlastname);

...

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvrecréationd'unRepositorystandard...

//SELECTpFromPersonpwherep.address.zipcode='zipcode'List<Person>findByAddress_ZipCode(Stringzipcode);

//querymethod:définitionduJPQLàexécuterparSpringData@Query("SELECTCOUNT(p)FROMPersonpwherep.lastnameLIKECONCAT('%',CONCAT(?1,'%'))")longcountPersonWithLastnameContaining(Stringlastname);

//querymethodavecdesparamètresnommés@Query("SELECTpFROMPersonpwherep.lastname=:lastname)")List<Person>findByLastname(@Param("lastname")Stringlastname);

}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvrecréationd'unRepositoryspécifiqueCertainscasnesontpasprévusparSpringData(recherchemulti-critères...),etlestechniquesvuesprécédemmentnesuffisentpas.

Ilesttoujourspossiblededéfinirsespropresméthodesspécifiques,qu'ilfautimplémenter.

Pourqueletouts'articulebiendansSpringData,ilfautalorscréer:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–uneinterfacespécifiquequilistelessignaturescustomisées–unRepositorystandard(avecousansméthodesauto-implémentées)quiétend:

-unrepositorystandardSpringData(JpaRepository<T,ID>...)-l'interfacespécifique

–uneclassed'implémentationpourl'interfacespécifique:-annotée@Repository-quiapournom:<nomInterfaceRepositoryStandard>Impl.java.

miseenoeuvrecréationd'unRepositoryspécifique//interfacespécifiquepublicinterfacePersonRepositoryCustom{//signaturesdeméthodesspécifiques...}

//repositorystandardpublicinterfacePersonRepositoryextendsJpaRepository<Person,Long>,PersonRepositoryCustom{

//méthodesrespectantlespatternsattendus,ouquerymethods......

}

//classed'implémentationnommée<repositoryStandard>Impl@RepositorypublicclassPersonRepositoryImplimplementsPersonRepositoryCustom{//implementationdesméthodesspécifiques...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

miseenoeuvreutilisationdesExample<T>Unproblèmepeutsurveniraveclaméthodeprécédente,lorsquel'entitépersistanteenquestioncontientplusieursattributs,etqu'onaimeraitpouvoirfairedesrecherchessurunouplusieursattibutscombinés.

Lenombredesignaturesàajouterpeutvitedevenirgrandetfastidieuxàénumérer.

UnealternativepuissanteestdepasserparlesExample<T>(grâceàl'interfaceQueryByExampleExecutor<T>).

Danslacouchemétier,ilfaut:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–créer/récupéreruneinstancedel'entitépersistante,dontcertainsattributssontrenseignés(critèresderecherche)–ajouterdesrègles(ExampleMatcher)surlarecherche(containing,starting...)–appelerl'unedesméthodesdenotrerepositoryquiaccepteunExample<T>enparamètre.

miseenoeuvreutilisationdesExample<T>

Exemple:imaginonsqu'unutilisateur,viaunformulairederecherche,asaisilescritèresderecherchesuivantspourunePerson:

//Person(id,firstName,lastName,age,address)Personperson=newPerson(null,"Mickael",null,18,null);

VoicicomentutilisercetexempledePerson:

publicList<Person>findAllPersonsByExample(Personperson){

//créationdesrèglesderechercheExampleMatchermatcher=ExampleMatcher.matching().withIgnorePaths("id","lastname","address").withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);

//créationdel'Exampleaveclescritères+lesrèglesExample<Person>exampleOfPerson=Example.of(person,matcher);

returnrepoPerson.findAll(exampleOfPerson);}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

testsSpringData

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

testsSpringDatatesterunitairementlapersistanceIlfauttestersilapersistancedesdonnéesfonctionne,afinquelesautrescouchesdel'applicationpuissents'apuyerdessusavecconfiance.Ilfautvaliderque:

EnplusdeJUnit,SpringBootapportel'annotation@DataJpaTestpourfaciliterletravaildeconfiguration:

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–lesannotationssurlesentitéspersistantessontcorrectementutilisées–lacohérencedesdonnéesestassurée(notammentlesrelationsentreobjetspersistants)–lerepositoryfonctionnepourleCRUDetlesméthodescustomisées.

–auto-configurationd'Hibernateetd'unebasemémoirepourlestests–recherchedesentitésJPA(anciennement@EntityScan)–activationdeslogsSQL–fournitured'unTestEntityManageroffrantdesméthodessouventutiliséespourlestests(persistAndGetId(),persistFlushFind()...).

testsSpringDatatesterunitairementlapersistance@DataJpaTestpublicclassToDoListRepositoryTest{

@InjectprivateTestEntityManagerentityManager;

@InjectprivateToDoListRepositorytdlRepo;

@TestpublicvoidsaveToDoList_OK(){longnb=tdlRepo.count();ToDoListtdl=newToDoList("vacances","12/03/2018",false,null);tdl=tdlRepo.saveAndFlush(tdl);assertNotNull(tdl.getId());assertEquals(tdlRepo.count(),nb+1);}...}

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

documentation

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

–SiteofficielSpring–DocumentationSpringData–DocumentationSpringDataJPA–DocumentationHibernate–DocumentationJPA–JavadocJPA–SiteSpringFrameworkGURU:tutoriel–SiteBaeldung:denombreuxtutoriaux

annexeparamétragedanslefichierpersistence.xml<?xmlversion="1.0"encoding="UTF-8"?><persistencexmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0">

<persistence-unitname="myPU"transaction-type="RESOURCE_LOCAL"><non-jta-data-source>jdbc/formationDataSource</non-jta-data-source><class>com.orange.formation.model.persistent.Catalogue</class><exclude-unlisted-classes/>...<properties><!--replacewithcorrectdatabasedialect--><propertyname="hibernate.dialect"value="org.hibernate.dialect.PostgreSQLDialect"/><!--developmentconfig-switchtofalseinproduction--><propertyname="hibernate.show_sql"value="true"/>...</properties></persistence-unit></persistence>

deSpringàSpringBoot-lapersistancedesdonnéesavecSpringData(Orangeinternal)

merci