Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pare pytan o JPA + RE: pytanie o sposób na pracę z "lazy loadingiem"

2 views
Skip to first unread message

GfP

unread,
Oct 1, 2007, 7:23:44 PM10/1/07
to
(Link do srcow na koncu , jest to prj z eclipsa dokladniej red hat dev
studio, niejest ladny ale jego celem sa eksperymenty na jpa i jest
baza dla tego posta)
Zaczne od propozycji rozwiazania problemu lazy loadingu (odrazu
zaznaczam ze jestem poczatkujacy w ORMach i moje pomysly biora sie z
ostatnich kilku dni spedzonych na rozgryzaniu JPA oczym bedzie
dalej ;) ). DO OSOB MAJACYCH POJECIE O JPA, prosze przyczytac post do
konca poniewaz w jego drugiej czesci bede mial pare pytan.
O ile dobrze rozumiem pytanie, problemem niejest co zrobic zeby pobrac
dane tylko co zrobic zeby pobierac jest sensownie (czyli bez
wczytawania polowy bazy ;) )
Wydaje mi sie ze problem mozna podzielic na dwie czesci (bede sie
opieral na wymenczonym do granic ludziej wytrzymalosci przykladzie
bloga):
1- (klient musi miec dostemp do czesciowej wersji wszystkich
informacji) klient chce zobaczyc naglowki wszystkich elementow (np
nazyw blogow) ale poniewaz nasz obiekt bloga zawiera mase informacji
ktore sa w tej chwili niepotrzebne niechcemy ich przesylac. W takim
przypadku z tego co narazie wiem o JPA najlepiej chyba bylo by
stworzyc jakis obiekt naglowkowy zawierajacy tylko najwazniejsze
informacje i wypleniac go. W momencie gdy klient kliknie na jakis
konkretny blog pobieramy juz pelen obiekt bloga w normalny sopsob ale
bez komentarzy ktore bede oznaczone jako lazy loading.
2- (klient musi miec dostemp do pelnej wersji ale tylko czesci
informacji) i tu walsnie pojawia sie pytanie w jakis sposob sensownie
je ladowac bo wczytywanie np 10000000 komentarzy jest bez sensu i tu z
pomoca przychodzi NativeQuery ktore (jezlei baza wspiera) umozliwia
wczytywanie np po 20 komentarzy, wystrczy wyslac id bloga (albo caly
obiekt w zaleznosci od zastosowania), limit, offset i mozemy sobei
doladowywac dane pokolei.
Jezeli chodzi o warstwe implementacyjna to na chwile obecna wydaje mi
sie ze najlepij miec obiekty DAO + obiekt(y) serwisowe. Czyli np
BlogDAO,BlogKomDAO + BlogSerwis ktory zawiera referncje do obu DAO,
niby nic wtym nadzywczjnego ale ma pare ciekawych wlsanoci. Popierwsze
to dobre miejsce do zazadzania tranzakcjami i mozna skorzystac z
ciekawej wlasnoci Springa (no wkazdym razie dlamnie ciekawej ;) )
mianowicie mozna zosatwic klasa DAO domyslny poziom widocznosci
(zamiast public) i chociaz sa one niewidoczne dla BlogSerwisu to
spring moze je i tak tam wszczyknac (oczywiscie interfejsy sa public).
Wydaje mi sie to otyle ciekawe ze dzieki temu niemusimy wystawiac
naszych bezcennych DAO.

Pytanie i problemy: (calosc tyczy JPA w wykonaniu toplinka 11.1 +
spring 2.1m4) <b> link do src na samym dole </b>

1- lazy loading
Jak to wlasciwie dziala ?? wszytko niby jest proste i oczywiste ale
jednak nie dziala tak jak bym sie tego spodziewal. Z tego co rozumiem
w momencie zakonczenia traznzakcji obiety powinny przejsc w stan
"odalczony"
ale zamiast tego caly czas mam dostemp do kolekcj oznaczonych jako
LAZY. Przyklad:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:application-context.xml"})
@TransactionConfiguration(transactionManager="transactionManager",
defaultRollback=false)
@Transactional
public class BlogServiceTest {

@Autowired
private BlogService blogService;

private Blog blog;


@Test
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void getAll(){
System.out.println("<<<<<<<<<<STRAT>>>>>>>>>>>");
List<Blog> bl=blogService.getAll();
//tu komentarzy niewidac
System.out.println(" blogi: "+bl);
blog=bl.get(0);
System.out.println("<<<<<<<<<<KONIEC>>>>>>>>>>>");
}

@AfterTransaction
public void jestlazy(){
System.out.println(("AfterTransaction"));

//a tu jak sie okazju mozna je bez problemu pobrac chociaz
tranzakcja juz sie zakonczyla
blog.getKomantarze().get(1);
System.out.println("kom2: "+blog.getKomantarze());
}
}

