14
1/14 1. Tečaj - Spring part 2 1.1. Dan 1 – Osnove Spring Frameworka Uvod u Spring, postavljanje radnog okruženja: Potreba za Spring Frameworkom – zašto je potrebno pojednostavljenje razvoja aplikacija u Javi Što je Spring Framework, kako se instalira i koristi Konfiguracija, aplikacijski kontekst Inversion of Control Dependency Injection Što je AOP i aspekti i zašto se koriste Aspekti u Springu 1.2. Dan 2 – Pristup podacima Pristup podacima pomoću Springa: Princip Spring Templatinga Princip DAO sloja (Template, Callback) Spring i JDBC Springova hijerarhija iznimki Transkacije Uvod u JPA Spring Dana

Spring Workshop

  • Upload
    mile

  • View
    10

  • Download
    4

Embed Size (px)

DESCRIPTION

Spring Java Workshop

Citation preview

10/1111/11Teaj - Spring part 2Dan 1 Osnove Spring FrameworkaUvod u Spring, postavljanje radnog okruenja: Potreba za Spring Frameworkom zato je potrebno pojednostavljenje razvoja aplikacija u Javi to je Spring Framework, kako se instalira i koristi Konfiguracija, aplikacijski kontekst Inversion of Control Dependency Injection to je AOP i aspekti i zato se koriste Aspekti u SpringuDan 2 Pristup podacimaPristup podacima pomou Springa: Princip Spring Templatinga Princip DAO sloja (Template, Callback) Spring i JDBC Springova hijerarhija iznimki Transkacije Uvod u JPA Spring Dana

Priprema radnog okruja (IDE)Upaliti IDE (SpringSource Tool Suite). Trenutna stabila verzija je 2.3.2. i temelji se na eclipse 3.5. koja dolazi prepakirana s nizom odlinih plugin-ova, meu kojima je i bivi Spring IDE.

Import projekta spring-workshop-parent.zip u IDEFile > Import > Existing Projects into WorksapaceSelsect radio buttion: "Select Archive file:" and navigate to spring-workshop-parent.zip file.Deselect all of the projects except spring256course.parentNow you should have one project in your workspace: spring256course.parentThis is the top level project, and inside him there are several more projects:spring256course.part1.core project with workshop examples for IoC, DI, Aspects spring256course.parrt2.core - project with workshop examples for JdbcTemplate, Transactions and Remotingspring256course.part2.web project with web part of the remooting example spring256course.part3.web - project with workshop examples for Spring MVC

All these projcets use maven for build and dependency managemt. (Few words about maven: has anyone used it, know what it does....)

Import of additional projects:File > Import > Existing Projects into WorksapaceSelsect radio buttion: "Select root directory:" and navigate to the spring256course.parent folder of imported project inside of your workspace, and then enter it and select spring256course.part1.core BE SURE TO UNCHECK "Copy projects into workspace" checkbox!

(Repeat procedure for all other projects)

How does one workshop project look like? Open spring256cpurse.part1.core. Workshop projects follow maven structure of the projects.src/ folder has several sub folders: src/main this is your working space src/tecaj finished java classes and configuration files examples, ready for use (cut and paste)NOTE: teaj is NOT added to the project classpath do not add it!

Plese write down where is your workspace and where you have unpacked SpringDistribution.Both main/ and teaj/ folders has the same structure and consist of 2 subfolders:java/ - here are your java filesresources/ - here you will place various configuration files.

Spring Data AccessInicijalna priprema:Import projekta: SpringPart2.zipRegistracija tomcat server-a u EclipseImport projekta: SpringPart2Web.zip

[Svi slideovi do Priprema]

Otvoriti dva DOS komandna promptu i pozicionirati se u root SpringPart2 projekta:cd C:/workspaces/SpringWorkshop/SpringPart2/te pokrenuti skripte: startdb.bat manager.bat

Prvi puta izvriti initDB.sql i insert_data.sql scripte

Probati select * from genre;Osnovne operacijeDohvaanje konekcije - DataSourceU IDE u net/croz/ src/tecaj otvorite sljedee dvije datoteke: ticket-ctx.xml TestDataSource.java[TestDataSource.java] Ova Java klasa pokazuje dva pristupa stvaranju konekcije prema bazi. Prvi nain je runo programski kroz Java kod:

