Írjunk egy olyan BookStore
osztályt, ami JPA-n keresztül végzi az előbbi feladatban készített Book
entitások kezelését. Valósítsuk meg a következő műveleteket:
// Konstruktor, egy persistence unit nevét kapja, és létre is hoz egy neki
// megfelelő `EntityManager`-t.
BookStore(String persistenceUnitName);
// Új könyvet visz fel az adatbázisba, és visszaadja a kapott azonosítóját.
int addBook(Book newBook);
// Törli a megadott azonosítójú könyvet, ha már szerepel az adatbázisban.
void removeBookById(int bookId);
// Lekérdezi az összes könyvet az adatbázisból.
java.util.List<Book> getAllBooks();
Készítsünk egy parancssori alkalmazást, ami teszteli az új BookStore
osztályt.
-cp
kapcsolóval megadni a Derby JDBC driver-ét, ami a JDK-n belül a db/lib/derbyclient.jar
-ban foglal helyet.Készítsünk egy Author
entitást, amely egy könyv szerzőjét képes reprezentálni:
id
, egész szám, elsődleges kulcs, automatikusan generált)name
, sztring, max. 255
karakter, kötelező)birthDate
, dátum - csak év/hónap/nap, opcionális)books
): a lejjebb leírt Book
entitásokból álló listaKészítsünk egy, az eddigiekhez hasonló Book
entitást, ami egy könyv következő adatait képes tárolni (jelenleg csak egy szerzője lehet!):
id
, egész szám, elsődleges kulcs, automatikusan generált)title
, sztring, max. 255
karakter, kötelező)author
): egy, a fentebb leírt Author
entitáspublishedAt
, dátum, opcionális)pages
, egész szám, opcionális)price
, java.math.BigDecimal
10,2
pontossággal, opcionális)StockStatus
, az értékek sztringként tárolva)IN_STOCK
)OUT_OF_STOCK
)UNKNOWN
)Írjuk felül az equals()
és a hashCode()
műveleteket úgy, hogy az elsődleges kulcs alapján működjenek!
Alakítsuk át az Author
és Book
entitásainkat úgy, hogy képesek legyenek egy könyvhöz több szerzőt is tárolni! Valósítsunk meg egy parancssoros alkalmazást, ami képes az alábbiakra:
// Szerző hozzáadása
Author addAuthor(String name, Date birthDate /* lehet null */);
// Szerző törlése
void removeAuthor(Author author);
// Könyv hozzáadása (legalább egy szerzővel, leltári állapot legyen `UNKNOWN`)
Book addBook(String title, Author author);
// Könyv társítása szerzőhöz
void assignAuthorToBook(Author author, Book book);
// Könyv törlése
void removeBook(Book book);`
Az entitásokat egy lokális books
nevű adatbázisban tároljuk. A szerzők és könyvek közötti kapcsolaton próbáljuk ki a különböző CascadeType
beállításokat!
Készíts olyan JPA programot, amely egy egyetemet modellez az alábbiak szerint. Az egyetemnek vannak hallgatói (Student
) és oktatói (Professor
), valamint vannak kurzusok (Course
). Mindegyik rendelkezik automatikusan generált azonosítóval (id
) és névvel (name
). A hallgatók a kurzusokat hallgatják (studies
, egy hallgató több kurzust is hallgathat), az oktatók oktatják (teaches
, egy oktató több kurzust is oktathat). A kurzusokhoz ismert a tárgy neve (subjectName
, de még elegánsabb külön Subject
entitás felvételével), valamint az, hogy mikor kezdődik: dayOfWeek
(ez DayOfWeek
felsorolási típusként ábrázolva), hour
, minute
.
Az adatok egyszerű feltöltéséhez legyenek student.csv
, professor.csv
, course.csv
fájljaink, ezek soronként, vesszővel elválasztva tartalmazzák egy-egy entitás adatait. A program kezdetben töltse fel a university
persistence unitot a fájlokból felolvasott adatokkal, majd a sztenderd bemenetről várt sorok utasítanak a következőkre. (A nagybetűs részek változtatható paraméterek, a kisbetűsek fix részei a parancsoknak. Feltételezhető, hogy a bemeneten csak érvényes parancsok érkeznek, nem szükséges a hibás eseteket – pl. hiányzó paraméterek – kezelni.)
day-off S
: kilistázza azokat a napokat, amelyeken S
hallgatónak nincsen órája.colleagues T
: kilistázza azokat az oktatókat, akiknek van T
-vel közös tárgyuk.teaches X Y
: X
oktató oktatja-e Y
hallgatót?kind X
: az X
névről megmondja, hogy hallgató, oktató vagy kurzus neve-e, vagy nincsen a rendszerben. A sztenderd kimenetre ennek megfelelően íródjon ki a student
, Professor
, course
, unknown
szöveg. Az egyszerűség kedvéért tegyük fel, hogy a négy lehetőség közül pontosan egy teljesül.merge C1 C2
: a C1
kurzus megszűnik, a hallgatói a C2
kurzust hallgatják a továbbiakban.swap C1 C2
: a két kurzus oktatói cserélődjenek fel.between D H1 H2
: kilistázza a D
napon H1
óra és H2
óra között kezdődő órák neveit. H2
óra 0
perc már nem értendő bele az intervallumba.
between D H1 M1 H2 M2
: kilistázza a D
napon H1
óra M1
perc és H2
óra M2
perc között kezdődő órák neveit, az intervallum mindkét vége zárt.Készítsünk egy webalkalmazást, ami egy ScopeTest
nevű szervletet tartalmaz. A szervlet legyen elérhető a /test
URL-en keresztül a webalkalmazás gyökeréhez képest. A szervlet a következőket tegye:
GET
kérésekre kell válaszolniaA szervlet működését próbáljuk ki egyszerre több, különböző böngészőben, vagy a munkamenet sütiket időnként eltávolítva; illetve az alkalmazásszervert közben újraindítva is! Ezek az események a különböző élettartamú attribútumok megszűnését eredményezik, így megfigyelhető viselkedésük.
Készítsünk egy webalkalmazást, ami egy FormTest
nevű szervletet tartalmaz. A szervlet legyen elérhető a /form
URL-en keresztül a webalkalmazás gyökeréhez képest. A szervlet a következőket tegye:
GET
kérésre írjon ki egy statikus HTML űrlapot, ami két szövegbeviteli mezőt tartalmaz, és POST
metódussal küldi el adatait.
POST
kérésre ellenőrizzük, hogy a két paramétert megkaptuk-e, és számok vannak-e benne. Ha igen, adjuk össze a két számot, és mentsük el a munkamenetben, majd irányítsuk vissza a kérést az aktuális oldalra, de most már GET
metódussal, külső átirányítással.
GET
metódus kezelését úgy, hogy az űrlap kiírása előtt ellenőrizze, hogy a munkamenetben van-e tárolt érték. Ha van, írja ki, majd törölje az értéket a munkamenetből. A korábbi űrlapot továbbra is írja ki.
A megfelelően elkészített alkalmazás a POST-Redirect-GET mechanizmust szemlélteti. A felhasznált munkamenet változó azt a gyakran “flash scope”-nak is nevezett objektum élettartamot szemlélteti, amikor az érték csupán a következő felhasználásig marad meg a munkamenetben.
Készítsünk egy webalkalmazást, ami a korábban látott könyv (Book
) entitásokat képes egy űrlapon keresztül hozzáadni, illetve ezeket egy táblázatban listázni. Ehhez készítsünk két szervletet:
AddBookServlet
GET
kérésre egy HTML űrlapot jelenít meg, ahol a könyv adatai kitölthetők. Az űrlapot POST
kéréssel küldjük el, majd dolgozzuk is fel; alkalmazzuk a POST-Redirect-GET mechanizmust.
ListBooksServlet
egy GET
kérésre a “request scope”-ba menti a lekérdezett könyvek adatait, majd egy JSP lapra belső átírányítással ott megjeleníti őket.
Az adatok perzisztálásához használjunk JPA-t, az adatforrás pedig az alkalmazás-szerverben legyen beállítva.
Készítsünk egy szerializálható Book
osztályt, ami egy könyv következő adatait képes tárolni:
title
)author
)year
)pages
)Készítsünk egy BookStore
nevű interfészt az alábbi műveletekkel:
// Hozzáad egy könyvet. Ha már létezik ilyen című, hamissal tér vissza,
// ellenkező esetben tényleg elmenti a könyvet, és igazat ad.
boolean addBook(Book newBook);
// Az összes könyv lekérdezése.
List<Book> getAllBooks();
// Lekérdez egy könyvet a címe alapján. Ha nem található, null-t ad vissza.
Book getBookByTitle(String title);
Implementáljuk a fenti interfészt egy stateless session bean távoli interfészeként. A bean neve legyen BookStoreBean
. Tároljuk a könyveket egy hasítótáblában, a címükkel azonosítva őket (ez egy java.util.HashMap
, neve books
).
Teszteljük a bean működését egy parancssoros alkalmazással!
Az előző feladatot egészítsük ki egy második, lokális felületű bean-nel, ami megadja egy könyvről, hogy oldalszáma 200
felett van-e:
boolean isALongBook(Book book);
Ezt az új, lokális LongBookFilterBean
-t használjuk fel a BookStoreBean
-ben, hogy csak azokat a könyveket adjuk vissza a getAllBooks
eredményeként, amikre ez a művelet igazat ad. Az új bean-t a @EJB
annotációval injektáljuk az előzőbe.
Készítsünk az előző feladatban szereplő bean-hez egy application client container-ben futó, függőség-injektálást alkalmazó klienst a teszteléshez.
Készítsünk egy szerializálható Book
osztályt, ami egy könyv következő adatait képes tárolni:
title
)author
)year
)pages
)Készítsünk egy könyvespolcot szimuláló Bookshelf
nevű interfészt az alábbi műveletekkel:
// Hozzáad egy könyvet. Ha már létezik ilyen című, hamissal tér vissza,
// ellenkező esetben tényleg elmenti a könyvet, és igazat ad.
boolean addBook(Book newBook);
// "Lezárja a polcot", tehát több könyvet nem lehet rátenni.
// A lekérdezés után a megvalósító bean példány többé nem használható!
// Visszaadja a polcon lévő könyvek összesített oldalszámát.
int closeShelf();`
Implementáljuk a fenti interfészt egy stateful session bean távoli interfészeként. A bean neve legyen BookshelfBean
. Tároljuk a könyveket egy hasítótáblában, a címükkel azonosítva őket (ez egy java.util.HashMap
, neve books
).
Teszteljük a bean működését egy parancssoros alkalmazással!
Készítsünk egy ShelfPages
entitást, ami az alábbi mezőkből áll:
shelfId
), egy automatikusan generált számtotalPages
)Az előző feladatot egészítsük ki egy ShelfStatistics
interfészű bean-el, ami az alábbi távoli műveletet támogatja:
// Megadja minden korábbi polchoz, hogy mennyi volt a rajtuk lévő könyvek
// összesített darabszáma.
List<ShelfPages> getStatistics();
Az implementáció egy állapotmentes session bean-ben kapjon helyet. Az adatokat JPA-val perzisztáljuk, egy lokális ShelfPersistence
interfészen keresztül:
// Elmenti az adott oldalszámot egy új `ShelfPages` entitás segítségével.
void saveNewShelfWith(int totalPages);`
A BookshelfBean
ezen a lokális interfészen keresztül tudja lementeni az adatokat. Az előző klienst egészítsük ki úgy, hogy a statisztika is lekérdezhető legyen!
Adjunk egy olyan interceptor metódust a BookshelfBean
-hez, ami az addBook
metódus végrehajtása esetén naplózza a 200 oldalnál hosszabb könyvek címét, ezen kívül pedig teljesen transzparens a működése.
Készítsünk egy singleton session bean-t, ami különböző számlálókat képes karbantartani, és értéküket lekérdezni. A következő Counters
interfészen keresztül legyen elérhető:
// Megnöveli egy számláló értékét 1-el. Ha nem létezik számláló a megadott
// néven, akkor hozzon létre egy újat, és értéke legyen 1.
void increment(String name);
// Visszaadja egy számláló értékét. Ha nincs ilyen néven számláló, akkor nullát
// adjon vissza.
int getValue(String name);`
A számlálók tárolását JPA-val valósítsuk meg egy adatbázisban. A bean működjön gyorsítótárként, az alábbiak szerint:
Map
-be, amit gyorsítótárnak használunkHasználjuk a konténer által biztosított konkurenciakezelést, az adatokat író és olvasó metódusokat lássuk el a megfelelő annotációkkal.
Parancssoros klienssel teszteljük a megoldást.
Készítsük el az előző feladatban létrehozott singleton session bean egy olyan változatát, ami a bean által kezelt konkurenciát alkalmaz.
Készítsünk egy alkalmazást, ami bemutatja a @TransactionAttribute
annotáció eltérő értékeinek működését!
Ennek tesztelésére készítsünk két stateless session bean-t:
TestBean
-en valósítsuk meg, minden értékhez egy metódust készítve.
WrapperBean
rendelkezzen egy referenciával egy csak lokálisan elérhető TestBean
-re. Ez a “külső” bean kezelje maga a tranzakciókat, ne a konténerre hagyatkozzon.
WrapperBean
tartalmazzon egy test
metódust, ami a TestBean
minden metódusát meghívja egy-egy érvényes tranzakcióval, illetve anélkül is. Naplózzuk az egyes végrehajtásokat, illetve a fellépő kivételeket.
Készítsünk egy parancssori klienst a WrapperBean
elérésére.
Készítsünk egy alkalmazást, ami teszteli a normál és a tartós feliratkozás (durable subscription) közötti különbséget!
Egy servlet egy űrlap elküldésének hatására írjon üzeneteket egy JMS témába (topic). Ugyanezt a témát olvassuk egy olyan klienssel, aminek paraméterként megadható, hogy normál vagy tartós feliratkozást végezzen.
Küldjünk üzeneteket a témára, miközben a kliens alkalmazás fut, illetve akkor is, mikor nem. Figyeljük meg a normál és tartós feliratkozás közötti különbséget!
Készítsünk egy BookService
szolgáltatást, ami a books
url alatt érhető el, és a korábban készített Book
entitások lekérdezésére, törlésére, hozzáadására, illetve módosítására alkalmas:
Készítsünk egy szerializálható Book
osztályt, ami egy könyv következő adatait képes tárolni:
title
: címauthor
: szerzőyear
: első kiadási évszámpages
: oldalszámEgy BookStore
nevű osztályban tároljuk a könyveket egy hasítótáblában, a címükkel azonosítva őket (ez egy java.util.HashMap
, neve books
). Az osztály valósítsa meg az alábbi műveleteket:
// Néhány előre létrehozott könyvet tesz a hasítótáblába, kezdeti adatnak.
void addSomeBooks();
// Betölti a egy megadott fájlból a könyveket.
// Ha sikerült, true-t ad vissza, egyébként false-t.
boolean loadBooks(String fileName);
// Kiírja egy megadott fájlba a könyveket.
// Ha sikerült, true-t ad vissza, egyébként false-t.
boolean saveBooks(String fileName);`
Készítsünk egy parancssori alkalmazást, ami kipróbálja a fenti műveleteket.
Készítsünk egy egyszerű kliens-szerver alkalmazást, amik TCP kapcsolaton kommunikálnak. A megoldáshoz használjuk fel az előző feladat Book
és BookStore
osztályait!
A szerver a következőket teszi egy kérés beérkezésekor:
BookStore
példányt az előző feladatbóltrue
-t, és a könyvetfalse
-tA kliens a következőket teszi:
true
, akkor kiolvassa a könyvet, és kiírjafalse
, akkor kiírja, hogy a könyv nem találhatóKészíts egy TCP-n kommunikáló szervert, ami egy-egy külön szálon szolgálja ki a kliensek kéréseit. A kliens egy sztringet küld a szervernek, amire az visszaadja, hányszor kaptott már ugyanilyen kérést (ez egy egész szám).
Ehhez a szervernek belső állapottal kell rendelkeznie, ami például egy java.util.HashMap<String, Integer>
típusú mezővel kivitelezhető. Figyeljünk oda, hogy az egyes szálak versengenek az állapot frissítéséért, ezért szinkronizálni kell a hozzáférést. (Először kiolvassa, majd megnövelve visszaírja az értéket. Ha ez nem atomi műveletként történik meg, a számláló nem lesz pontos.)
Készíts egy Java RMI-n keresztül elérhető szolgáltatást megvalósító szervert, és egy hozzá kapcsolódó klienst, ami az alábbi műveleteket valósítja meg:
// Új könyv hozzáadása. Ha szerepelt már ilyen című, adjon vissza false-t,
// egyébként true-t.
boolean add(Book newBook);
// Könyv lekérdezése cím alapján. Ha nem található, adjon null-t.
Book getByTitle(String title);
// Könyv eltávolítása cím alapján. Ha nem vol ilyen, adjon vissza false-t,
// egyébként true-t.
boolean removeByTitle(String title);`
Az interfész neve legyen BookService
. Felhasználhatók az előző gyakorlaton írt Book
és BookStore
osztályok, vagy azoknak az alábbi, egyszerűsített változata:
// Book.java ///////////////////////////////////////////////////////////////////
import java.io.Serializable;
public class Book implements Serializable {
public String title;
public String author;
public int year;
public int pages;
}
// BookStore.java //////////////////////////////////////////////////////////////
import java.util.HashMap;
public class BookStore {
public HashMap<String, Book> books = new HashMap<>();
public void BookStore() {
for (int i = 0; i < 10; ++i) {
Book book = new Book("Book #" + i, "Author #" + i, 2000 + i, 300 + 10 * i);
books.put(book.getTitle(), book);
}
}
}
startNetworkServer
paranccsal.
ij
kliens használatával.
db/bin
alkönyvtárban található.ij
név az “i
nteractive J
DBC scripting tool” rövidítése.Miután az ij
kliens elindult, annak promptján belül adjuk ki a következő parancsokat.
Hozzunk létre egy adatbázist azáltal, hogy csatlakozunk hozzá:
CONNECT 'jdbc:derby://localhost:1527/books;create=true';
BOOK
nevű táblát, amiben a könyveket tárolhatunk.
Book
osztály mezőivel.CREATE TABLE
SQL parancsot.year
név foglalt, ezért vagy "year"
alakban kell leírnunk, vagy más nevet kell helyette választani.SHOW TABLES
paranccsal, majd jelenítsük meg az új táblánk oszlopait a DESCRIBE BOOK
paranccsal.
SELECT
, INSERT
, UPDATE
, DELETE
).
Végül állítsuk le az adatbázis-szervert a stopNetworkServer
paranccsal.
+1. Alternatív megoldás: használjunk beágyazott Derby szervert, olyan módon, hogy nem indítunk adatbázis-szervert, és az ij
-vel az alábbi kapcsolatot kérjük:
`CONNECT 'jdbc:derby:books;create=true';`{.java}
Készítsünk egy JDBC-t használó alkalmazást, ami az előző gyakorlatokon készített könyv (Book
) entitásokat képes perzisztálni! Ehhez használjuk az Apache Derby adatbázis-szervert. A könyvek tárolásának módját és egyéb részleteket az előző feladat tartalmazza.
Valósítsunk meg egy új BookStore
osztályt úgy, hogy adatbázist használjon:
// Konstruktor, csatlakozik az adatbázishoz. Kivétel esetén hibát ír a konzolra.
BookStore(String host, int port);
// Új könyvet visz fel az adatbázisba. Hiba esetén hamisat ad, különben igaz
// logikai értéket.
boolean addBook(Book newBook);
// Töröl egy könyvet a címe alapján. Hiba esetén hamisat ad, különben igaz
// logikai értéket.
boolean removeBookByTitle(String title);
// Lekérdezi az összes könyvet az adatbázisból. Hiba esetén üres listát ad.
java.util.List<Book> getAllBooks();
BookStore
osztályt, majd nézzük meg az adatbázis tartalmát az ij
parancs segítségével is.-cp
kapcsolóval megadni a Derby JDBC driver-ét, ami a JDK-n belül a db/lib/derbyclient.jar
-ban foglal helyet.Készítsünk egy, az eddigiekhez hasonló Book
entitást, ami egy könyv következő adatait képes tárolni:
id
, egész szám, elsődleges kulcs, automatikusan generált)title
, sztring, max. 255
karakter, kötelező)author
)publishedAt
, dátum, opcionális)pages
, egész szám, opcionális)price
, java.math.BigDecimal
10,2
pontossággal, opcionális)StockStatus
, az értékek sztringként tárolva)IN_STOCK
)OUT_OF_STOCK
)UNKNOWN
)A könyvek a books
nevű táblában legyenek tárolva.
equals()
és a hashCode()
műveleteket úgy, hogy az elsődleges kulcs alapján működjenek!