no wlasnie dlaczego moge juz po zakonczeniu tranzakcji zainicjalizowac
kolekcje?? przyokazji pytanie czy mozna jakos (nieuzywajac
nativeQuery) nieinicjalizowac odrazu calej kolekcji?? w logach wszytko
niby ok tranzakcja sie konczy em jest zamykany ale wyglonda to tak
jakby obiekt proxy ktory odpowiada za lazy loading sam sie laczyl do
bazy i pobieral dane. Wydaje mi sie to dzine ale moze czegs
nierozumim.

2-Z jakeigos powodu do bazy jest zapisywane wiencej obiektow nizbym
sie spodziewal przyklad:

public class BlogServiceTest3 extends
AbstractTransactionalDataSourceSpringContextTests {
BlogService blogServ;

public void testGetLimit(){
List<BlogKomentarz>
blogKomList=blogServ.getLimitedKomentarzeDlaBloga(1, 3, 5);
Blog b=blogServ.getById(1);
this.setComplete();
this.endTransaction();

BlogKomentarz bk=blogKomList.get(0);
bk.setTresc("ZMIENILEM TO!!!");

b.addKomentarz(bk);

this.startNewTransaction();

b=blogServ.margeBlog(b);

this.setComplete();
}

public void setBlogServ(BlogService blogServ) {
this.blogServ = blogServ;
}

@Override
protected String[] getConfigLocations() {
return new String[] { "classpath:application-context.xml" };
}
}

Rozpoczynam tranzakcje.
Pobieram najpierw liste komentarzy (dla bloga o id=1, 3 komentarze
(limit), z offsetem ustawionym= 5 )
Nastempnie pobieram obiekt bloga
Zatwierdzam traznzakcje i ja koncze. (i wszytko narazie jest ok)

Zmieniam teraz JEDNE komentarza
dodaje go do bloga
Rozpoczynam tranzakcje
merguje
zawtierdzam i koncze tranzakcje


I tu zaczyna sie dzaic cos czego nierozumiem. mianowicei pole @version
zmienia sie dla tych 3 komentarzy, przeciez niekazalem w zaden sposob
ich zapisywac do bazy. Jezeli komus bedzie chialo sie zaglondac do
kodu to zauwazy ze komentarze sa pobiera przy pomocy nativeQuery i
wiazane z blogiem do ktorego naleza, ale nawet jezeli wylacze te
powiazanie i pole blog w obiekcie BlogKomentarz bedzie nullem to i tak
ta zmiana powendruje do bazy :/ Dlaczego?

Z gory dzieki za odp.

Testowany na: JPA = toplink, spring 2.1 m4, postgresql, junit 4.4,
log4j,cglib, + commons
przy odpalaniu w argumentach dla vm nalezy dodac: -javaagent:C:\spring-
framework-2.1-m4\dist\weaving\spring-agent.jar

link src: http://www.mediafire.com/?8xuodtxh1uy

GfP