BasicDataSource basicDs = new BasicDataSource();basicDs.setDriverClassName("org.hsqldb.jdbcDriver");basicDs.setUrl("jdbc:hsqldb:hsql://localhost/tecaj");basicDs.setUsername("sa");basicDs.setPassword("");

Drugi nain je kroz Spring container:

org.hsqldb.jdbcDriverjdbc:hsqldb:hsql://localhost/sa

U knjizi na stranici 168 (123) pogledati ostale opcije za konfiguriranje commons dbcp BasicDataSource-a.

Ovako konfigurirani dataSource dohvaama iz Container-a kao i svaki drugi Bean:

ApplicationContext ctx = new ClassPathXmlApplicationContext("/net/croz/springinaction/exercise1/ticket-ctx.xml");DataSource ds = (DataSource) ctx.getBean("dataSource");

te ga potom dohvatimo i izvrimo jednostavni upit.

JdbcTemplate jt = new JdbcTemplate(ds);int count = jt.queryForInt("select count(*) from Genre");System.out.println("Done found total of " + count + " records");

queryForXXX je najednostavniji oblik metode koji prua JdbcTemplate. Pogodan je za male jednostavne brze upite na bazu. No osim int queryFor podrava upite i za objekte mape, liste i sl.Sljedei primjer demonstrira osnovne operacije dohvaanja podataka s queryForXXXX metodama.[TestJdbcTemplate.java]Dohvaanje podataka:Za razliku od prethodnog primjera gdje smo JdbcTemplate instancirali runo preko konstruktora ovdje ga dohvatimo iz container-a: JdbcTemplate jt = (JdbcTemplate) ctx.getBean("jdbcTemplate");

Kao to je reeno, osim queryForInt() metode JdbcTemplate ima jo cijeli niz slinih metoda no osim Java primitiv-a je JdbcTemplat-a zna vraati i Java klase. Takav je npr. prvi upit:Object[] parameters = new Object[] {new Integer(3)};String name = (String) jt.queryForObject("select name from Genre where id= ? ", parameters, String.class);

Kao to vidimo ovo je malo kompleksniji upit u kojem se kao rezultat vraa objekt klase String, no osim toga query koji se izvrava je parametriziran.Konano pogledajmo kako izgleda query koji vraa Listu objekata:

List l = jt.queryForList("select id, name from Genre where id> ?", parameters);

U ovom sluaju svaki dohvaeni redak iz baze u vraenoj Listi, je predstavljen kao Map-a, iji elementi su: key ime polja, a value vrijednost.Pogedajmo sljedei "code snnipet": System.out.println("List size >> " + l.size());Iterator it = l.iterator();while (it.hasNext()) {Map m = (Map) it.next();System.out.println("Map:" + m);System.out.println("ID:" + m.get("ID"));System.out.println("NAME:" + m.get("NAME"));}Osnovne operacije nad bazom: Insert/Update/DeletePogledajmo sada kako se rade osnovne Database operacije Insert, Update i Delete. U tu svrhu otvorite java klasu: TestDbInsertUpdate.java.Bilo koji SQL query moe se izvriti jednostavnim koritenjem metode execute:

jt.execute("delete from Genre");

Jednostavnu insert operaciju najlake je izvesti na sljedei nain:

int x = jt.update("insert into Genre (id, name) values (1, 'Opera')");

Update operacija izvodi se lino kao i queryForObject() metoda:

x = jt.update("update Genre set name='Pop /Rock' where id= ? ", new Object[] {new Integer(3)});System.out.println(x + " rows updated.");

Na slian nain izvodi se i parametrizirana Delete operacija:

x = jt.update("delete from Genre where id= ? ", new Object[] {new Integer(2)});

U prijanjim primjeriima JdbcTemplat nam je vraao Java objekte tipa HashMap, String i sl. koritenjem RowMapper() moemo na jednostavan nain postii da nam Dao vraa prave poslovne objekte koje oekuje naa aplikacija.

[TestCallback.java] Upit koji vraa kompleksne Java objekteOsim ovih jednostavnih upita, JdbcTemplate omoguava i Upite koji kao rezultat vraaju kompleksne java objekte ili liste istih. Ovo se postie prosljeivanjem Java klase koje implementiraju jedan od sljedea dva interface-a: RowMapper RowCallbackHandlerOtvorite datoteku TestCallback.java.Interface RowMapper je najjednostavniji nain za mapiranje rezultata SQL query-a u Java objekte, pogledajte sljedei primjer:

List genres = jt.query(sql, new Object[] {new Integer(4)},new RowMapper() {public Object mapRow(ResultSet rs, int numRow) {Genre genre = new Genre();try {genre.setId(rs.getLong("id"));genre.setName(rs.getString("name"));} catch (SQLException e) {e.printStackTrace();}return genre;}});U ovom primjeru kao CallbackHandler realiziran je pomou inline klase, koja obavlja konverziju. U sluaju RowMappera potrebno je samo implementirati mapRow() metodu. Ova metoda se poziva za svaki redak koji je od DB pristigao kao rezultat upita i oekuje se da ona vrati inicijaliziran "Value Object."

Drugi nain je koritenjem RowCallbackHandelr interface-a. Upotreba istog je slina kao i RowMappera no ipak malo drugaija. Pogledajmo sljedei blok koda:

jt.query(sql, new Object[] {new Integer(4)},new RowCallbackHandler() {public void processRow(ResultSet rs) {Genre genre = new Genre();try {genre.setId(rs.getLong("id"));genre.setName(rs.getString("name"));result.add(genre);} catch (SQLException e) {e.printStackTrace();}}});Kao to vidimo RowCallbackHandler interface se sastoji samo od jedne metode processRow(), unutar koje itamo podatke iz dobijenog ResultSet-a i pretvaramo ih u kompleksni Java Objekt.No, RowCallbackHandler NIJE statless kao RowMapper tj. rezultati se uvaju u npr. static varijabli a koja je vidljiva iz RowCallbackHandler. Ovo je jako pogodno ako je potrebno mapirati vie redaka iz DB u jedan Java objekt.

Osim ova dva interface-a postoje i: ParameterizedRowMapper i ParameterizedRowCallbackHandler kkoji se mogu koristiti za iskoriste punu snagu Java 5.

[ShowDaoImpl.java] Primjer DAO-a implementiranog pomou JdbcTemplate-aPogledajmo sad primjer jednog cijelog DAO-a implementiranog kroz JdbcTemplate.Kao to je vidljivo iz prethodnih primjera kljuni element je objekt JdbcTemplate.Otvorite Java klase: ShowDao.java ShowDaoImpl.java TestShowDao.javaOsim koritenja upitnika u query-u koje onda zahtjeva da parametri budu predani u nekom redosljedu Spring JdbcTemplate podrava i tzv. NamedParameter upite. Pogledajmo npr. metodu:

public int create(Show show) {Map params = new HashMap();params.put("name", show.getName());params.put("genre_id", show.getGenre().getId());return getNamedParameterJdbcTemplate().update(INSERT_SQL, params);}

Slino ovome NamedParameter query-e moemo koristiti i u SELECT upitima, pogledajmo npr. metodu:

public Show findById(long id) {Map params = new HashMap ();params.put("id", new Long(id));List result = getNamedParameterJdbcTemplate().query(FIND_BY_ID_QUERY, params,new ShowMapper());if (result.size() > 0) {if (result.size() == 1) {return (Show) result.get(0);}throw new IncorrectResultSizeDataAccessException(1);}return null;}Ovdje takoer treba ukazati na koritenje klase ShowMapper() koja se koristi da mapiranje podatka iz baze na Java objekte, i nalazi se u net.springinaction.exercise1.dao.jdbctemplate.mapper paketu.Testiranje: JUNIT testoviKako provjeriti da li sve radi? Pomou JUNIT i integration testova. (Da li je netko ve koristio Junit i integracijske testove)