unread,
Oct 2, 2007, 2:57:50 PM10/2/07
to
Dzis udalo mi sie sciagnac hiberneta (czego wczoraj sie nie dalo) i
sprawdzilem czy sa roznice. No i okazuje sie ze jest ruznica. W
hibernecie ten problem nie wystempuje
po zakonczeniu tranzakcji (o ile zakomentuje System.out.println("
blogi: "+bl); a to daltego ze odwoluje sie w toString do lazy
kolekscji ) wyskauje LazyInitializationException czyli moim zadniem
prawidlowo. Oczyiscie pytanie do sobob znajachy sie na toplinku
pozotsaje otwarte bo chcialbym wiedziec skad ta roznica. Czy odgrywa
tu jaks role cache toplinka?
Ale zeby niebylo zafajnie to sie okazalo ze teraz jest problem z
NativeQuery ktorego w toplinku niema :/ ze zamiast #<nazwa_parametru>
pisze sie :<nazwa_param> to zdazylem juz zauwazyc ale blad ktory sie
pojawia ejst przedziwny...
NativeQury wygolnda nastempujaco:


@NamedNativeQueries( {
@NamedNativeQuery(name = "BlogKomentarz.getLimitKomForBlog", query =
" select k.id ,k.tresc as tresc , k.version, b.id as bid, b.tytul as
btytul,b.tresc as btresc, b.version as bversion from BlogKomentarz k
JOIN Blog b on k.blog_id=b.id where k.blog_id=:blog_id order by k.id
limit :limit offset :offset ", resultSetMapping = "mapBlogKom")
})
@SqlResultSetMappings( {
@SqlResultSetMapping(name = "mapBlogKom", entities = {
@EntityResult(entityClass = BlogKomentarz.class,fields= {
@FieldResult(column="id",name="id"),
@FieldResult(column="tresc",name="tresc"),
@FieldResult(column="version",name="version")
}),
@EntityResult(entityClass = Blog.class,fields= {
@FieldResult(column="bid",name="id"),
@FieldResult(column="btytul",name="tytul"),
@FieldResult(column="btresc",name="tresc"),
@FieldResult(column="bversion",name="version")
})
})
})


jedyna roznica miedzy toplinkiem a hibernatem jaka zauwazylem to
wspowmniane juz parametry ( ':' zamiast '#' ) blad jest nastempujacy:

2007-10-02 20:40:18,359 DEBUG [org.hibernate.loader.Loader] - result
row: EntityKey[domain.BlogKomentarz#8], EntityKey[domain.Blog#1]
2007-10-02 20:40:18,390 INFO [org.hibernate.type.LongType] - could not
read column value from result set: blog4_1_0_; The column name
blog4_1_0_ was not found in this ResultSet.
2007-10-02 20:40:18,390 DEBUG [org.hibernate.jdbc.AbstractBatcher] -
about to close ResultSet (open ResultSets: 1, globally: 1)
2007-10-02 20:40:18,390 DEBUG [org.hibernate.jdbc.AbstractBatcher] -
about to close PreparedStatement (open PreparedStatements: 1,
globally: 1)
2007-10-02 20:40:18,390 DEBUG
[org.hibernate.util.JDBCExceptionReporter] - could not execute query
[select k.id ,k.tresc as tresc , k.version, b.id as bid, b.tytul as
btytul,b.tresc as btresc, b.version as bversion from BlogKomentarz k
JOIN Blog b on k.blog_id=b.id where k.blog_id=? order by k.id limit ?
offset ?]
org.postgresql.util.PSQLException: The column name blog4_1_0_ was not
found in this ResultSet.
at
org.postgresql.jdbc2.AbstractJdbc2ResultSet.findColumn(AbstractJdbc2ResultSet.java:
2450)
at
org.postgresql.jdbc2.AbstractJdbc2ResultSet.getLong(AbstractJdbc2ResultSet.java:
2317)
at org.hibernate.type.LongType.get(LongType.java:28)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:103)
at
org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:
2096)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)

<tu reszta info caly log mozna pobrac: http://www.mediafire.com/?0jjenxoy5t3
>

Moze tu tez problemem jest tez jakis cache?? Bede wdzieczny za jakies
wskazowki (albo plene wyjasniene ;) )


Jacek Laskowski

unread,
Oct 2, 2007, 4:20:37 PM10/2/07
to
GfP wrote:

> DO OSOB MAJACYCH POJECIE O JPA, prosze przyczytac post do
> konca poniewaz w jego drugiej czesci bede mial pare pytan.

Cześć!

Muszę przyznać, że już dawno nie widziałem tak rozwle^H^H^Hległej
wiadomości na grupie. Generalna uwaga, która pomoże w komunikacji
elektronicznej - jeśli wiadomość zajmuje więcej niż pół ekranu to raczej
możesz liczyć na nikłe zainteresowanie (= wszyscy zarobieni).

A odnośnie pytań to wierz mi - chciałem pomóc, ale...nie dałem rady
zrozumieć problemów, bo one nie wynikają z działania JPA, ale
konfiguracji Springa i kilku innych zabawek. Nie dam dzisiaj rady
przebrnąć przez to. Proponuję wyłuskanie problemów JPA i przedstawienie
ich w strawniejszej postaci :P

Jacek

--
Jacek Laskowski
http://www.JacekLaskowski.pl

GfP

unread,
Oct 2, 2007, 7:14:40 PM10/2/07
to
Po pierwsze dzieki za dobre checi :)

Co do springa to moim zdaniem wszytko jest z nim ok ale jezeli widzisz
jakies przeslanki, ze to on moze bcy powodem to bede wdzieczny za nie,
bo moze cos przeoczylem.