U mapi src/test/java nalazi se klasa: ShowDaoTest.java koja sadri testove za ShowDao, U PackageExplorer perspektivi odabrati ShowDaoTest.java klasa, i u izborniku nakon desnog klika orabrati opciju: Run As Junit Test.No u ovom trenutku kako Dao nije zavren klasa e javiti greke, za sve metode koje nisu gotove.Pogledajmo sve metode:Prvo idu find medote, praene s metodama koje testiraju i neuspjeh.

Zatim sljede metode za testiranje create() i update() koje su sline po strukturi.

Konano, na kraju su metode za brisanje pojedinog zapisa i brisanje cijele tablice.

ZADATAK: dovriti ShowDaoImplKoristei gotove metode i primjere implementirati preostale metode u ovom Dao objektu: findShowByName() createShow() delete() deleteAll()Te pomou ShowDaoIntegrationTest.java osigurati da je implementacija metoda korektna.

Rasprava na kraju:to nedostaje ovom Dao objektu: Poslovni Exceptioni, npr. ShowNotFoundException sad vraa null. Za ovo smo mogli iskoristiti i generiki Exception od Springa-a: throw new DataRetrievalFailureException("No show found for ID=" + id);

NotImplementedMethod Exception!

GenreDaoImpl.java i mapiranje SQL operacija na Java objektePogledajmo sad kako kroz JdbcTemplate ostvariti tehniku koja se zove mapiranje Java objekata na SQL operacije.Otvorite sljedee datoteke: GenreDao.java GenreDaoImpl.java Genre.java TestDao.java

Pokrenimo TestGenreDao.java i ako je sve u redu u database e biti dodan novi redak i potom e biti obavljen update na njemu.

INSERTED genre: net.springworkshop.demo3.model.Genre@27d15e[id=6,name=Conference]1 rows updatedUPDATED genre: net.springworkshop.demo3.model.Genre@94fe42[id=6,name=Spring conference]

Pogledajmo sada GenreDaoImpl.java, za razliku od dosadanjih JdbcTemplate primjera ova klasa koristi neke napredne tehnike koje donosi Spring database layer.

Kao prvo pogledajmo metodu update() koja obavlja izmjenu ve postojeeg retka u tablici.public int update(Genre genre) {//1. construct SqlUpdate objectSqlUpdate updateGenre = new SqlUpdate();updateGenre.setDataSource(dataSource);updateGenre.setSql(UPDATE_SQL);updateGenre.declareParameter(new SqlParameter("name", Types.VARCHAR));updateGenre.declareParameter(new SqlParameter("id", Types.INTEGER));updateGenre.compile();

//2. execute update...Object[] parameters = new Object[] {genre.getName(), genre.getId()};return updateGenre.update(parameters);}U gornjem primjeru iskoriten je objekt SqlUpdate koji je Java reprezentacija jedne SQL operacije. Korisnost ovog pristupa je u veoj brzini izvravanja kompajliranih query-a.

to nije u redu s ovom metodom?SqlUpdate objekt je unutar same metode, pa se instancira svaki put prilikom poziva metode i u stvari usprava rad!

Pogledajmo sad malo kompliciraniji primjer, metodu findById(Long id). public Genre findById(Long id) {GenreMappingQuery custQuery = new GenreMappingQuery(dataSource, FIND_BY_ID_QUERY);Object[] parms = new Object[1];parms[0] = id;List results = custQuery.execute(parms);if (results.size() > 0) {return (Genre) results.get(0);} else {//or throw Exception....return null;}}Kao to se vidi ova metoda za dohvaanje podataka koristi klasu GenreMappingQuery, koja je ekstenzija klase MappingSqlQuery. Na ovaj nain se moe ubrzati izvravanje SELECT upita.

private class GenreMappingQuery extends MappingSqlQuery {public GenreMappingQuery(DataSource ds, String initQuery) {super(ds, initQuery);super.declareParameter(new SqlParameter("id", Types.INTEGER));compile();}public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {Genre g = new Genre();g.setId(rs.getLong("id"));g.setName(rs.getString("name"));return g;}}Klase koje nasljeuju MappingSqlQuery moraju imati implementiranu samo jednu metodu: mapRow(ResultSet rs, int rowNumber). U gornjem primjeru vidimo jednu jednostavnu implementaciju ove metode.ZADATAK: Napisati JUNIT test za GenreDaoIntegrationTest.javaTest bi trebao imati barem sljedee metode:testFindById()testFindById_NotFound()testCreateGenre() Napomena: Table GENRE NEMA automatic ID generation kao SHOW, ve prilikom upisa novog zapisa treba dodieliti i ID doznati koliko ima zapisa u db: numGenres napraviti novi Genre: id = numGenres+1, name = CONFERENCE provjeriti da li se poveao broj zapisa u db provjeriti upisane vrijednostitestUpdateGenre() dohvatiti postojeci Genre iz db (id=4) promijeniti name = ARTISTC PERFORMANCE procitati ponovo iz db-a Genre id=4 provjeriti da li spremljene vrijednosti odgovaraju

Prodiskutirati probleme kod ovog scenarija pisanja testova: database cache, npr. Kod OpenJPA, itanje nakon update operacije NE ide do baze ve ita iz lokalnog cache-a. Da bi se otilo do baze treba napraviti commit transakcije.(Rijeenje ove klase ve postoji u src/tecaj/java)

JdbcTemplate Vjeba: WeatherDaoZadatak:Otvorite u src/tecaj folderu datoteke: WeatherDao.java WeatherdaoImpl.java WeatherApp.java

Na osnovu znanja steenih u ovoj vjebi napraviti WeatherdaoImpl koji za svoj rad koristi tablicu WEATHER_FORECAST, koja se sastoji od tri polja ID, PLACE i FORECAST, te napiite Junit integration test koji testira ovaj Dao.Tablica WEATHER_FORECAST pojanjenja:U ovu tablicu pohranjuju se podaci o prognozi za odreeni grad, za danas, sutra i prekosutra.ID - ovo polje sadri ifrirano informaciju o kojem se danu radi: 0- Danas 1- Sutra 2 - PrekosutraPLACE - sadri ime gradaPrimary key je ID, PLACE kombinacijaFORECAST - tekstualni opis prognoze (sunano, oblano, kiovito...).Hint: ponite sa forDate() te potom sa setForecast() metodama.

Na kraju pogledajte i prebacite klasu MyWeatherDaoDbImpl.java iz foldera src/tecaj u folder src/java!TransakcijeU Junit testovima transakcije je za nas odradila Spring klasa, koju su nai testovi nasljeivali. Nakon svake test metode izvren je rollback i baza je vraena u prvotno stanje.Sad emo pogledati mogunosti Spring-a za upravljanje transakcijama.

[Slide-ovi do "Deklaracija Transakcija"]Otvorite u src/taaj folderu package springinaction/exercise3/Ovdje se nalazi WeatherService.java (interface) i dvije njegove implementacije: WeatherServiceImpl.java - jednostavna implementacija koja koristi WeatherDao za svoj rad. WeatherServiceTransacionSupportedImpl.java - klasa koja nasljedjuje WeatherServiceImpl i ima ugraenu programsku podrku za transakcije.Otvorite datoeteku TestTransaction.java i WeatherServiceImpl.javaU WeatherServiceImpl.java primjetite da je metoda:

protected void setDayAfterTomorrow(WeatherData weatherData) {...}sabotirana tj. da ne radi nita ve samo baca exception.Ovo emo iskoristiti da vidimo da li radi transaction rollback.Pokrenite TestTransaction.java i nakon to se program zavri sa Exception-om provjeirite da lil u db ima zapisa za Petrinju? (Ne bi ih trebalo biti)Pogledajmo sad sljedei blok u transaction-ctx.xml

PROPAGATION_REQUIRED

Ova dva bean-a su srce podrke za transakcije.Prvi deklarira transaction manager oko database source-a, dok je drugi Proxy bean oko WeatherService-a.Obratite panju na zakomentiranu liniju, odkomentirajte je i zakomentirajte ovu iznad nje!Ponovo pokrenite TestTransaction.java i vidjeti ete da sad unato Exceptionu u bazi imamo 2 zapisa za Petrinju!

Anotation driven transactions

Pogledati src/tecaj/java exercise3 WeatherServiceImpl

Takoer su realizirane su preko AOP-a.Import namespacea

@Transactional