Co do moich pytan, po instalacji hibernata i paru eksperymentach
okazuje sie, ze anomalie jakie opisalem wczesie w zwiazku z
toplinkiem, w hiebernacie sie nie pojawiaja, zobacze moze na jakims
forum toplinka uda sie cos wyjasnic. Ale zato mam nowe pytania (tym
razem o hibernata) ;) (postarm sie krotko)
I - problem optymistic lockow. Skoro spec JPA niezaleca recznego
ustawiania @version to czy jest jakas ogolna zasada co zrobic gdy
klient odsyla mi obiekt (bdedacy modyfikacja istniejacego) ale ktory
stowrzyl usiebie niemajac dostempu do aktualnego numeru @version.
Widze pare wyjsc:
1 -pobrac odpowiadajacy obiekt z bazy uzyc jakiesc metody kopjujacej
nowe wartosci w miejsce starych i zapisane do bazy (+ to niegrzebanie
przy @version, - to wlasnei metdoa kopjujaca)
2 - pobrac obj z bazy i w otrzymanym od klienta obj nadpisac @version
aktualnym ( - grzebanie przy wersji)
3 - uzycie @version w przypadku obj ktorch modyfikacja nieopiera sie
na analizie ich obecnego stanu jest zlym pomyslem
Czyt ktos ma doswiadczenie jaka metoda sie lepiej sprawdza albo moze
ma jakis lepszy pomysl, i wogle jak czesto i wjakich sytuacjah
uzywacie optymistic locking ?

II - Niedokonca jest dlamnie jasne jak dziala
@QueryHint(org.hibernate.fetchSize) w @NamedQuery. Z tego co widze z
bazy i tak pobierana jest calosc (to jest dlamnie jasne skoro jpa
niema czegos takiego jak limit a co gorsza nie wsyztkie bazy to
wspieraja), ale co juz niejest dlamnei oczywsite wydaje mi sie , po
analize logow, ze tak naprawe do pamienic sa wczytwane i inicjalizowne
tez wszytkie obiekty zwrocne przez zapytanie (fetchSize na tym etapie
tez chyba niejest jeszcze uwzglendniany) i tak naprawde dopiero na
ostatnim etapie do klienta zaptanie idzie obcieta lista obiektow.
Dobrze rozumiem? i czy mozna to jakos optymalizowac czy moze juz sam
hibernate podejmuje decyzje czy wczytac do pamienci wszytkie jesli
jest ich malo czy zawsze wczytuje calosc?Wskrucie, jak to wlasciwie
dziala?


Jacek Laskowski

unread,
Oct 3, 2007, 4:38:35 PM10/3/07
to
GfP wrote:
> Po pierwsze dzieki za dobre checi :)

...którymi piekło jest wybrukowane (gdzieś to już słuszałem pod moim
adresem - de javu?).

> Co do moich pytan, po instalacji hibernata i paru eksperymentach
> okazuje sie, ze anomalie jakie opisalem wczesie w zwiazku z
> toplinkiem, w hiebernacie sie nie pojawiaja, zobacze moze na jakims
> forum toplinka uda sie cos wyjasnic. Ale zato mam nowe pytania (tym
> razem o hibernata) ;) (postarm sie krotko)

Widzę jest krócej, ale nadal za długo. Pisarz jakiś z Ciebie, czy co?
Może pora na artykuł - byłby akurat pod względem zawartości słów ;-)

> I - problem optymistic lockow. Skoro spec JPA niezaleca recznego
> ustawiania @version to czy jest jakas ogolna zasada co zrobic gdy
> klient odsyla mi obiekt (bdedacy modyfikacja istniejacego) ale ktory
> stowrzyl usiebie niemajac dostempu do aktualnego numeru @version.

Nie martw się o to doputy nie nastąpi modyfikacja na kliencie. W takim
przypadku wykonujesz merge, a później podczas zatwierdzenia zmian
(flush, albo koniec transakcji) przechwytujesz wyjątek.

p.s. Manipulujesz @Version przy operacjach zbiorczych (ang. bulk
updates). Specyfikacja wręcz to nakazuje - patrz 3.4 Optimistic Locking
and Concurrency strona 54 (albo
http://jlaskowski.blogspot.com/2007/04/optymistyczne-blokowanie-w-jpa.html)

GfP

unread,
Oct 4, 2007, 4:18:35 AM10/4/07
to
ok, dzieki :)

0 new messages