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

EJB3, JPA, @EJB

45 views
Skip to first unread message

Kolszew

unread,
Jan 17, 2008, 9:01:25 AM1/17/08
to
Mam beana Entity w którym jest metoda callback'owa @PostRemove
w której w zależności od pewnych warunków chcę zapisać informacje
do "kapownika" że ktoś usunął encję.
Kapownik ma fasadę - zwykły bezstanowy sesyjny bean.
Potrzebuję go w tej metodzie @PostRemove.
Niestety kiedy chcę zrobić tak aby został wstrzelony do mojego beana
Entity za pomocą

@EJB KapownikFacadeLocal kapownik;

to wstrzeliwany nie jest (kapownik == null)

Stąd pytanie czy @EJB i wstrzeliwanie nie działa dla beanów Entity?
No i jak tak, to czy da się to jakoś inaczej zrobić?

Pozdrawiam
Krzysiek

Jakub Kaniewski

unread,
Jan 17, 2008, 10:28:00 AM1/17/08
to
Kolszew napisał(a):

> Stąd pytanie czy @EJB i wstrzeliwanie nie działa dla beanów Entity?
> No i jak tak, to czy da się to jakoś inaczej zrobić?
Też się nad tym tym zastanawiałem ... chyba pozostaje tylko JNDI lookup.

J.K.

Kolszew

unread,
Jan 17, 2008, 10:40:59 AM1/17/08
to
> Też się nad tym tym zastanawiałem ... chyba pozostaje tylko JNDI lookup.
>
> J.K.

... mało eleganckie jest.
Szukam jakiegoś innego rozwiązania ale z tego co widzę w necie to
tak jak piszesz ręczna dłubanka.
Myślałem o obsłudze adnotacji @EJB w interceptorze, ale niestety
interceptory nie dotyczą beanów Entity.

Pozdro
Krzysiek

Jacek Laskowski

unread,
Jan 17, 2008, 3:47:20 PM1/17/08
to
Kolszew wrote:

> Stąd pytanie czy @EJB i wstrzeliwanie nie działa dla beanów Entity?

Nie. Encje są najzwyklejszymi klasami Java (POJO), które mimo, że są
zarządzane przez kontener EJB, to nie dotyczy ich obsługa @EJB.

Sądzę, że chciałbyś zrobić z encjami więcej niż do czego zostały
stworzone - mapowanie bytów obiektowych na relacyjne i odwrotnie.
Wszystkie inne "akcje" powinny być wykonywane w bycie, który nimi
zarządza (przesłania ich widoczność). Tym bytem może być ziarno EJB, ale
równie dobrze podobne "coś".

Zapewne zadasz teraz pytanie, a do czego zatem wykorzystać @PostRemove?
Ja odpowiem, że do zamknięcia zasobów/innych elementów, które służyły do
pracy encji - wykonania mapowania. Muszę popytać ludzi z grupy
standaryzującej specyfikację i/lub poczytać książki na ten temat skąd
pomysł na owe interceptory rozwojowe.

> No i jak tak, to czy da się to jakoś inaczej zrobić?

JNDI to tak na prędce.

Jacek


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

Jakub Kaniewski

unread,
Jan 17, 2008, 5:48:07 PM1/17/08
to
Jacek Laskowski napisał(a):

> Nie. Encje są najzwyklejszymi klasami Java (POJO), które mimo, że są
> zarządzane przez kontener EJB, to nie dotyczy ich obsługa @EJB.
Jasne Encje to Encje - po prostu POJO, injection nie ma prawa działać.
Ale dlaczego nie można wstrzelić Session Beana w EntityListenera.
Ostatnio zastanawiałem się nad zastąpieniem bazodanowych triggerów
EntityListenerami (Waldek ostatnio o tym pisał), ale bez dostępu do
EntityManager jest to niemożliwe. Wiadomo, że jest JNDI lookup ... ale
to takie niezbyt eleganckie i średnio przenośne między serwerami aplikacji.

http://www.mail-archive.com/us...@openjpa.apache.org/msg00656.html -
sposób obejścia tego problemu w Geronimo

J.K.

Kolszew

unread,
Jan 18, 2008, 2:27:56 AM1/18/08
to
> Jasne Encje to Encje - po prostu POJO, injection nie ma prawa działać.
> Ostatnio zastanawiałem się nad zastąpieniem bazodanowych triggerów
> J.K.

Dokładnie o to mi chodzi, skoro używam technik mapowania OR, gdzie
jednym z celów jest uniezależnienie się od konkretnego silnika bazy
danych, to naturalne jest że muszę jakoś zastąpić trigery (inaczej
musiał bym pisać i utrzymywać je dla każdej bazy z osobna).
Szukam miejsca/metody w JPA gdzie mógłbym to zrealizować.

Pozdrawiam
Krzysiek

Kolszew

unread,
Jan 18, 2008, 2:41:10 AM1/18/08
to
> Wszystkie inne "akcje" powinny być wykonywane w bycie, który nimi
> zarządza (przesłania ich widoczność). Tym bytem może być ziarno EJB, ale
> równie dobrze podobne "coś".

Tak, ale idea triggera (który tak naprawdę chcę zastąpić) jest taka że
on ma się wykonać zawsze jak ktoś zmieni np jakieś pole. A jak mam
to zapewnić na poziomie ziarna ejb jak ziarno to zwraca zarządzaną
encję do warstwy logiki biznesowej a w tej warstwie ustawiane jest
jakieś pole w tej encji (co gwarantuje że ta nowa wartość trafi do bazy)
a o tej zmianie ziarno które zwróciło tą encję po prostu nie wie.

W którym miejscy mam wstawić kod który będzie odpowiadał za to że ta
zmiana zostanie odnotowana w jakimś logu (kapownik :)), czy zostaną
zaktualizowane odpowiednio inne encje. Naturalnym miejscem jest
@PrePersist, z tym że za ch..rę nie wiem jak w tym @PrePersist dobrać
się do poprzedniej wartości pola.
Trudności z wstrzeleniem innego ziarna umiem już pokonać (JNDI)
choć wygląda to na "sztukę".

>
> Zapewne zadasz teraz pytanie, a do czego zatem wykorzystać @PostRemove?
> Ja odpowiem, że do zamknięcia zasobów/innych elementów, które służyły do
> pracy encji - wykonania mapowania.

? enigmatyczne trochę ...


Muszę popytać ludzi z grupy
> standaryzującej specyfikację i/lub poczytać książki na ten temat skąd
> pomysł na owe interceptory rozwojowe.

Pozdrowienia
Krzysiek

Jacek Laskowski

unread,
Jan 20, 2008, 4:20:00 AM1/20/08
to
Kolszew wrote:


> W którym miejscy mam wstawić kod który będzie odpowiadał za to że ta
> zmiana zostanie odnotowana w jakimś logu (kapownik :)), czy zostaną
> zaktualizowane odpowiednio inne encje. Naturalnym miejscem jest
> @PrePersist, z tym że za ch..rę nie wiem jak w tym @PrePersist dobrać
> się do poprzedniej wartości pola.
> Trudności z wstrzeleniem innego ziarna umiem już pokonać (JNDI)
> choć wygląda to na "sztukę".

Przysiągłbym, że ktoś już o to mnie pytał poza grupą i odłożyłem sobie
ten temat "na później". Intrygujące.

>> Zapewne zadasz teraz pytanie, a do czego zatem wykorzystać
>> @PostRemove? Ja odpowiem, że do zamknięcia zasobów/innych elementów,
>> które służyły do pracy encji - wykonania mapowania.
>
> ? enigmatyczne trochę ...

Mógłyś wyliczać pewne wartości na podstawie danych z bazy danych, które
zmieniane byłyby z danymi z innego miejsca, np. jakiegoś źródła danych -
URL. Wtedy w @PostRemove chciałbyś to zamknąć, aby nie powodować
wycieków. Ot, taki pomysł na prędce. Tutaj widzę zastosowanie dla
@PostRemove.

Jacek Laskowski

unread,
Jan 20, 2008, 9:23:11 AM1/20/08
to
Jacek Laskowski wrote:
> Kolszew wrote:
>
>
>> W którym miejscy mam wstawić kod który będzie odpowiadał za to że ta
>> zmiana zostanie odnotowana w jakimś logu (kapownik :)), czy zostaną
>> zaktualizowane odpowiednio inne encje. Naturalnym miejscem jest
>> @PrePersist, z tym że za ch..rę nie wiem jak w tym @PrePersist dobrać
>> się do poprzedniej wartości pola.
>> Trudności z wstrzeleniem innego ziarna umiem już pokonać (JNDI)
>> choć wygląda to na "sztukę".

Pierwsze przyjrzenie się tematowi i czytając specyfikację JPA - 3.5
Entity Listeners and Callback Methods (str. 58):

In general, portable applications should not invoke EntityManager or
Query operations, access other entity instances, or modify relationships
in a lifecycle callback method

i tutaj pojawia się adnotacja:

The semantics of such operations may be standardized in a future release
of this specification.

I dalej napisano:

When invoked from within a Java EE environment, the callback listeners
for an entity share the enterprise naming context of the invoking
component, and the entity callback methods are invoked in the
transaction and security contexts of the calling component at the time
at which the callback method is invoked.

co oznacza, że skoro:

Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.

więc *teoretycznie* można odszukać zarządcę encji w drzewie JNDI, który
właśnie jest wykorzystywany.

Nadmienię, że wykorzystanie adnotacji jest jedynie skrótem dla wykonania
metody lookup z odpowiednim parametrem, więc nawet jeśli adnotacje nie
są wspierane przez metody zwrotne (callback listeners) to i tak cały
kontekst wykonania jest współdzielony, a tym samym i zawartość drzewa JNDI.

Należy pamiętać o tej uwadze na początku:

In general, portable applications should not invoke EntityManager or
Query operations, access other entity instances, or modify relationships
in a lifecycle callback method

Jeśli zaczniemy "mieszać" w stanie encji to dostawca JPA może się
pogubić. Nie wiem jak bardzo są one odporne na ten typ "ataku", więc nie
pozostaje nic innego jak pobadać (i potencjalnie opisać).

Jacek

Jacek Laskowski

unread,
Jan 20, 2008, 4:49:55 PM1/20/08
to
Jacek Laskowski wrote:

> When invoked from within a Java EE environment, the callback listeners
> for an entity share the enterprise naming context of the invoking
> component, and the entity callback methods are invoked in the
> transaction and security contexts of the calling component at the time
> at which the callback method is invoked.
>
> co oznacza, że skoro:
>
> Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
>
> więc *teoretycznie* można odszukać zarządcę encji w drzewie JNDI, który
> właśnie jest wykorzystywany.
>
> Nadmienię, że wykorzystanie adnotacji jest jedynie skrótem dla wykonania
> metody lookup z odpowiednim parametrem, więc nawet jeśli adnotacje nie
> są wspierane przez metody zwrotne (callback listeners) to i tak cały
> kontekst wykonania jest współdzielony, a tym samym i zawartość drzewa JNDI.

(Nie ma to jak rozmawiać z samym sobą)

Po lekturze specyfikacji JPA i EJB stworzyłem niewielką aplikację i ku
mojemu zdumieniu zobaczyłem dwa różne wyniki, które wskazują na Geronimo
2.1 jako poprawniejszą implementację Java EE 5 (!)

W/g specyfikacji EJB 3.0:

16.11.1.2 Programming Interfaces for Persistence Context References

The Bean Provider must use persistence context references to obtain
references to a container-managed entity manager configured for a
persistence unit as follows:

* Assign an entry in the enterprise bean's environment to the
persistence context reference.
* The EJB specification recommends, but does not require, that all
persistence context references be organized in the
java:comp/env/persistence subcontexts of the bean's environment.
* Lookup the container-managed entity manager for the persistence unit
in the enterprise bean's environment using the EJBContext lookup method
or using the JNDI API.

Niestety, ale przy następującej implementacji encyjnej metody zwrotnej
@PostPersist:

@PostPersist
protected void postPersist() {
try {
Context ctx = new InitialContext();
display((Context)ctx.lookup("java:comp/env"));
} catch (Exception e) {
e.printStackTrace();
}
}

// Czy pracujemy z Geronimo czy GlassFish
// Rozna (acz dozwolona) implementacja Context.lookup()
private boolean glassfish = true;

private void display(Context ctx) {
try {
NamingEnumeration<Binding> ne = ctx.listBindings("");
while (ne.hasMore()) {
Binding b = (Binding) ne.next();
// HACK: Rozroznij Geronimo vs GlassFish
String name = b.getName();
if (!name.startsWith("java:comp/env")) {
// Pracujemy z Geronimo
name = "java:comp/env" + "/" + name;
glassfish = false;
} else {
glassfish = true;
}
System.out.println(String.format("%s -> %s", name,
b.getClassName()));
if (b.getObject() instanceof Context) {
if (glassfish) {
display((Context)b.getObject());
} else {
display(name, (Context)b.getObject());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

private void display(String parentContextName, Context ctx) {
try {
NamingEnumeration<Binding> ne = ctx.listBindings("");
while (ne.hasMore()) {
Binding b = (Binding) ne.next();
System.out.println(String.format("%s/%s -> %s",
parentContextName, b.getName(), b.getClassName()));
if (b.getObject() instanceof Context) {
display(parentContextName + "/" + b.getName(),
(Context)b.getObject());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

z Geronimo 2.1 dostaję oczekiwany wynik:

java:comp/env/entitymanager -> java.lang.Object
java:comp/env/pl.jaceklaskowski.beanname.ejb.PolskoAngielskiSlownik ->
org.apache.xbean.naming.context.WritableContext$NestedWritableContext
java:comp/env/pl.jaceklaskowski.beanname.ejb.PolskoAngielskiSlownik/ctx
-> javax.naming.LinkRef
java:comp/env/persistence ->
org.apache.xbean.naming.context.WritableContext$NestedWritableContext
java:comp/env/persistence/InventoryAppMgr -> java.lang.Object

podczas, gdy z GlassFish v2 b58g:

java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean ->
com.sun.enterprise.naming.java.javaURLContext
java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean/angielskoPolskiSlownik
-> $Proxy185
java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean/polskoAngielskiSlownik
-> $Proxy185

Dla pełnego obrazu dopiszę, że wywołujące ziarno EJB miało następującą
adnotację:

@PersistenceContext(name="persistence/InventoryAppMgr",
unitName="ear-ejb-beanname-ejbPU")
@Stateless(name="PolskoAngielskiSlownik")
public class PolskoAngielskiSlownik implements Slownik {

@PersistenceContext(name="entitymanager")
EntityManager em;
....
}

Pozostaje jeszcze sprawdzić, czy obiekt w drzewie JNDI to faktycznie
użyteczny zarządca encji.

(tym razem jednak poczekam na odpowiedź od kogoś innego, bo co tak sam
będę ze sobą rozmawiał ;-))

Jacek Laskowski

unread,
Jan 20, 2008, 5:32:50 PM1/20/08
to
Jacek Laskowski wrote:

> Pozostaje jeszcze sprawdzić, czy obiekt w drzewie JNDI to faktycznie
> użyteczny zarządca encji.

(A co tam na zakończenie niedzielnego rozpoznawania tematu dopiszę sam
sobie wbrew moim wcześniejszym obietnicom, że reszta po reakcji grupy.)

Sprawdziłem i o dziwo GlassFish odmówił współpracy kończąc

java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean ->
com.sun.enterprise.naming.java.javaURLContext
java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean/angielskoPolskiSlownik

-> $Proxy191
java:comp/env/pl.jaceklaskowski.beanname.faces.SlownikBean/polskoAngielskiSlownik
-> $Proxy191
javax.naming.NameNotFoundException: No object bound to name
java:comp/env/persistence/InventoryAppMgr
at
com.sun.enterprise.naming.NamingManagerImpl.lookup(NamingManagerImpl.java:834)
at
com.sun.enterprise.naming.java.javaURLContext.lookup(javaURLContext.java:173)
at
com.sun.enterprise.naming.SerialContext.lookup(SerialContext.java:337)
at javax.naming.InitialContext.lookup(InitialContext.java:351)
at
pl.jaceklaskowski.beanname.entity.Slowo.postPersist(Slowo.java:62)

podczas gdy ta sama aplikacja pod Apache Geronimo 2.1 zwraca, co następuje:

23:25:27,406 INFO [Transaction] TX Required: Started transaction
org.apache.geronimo.transaction.manager.TransactionImpl@110c73b
Slowo ziarno utworzone (za pomoca konstruktora)


java:comp/env/entitymanager -> java.lang.Object
java:comp/env/pl.jaceklaskowski.beanname.ejb.PolskoAngielskiSlownik ->
org.apache.xbean.naming.context.WritableContext$NestedWritableContext
java:comp/env/pl.jaceklaskowski.beanname.ejb.PolskoAngielskiSlownik/ctx
-> javax.naming.LinkRef
java:comp/env/persistence ->
org.apache.xbean.naming.context.WritableContext$NestedWritableContext
java:comp/env/persistence/InventoryAppMgr -> java.lang.Object

Wpis do kapownika o tresci: Zapisano ziarno zapisany
23:25:27,453 INFO [Transaction] TX Required: Committing transaction
org.apache.geronimo.transaction.manager.TransactionImpl@110c73b

Odpowiednie wpisy w ziarnie i encjach to:

@PersistenceContext(name="persistence/InventoryAppMgr",
unitName="ear-ejb-beanname-ejbPU")
@Stateless(name="PolskoAngielskiSlownik")
public class PolskoAngielskiSlownik implements Slownik {

@PersistenceContext(name="entitymanager")
EntityManager em;

@Resource SessionContext ctx;

public String przetlumacz(String slowo) {

((EntityManager)ctx.lookup("persistence/InventoryAppMgr")).persist(new
Slowo(slowo));
return "pl->ang: " + slowo;
}

}

@Entity
public class Slowo implements Serializable {

@PostPersist
protected void postPersist() {
try {

display((Context)new InitialContext().lookup("java:comp/env"));
// Pora na sprawdzenie, czy istnieje możliwość skorzystania
z zarządcy encji
// związanego z ziarnem EJB, z którym jesteśmy związani
// UWAGA: Encja nie musi być związana z żadnym z ziaren
EJB, tj. nic nie będzie w kontekście JNDI
((EntityManager)new
InitialContext().lookup("java:comp/env/persistence/InventoryAppMgr")).persist(new
WpisDoKapownika("Zapisano " + slowo));
} catch (Exception e) {
e.printStackTrace();
}
}
}

oraz

@Entity
public class WpisDoKapownika implements Serializable {

@PostPersist
protected void postPersist() {
System.out.println("Wpis do kapownika o tresci: " + wpis + "
zapisany");
}
}

Ciekawe, nieprawdaż? Może by tak sprawdzić jak to się sprawuje na innych
serwerach aplikacyjnych certyfikowanych na zgodność z Java EE 5. Propozycje?

Kolszew

unread,
Jan 21, 2008, 9:32:17 AM1/21/08
to
> Ciekawe, nieprawdaż? Może by tak sprawdzić jak to się sprawuje na innych
> serwerach aplikacyjnych certyfikowanych na zgodność z Java EE 5.
> Propozycje?
>
> Jacek

Dzięki że poszedłeś na poważnie do tematu.
Jednak ... wiem że nie powinienem narzekać,
zadałem pytanie, Ty tracisz swój czas na sprawdzanie
tego i owego. Jestem wdzięczny.

Niestety dla mnie są to czyste rozważania akademickie,
wynika z nich że:
- metody te są wywoływane w kontekście
- lecz specyfikacja nie zaleca "dobierać" sie do innych encji
- kiedyś to ustandaryzują (co powinno się robić w callback'ach)
- różne serwery działają różnie (JNDI) (pomimo zgodności z EJB3)
no i fajnie (albo nie fajnie).
Tylko jaki jest pomysł bardzo dojrzałej i okrzepłej technologii EJB
na przeniesienie logiki z triggerów z bazy do serwera aplikacji?
Nie ma żadego?! Nie wierzę, pewnie po prostu jestem niedouczony.

Zaczynam baczniej się przyglądać JPA bo to raczej od tej technologii
tego można wymagać, i zaczynam postrzegać to jako zwykły
"buforowany generatorek SQL'a".

Czy w ogóle da się napisać soft
niezależny od bazy danych przy pomocy JPA?

Pozdro
Krzysiek

Waldemar Kot

unread,
Jan 21, 2008, 11:59:11 AM1/21/08
to
Kolszew <WYTNIJTOkrzys...@stream.com.pl> napisał(a):
> Niestety dla mnie są to czyste rozważania akademickie,
> wynika z nich że:
> - kiedyś to ustandaryzują (co powinno się robić w callback'ach)
> - różne serwery działają różnie (JNDI) (pomimo zgodności z EJB3)

Widzisz Krzysiek - to nie do końca jest tak... Po pierwsze EJB jest
specyfikacją i ma wersje. Coś chciano osiągnąć w wersji 1.0, 2.0, ...
Specyfikacje mają tę wadę, że wersje powstają co kilka lat (dzięki Sunowi
przejście z EJB 2.1 na 3.0 zabrało jedyne 6 lat). Po drugie, nietrywialna
specyfikacja, żeby nie wiem jak światli ludzie ją pisali, zawsze czegoś tam
nie wyspecyfikuje. To samo spotkasz w innych standardach - HTTP, TCP/IP. Stąd
różnice między implementacjami. Zawsze były, zawsze będą. Problem EJB polega
na tym, że - inaczej niż w pozostałej części J2EE/JEE - próbuje standaryzować
model programistyczny, a ten jednak wykuwa się w praktyce i deweloperzy muszą
mieć mnóstwo elastyczności, żeby dany model zaakceptować. EJB musiałoby się
zatem rozwijać w tempie nie lat, a góra kwartałów... Dlatego też wokół J2EE -
a w przypadku EJB często zamiast niego - powstało tyle framework'ów. I mimo
rozwoju J2EE sens ich stosowania RAZEM (!) z J2EE będzie zawsze aktualny.
Frameworki upraszczają i uelastyczniają niskopoziomowe API standaryzowane w
ramach J2EE. To chyba zawsze w nietrywialnych aplikacjach będą hybrydy:
framework + standardowa infrastruktura.

Co do @EJB w Entity Listenerach, to mały wywód, który mam nadzieję trochę to
wyjaśni (i doda Ci odrobinę optymizmu ;-))

Sun - w swoim zwyczajowym stylu - narzucił nazwę EJB3 do zbyt wielu rzeczy.
JPA ma naprawdę niewiele wspólnego z EJB3 i stąd konfuzja, której
doświadczasz. Proponuję nazywać "EJB" TYLKO obiekty typu stateless/stateful i
message-driven. I teraz:
@EJB dotyczy wstrzeliwania zależności pomiędzy obiektami EJB. I tylko EJB ! A
entity z JPA nie są obiektami EJB ! To czego doświadczasz, jest jednym z
największych zarzutów pod adresem pomysłu na wstrzeliwanie zależności w
wykonaniu "jak w EJB3". To co wprowadził m.in. Spring, czyli uniezależnienie
się od JNDI, niestety ale NIE ZOSTAŁO wprowadzone do EJB3. @EJB (podobnie jak
i @Resource) zakładają, że wstrzeliwany bean jest w JNDI. Czyli nie jest POJO.
A JPA jest POJO... Łapiesz ? ;-)
Gdyby autorzy JPA (moim zdaniem rozsądnej specyfikacji i jednej z niewielu w
JEE5 wprowadzającej realnie korzystne innowacje), w obecnej postaci EJB3
dopuścili do używania wstrzeliwania poprzez @EJB, to musieliby zrezygnować z
możliwości działania JPA poza kontenerem. A to miałoby zbyt duży wpływ na
filozofię JPA, unit-testowalność entity, itd. Stąd taki "zgniły kompromis" :-)

> Tylko jaki jest pomysł bardzo dojrzałej i okrzepłej technologii EJB
> na przeniesienie logiki z triggerów z bazy do serwera aplikacji?

Zanim przejdę do tego pytania, to najpierw powrócę do pierwszego pytania,
czyli jak wstrzyknąć Session Bean'a w Entity Listenerze. Ci którzy śledzą moje
filozofowanie już znają odpowiedź - użyj Spring-a ;-). Oczywiście inny
kontener DI też da radę, ale jak rozpoznasz pozostałe udogodnienia Spring-a
(typu AOP), to przypuszczam, że przy nim pozostaniesz. Jak to zrobić w Spring-u ?
1. Zarejestrować w konfiguracji Spring-a klasę Entity Listera jako bean (czyli
<bean> xxx </bean>).
2. Zarejestrować w konfiguracji Spring-a klasę Session Beana jako bean, z
wykorzystaniem JndiObjectFactoryBean (czyli tak samo jak w Springu wstrzykuje
się dowolne obiekty z JNDI, w tym EJB)
3. za pomocą DI (via constructor- lub setter- injection) wstrzelić beana
Session Beana do beana Entity Listenera

Fakt, że jeśli masz jeden entity listener, to użycie do tego celu Springa to
może wyglądać na trochę za dużo (bo trzeba jakoś skonfigurować uruchamianie
kontenera Spring, poznać go, a i atuty Springa, czyli model POJO ułatwiający
tworzenie testowalnego kodu mogą nie być zauważalne). Ale przy kolejnych
listenerach będziesz już mógł się cieszyć "eleganckim" rozwiązaniem...

Wracając do kwestii jak przenieść logikę triggerów z bazy do serwera
aplikacji, to oczywiście (Sun strikes back again :) jest to nadużycie, że
zawsze triggery czy stored procedures powinny być w J2EE/JEE zastąpione przez
kod Java w EJB, czy JPA. Na pewno trudniej poprzez beany zachować referencyjną
integralność bazy danych niż poprzez triggery (nie wspominając o relacjach). Z
drugiej strony niektóre triggery działają trochę magicznie i dla lepszej
przejrzystości/modyfikowalności aplikacji nadają się do zastąpienia przez kod
Java. Takich argumentów za i przeciw beanom i SP/triggerom jest wiele... I
jeśli uznasz, że w Twoim przypadku warto przenieść SP/triggery, to tak -
EJB/JPA i/lub Spring/JPA i/lub EJB/Spring/JPA są sposobem, aby w miarę
bezboleśnie to zrobić...

> Zaczynam baczniej się przyglądać JPA bo to raczej od tej technologii
> tego można wymagać, i zaczynam postrzegać to jako zwykły
> "buforowany generatorek SQL'a".
> Czy w ogóle da się napisać soft
> niezależny od bazy danych przy pomocy JPA?

To jest technologia klasy ORM (mapowania obiektów Java na tabele RDBMS) i jej
korzyści i wady dziedziczy. "Generatorek SQL" to brzmi IMHO lekceważąco -
kontakt z bazą danych w ogóle polega na "generowaniu" SQL ;-). Poza mapowaniem
"obiekty na tabele" tego rodzaju technologie są też jednym z pomysłów na
osiągnięcie wysokiej niezależności od bazy danych. Nie niedoceniałbym JPA...
Czy 100% niezależność jest możliwa ? Tak, choć czasem zbyt wysokim kosztem np.
rezygnowania ze specyficznych funkcji bazy danych czy jej wydajności. Takie
podejścia jak podział aplikacji na warstwy, DAO, ORM i inne technologie
persystencji są tutaj zdrowym kompromisem...

Pozdrawiam,
Waldek Kot

--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/

Marx

unread,
Jan 22, 2008, 9:33:23 AM1/22/08
to
Jesli zostawimy trygery w bazie - jak zapewnic aby dane przez nie
tworzone byly widziane po stronie aplikacji?
Marx

Jacek Laskowski

unread,
Jan 22, 2008, 5:01:46 PM1/22/08
to
Marx wrote:
> Jesli zostawimy trygery w bazie - jak zapewnic aby dane przez nie
> tworzone byly widziane po stronie aplikacji?

EM.refresh()

krzysiek

unread,
Jan 22, 2008, 5:40:12 PM1/22/08
to
> EM.refresh()
>

Jacku ... nie zbywaj kolegi czymś takim! :)
Przeca skąd on ma wiedzieć kiedy i dla których encji ma wołać to
EM.refresh()?

Po pierwsze "dla których", trigger w bazie nie musi ograniczać się do
modyfikacji tylko aktualnie zapisywanego wiersza, ba może grzebać w
innych wierszach i do tego w innych tabelach. Skąd po stronie JPA mamy
wiedzieć które wiersze on (ten trigger) zmienił i które z nich są w tej
chwili zarządzane i trzeba je odświeżyć?!

Po drugie "kiedy", magia JPA polega na tym (miedzy innymi) że samo
decyduje kiedy posłać UPDATE, INSERT czy DELETE do bazy, czyli my biedne
żuczki nie mamy pojęcia kiedy tak naprawdę wywoła się trigger i kiedy
ewentualnie można by przystąpić do odświeżania po jego działaniu.
Teoretycznie można zrobić EM.flush(), ale niestety tracimy wtedy zaletę
uruchamiania wszystkich poleceń na końcu w jednej krótkiej transakcji.


Pozdrowienia
Krzysiek

krzysiek

unread,
Jan 22, 2008, 5:42:34 PM1/22/08
to
> Pozdrawiam,
> Waldek Kot

Dzięki.
Trudno mi się ustosunkować.
Musze te wszystkie sprawy przemyśleć.

Pozdrawiam
Krzysiek


Marx

unread,
Jan 25, 2008, 6:04:44 AM1/25/08
to
krzysiek pisze:

> Teoretycznie można zrobić EM.flush(), ale niestety tracimy wtedy zaletę
Czy em.flush robi refresh?
Marx

Marcin 'frodo2000' Molak

unread,
Jan 25, 2008, 7:02:46 AM1/25/08
to
On 21 Sty, 17:59, "Waldemar Kot" <waldemar_...@NOSPAM.gazeta.pl>
wrote:

> Czy 100% niezależność jest możliwa ? Tak, choć czasem zbyt wysokim kosztem np.
> rezygnowania ze specyficznych funkcji bazy danych czy jej wydajności. Takie
> podejścia jak podział aplikacji na warstwy, DAO, ORM i inne technologie
> persystencji są tutaj zdrowym kompromisem...
>
> Pozdrawiam,
> Waldek Kot

Waldku, cieszę się że to napisałeś. Obserwując technologię mapowania
relacyjno-obiektowego coraz częściej dochodzę do wniosku, iż ich
twórcy często dostrzegają jedynie zalety, zapominając o ograniczeniach
platformy (pewnie działy marketingu by mnie zabiły za te słowa).
Dla programisty ta technologia jest zapewne bardzo wygodna - można
zapomnieć o tworzeniu szeregu zapytań SQL, posługując się znanymi nam
klasami Javy. Problem w tym, że bezpośrednie odwoływanie do bazy
danych wymuszało zrozumienie sposobu funkcjonowania serwera danych.
Każdy musiał uwzględnić ich integralność danych, czy poziomy izolacji
(optymistyczne locki nie zawsze są dobrym rozwiązaniem). Teraz mamy
menedżera encji, który robi to za nas.
Tyle że zaczynamy to wykorzystywać nagminnie do wszystkiego a nasze
aplikacje stają się gorzej zoptymalizowane.

Nie jestem przeciwnikiem JPA, choć podobają mi się też takie
rozwiązania jak pureQuery. Ale często wolę się cofnąć do JDBC by mieć
szerszą kontrole nad wszystkim i wywołać chociażby procedurę
składowaną, której plan wykonania baza ma zoptymalizowany.

Pozdrawiam,
Marcin Molak

Waldemar Kot

unread,
Jan 26, 2008, 12:38:02 PM1/26/08
to
Marcin 'frodo2000' Molak <frod...@gmail.com> napisał(a):
> Waldku, cieszę się że to napisałeś. Obserwując technologię mapowania
> relacyjno-obiektowego coraz częściej dochodzę do wniosku, iż ich
> twórcy często dostrzegają jedynie zalety, zapominając o ograniczeniach
> platformy (pewnie działy marketingu by mnie zabiły za te słowa).

Moim zdaniem ciężko powiedzieć, kto tak naprawdę jest "winny". Z jednej strony
autorom technologii (nawet jeśli nie mają działów marktingu za sobą) trudno
jest uniknąć (często niezamierzonego) mówienia o swoim "dziecku" głównie w
pozytywny sposób. I jest to "ludzkie" zachowanie, zwłaszcza jeśli autor jest
pasjonatem. Z drugiej strony, to jednak na użytkownikach tych technologii -
czytaj deweloperach/architektach - spoczywa odpowiedzialność, aby rozumieć za
i przeciw danej technologii czy podejścia w DANYM projekcie (a że NIE MA
idealnych technologii, tak więc KAŻDA technologia/podejście ma i zalety, i
wady). To jest zresztą motywujące, bo oznacza, że mimo ciągłego ulepszania
technologii, super narzędzi, itd. deweloperzy/architekci będą ZAWSZE mieli co
robić i ich szare komórki zawsze będą w cenie :-)

> Dla programisty ta technologia jest zapewne bardzo wygodna - można
> zapomnieć o tworzeniu szeregu zapytań SQL, posługując się znanymi nam
> klasami Javy. Problem w tym, że bezpośrednie odwoływanie do bazy
> danych wymuszało zrozumienie sposobu funkcjonowania serwera danych.
> Każdy musiał uwzględnić ich integralność danych, czy poziomy izolacji
> (optymistyczne locki nie zawsze są dobrym rozwiązaniem). Teraz mamy
> menedżera encji, który robi to za nas.

Tak bym chyba tego nie ujął. Zrozumienie jak działa serwer bazy danych i pojęć
typu integralność, izolacja, itd. to jest absolutny elementarz szanującego się
inżyniera dewelopera i stosowanie ORM nie zwalnia nas z posiadania tej wiedzy.
Podobnie jak to, że mając wysokopoziomowy język programowania musimy jako
deweloperzy rozumieć jak działa procesor, czy logika Boole'a... Mimo to,
dzięki takim technologiom jak ORM wydajność pracy dewelopera jest (zwykle !)
wyższa, bo to jak działa np. menedżer encji jest wystarczająco dobre dla
większości przypadków (kłania się reguła Pareto: 80/20).

> Tyle że zaczynamy to wykorzystywać nagminnie do wszystkiego a nasze
> aplikacje stają się gorzej zoptymalizowane.

No własnie - ale z drugiej strony powinniśmy optymalizować dopiero wtedy, gdy
coś nas 'boli' (np. wydajność jest zbyt niska). Niestety, ale żyjemy w czasach
permanentnego braku czasu i zwykle 'time-to-market' jest najważniejszy. W
kontekście JPA - tu znowu wraca stara dobra reguła Pareto - zwykle (80%, co do
JPA to myślę, że nawet 90%) to jak optymalnie działa Entity Manager jest
wystarczające. Czasem (10-20%) trzeba coś poprawić, albo dać dodatkowe
wskazówki danej implementacji JPA. Im bardziej dojrzałe implementacje (a w
przypadku JPA, to zarówno BEA Kodo/OpenJPA, jak i Oracle TopLink mają po 6-7
lat, więc są raczej dojrzałe) tym mniej trzeba poprawiać w zakresie
optymalizacji...

> Nie jestem przeciwnikiem JPA, choć podobają mi się też takie
> rozwiązania jak pureQuery. Ale często wolę się cofnąć do JDBC by mieć
> szerszą kontrole nad wszystkim i wywołać chociażby procedurę
> składowaną, której plan wykonania baza ma zoptymalizowany.

Oczywiście - pełna zgoda - ja też jestem przekonany, że nietrywialne aplikacje
są hybrydami i zawierają po kilka różnych technologii 'danego rodzaju'. I to w
każdej warstwie - web, service, dostęp do danych... Stąd też tak wysoką ocenę
daję takiemu frameworkowi jak Spring, bo - oprócz przydatnych technologii jak
IoC/DI i AOP - mogę łatwo (i spójnie!) korzystać równocześnie z wielu
frameworków prezentacji, persystencji, czy takiego albo innego języka (Java,
Groovy, JRuby, ...), itd. Czasem wystarcza mi JPA, czasem pasuje mi przesiąść
się na Spring-JDBC. To ostatnie jest cienką nakładką na JDBC eliminującą
główne wady tego API, ale wciąż pracuję na 'żywym' dostępie do JDBC...

Co do pureQuery - nie znałem tego wcześniej, ale po pobieżnym przyjrzeniu się
to lekko się uśmiechnąłem. Wygląda, że jak to często obywa historia złośliwie
zatoczyła koło. pureQuery jako żywo wygląda jak to co BEA promuje od 7 lat,
czyli technologię kontrolek. Technologia kontrolek została przekazana ze 3
lata temu jako projekt open source do Apache - projekt Beehive
(http://beehive.apache.org), choć wciąż nie rozumiem stosunkowo małego nią
zainteresowania. Z tym, że pureQuery ogranicza się do komunikacji z bazą
danych, podczas gdy kontrolki BEA/Apache Beehive są uogólnione na komunikację
z dowolny typem zasobów. Ciekawostki :-) Obie technologie zresztą pasują do
filozofii Springowej i pod DAO w Springu można je także 'ukryć'...

Waldemar Kot

unread,
Jan 26, 2008, 2:38:01 PM1/26/08
to
krzysiek <kols...@wytnijto.o2.pl> napisał(a):

> Jacku ... nie zbywaj kolegi czymś takim! :)
> Przeca skąd on ma wiedzieć kiedy i dla których encji ma wołać to
> EM.refresh()?
> Po pierwsze "dla których", trigger w bazie nie musi ograniczać się do
> modyfikacji tylko aktualnie zapisywanego wiersza, ba może grzebać w
> innych wierszach i do tego w innych tabelach.

Eee - Jacek nie ma w zwyczaju zbywania... I też zgadzam się tu z Jackiem, że
refresh() jest chyba _najprostszym_ rozwiązaniem. Skoro w pytaniu Marxa było,
że trigger pozostaje w bazie, tzn. że ktoś kto pisze kod używający entity
powinien wiedzieć o istnieniu takiego triggera i co/jak on robi. To jest jeden
z tych problemów nad ktorymi trzeba jakoś zapanować, gdy rozdziela się 'logikę
bazodanową' między kilka stron - zapanować poprzez dokumentację, komunikację,
żółte karteczki na ekranie i inne ;-). Jak to mówią Amerykanie 'there is no
free lunch' ;-) Są korzyści, są wady...

> Skąd po stronie JPA mamy
> wiedzieć które wiersze on (ten trigger) zmienił i które z nich są w tej
> chwili zarządzane i trzeba je odświeżyć?!

Na tym polega urok .refresh(), że po prostu idzie do bazy i nadpisuje entity
AKTUALNĄ zawartością z bazy. To które rekordy z bazy zostaną odczytane zależy
od tego CZYM jest dane entity... Jeśli trigger zmienił dane w bazie w
miejscach które danego entity nie dotyczą, to... jakby nie ma to dla TEGO
entity znaczenia.

> Po drugie "kiedy", magia JPA polega na tym (miedzy innymi) że samo
> decyduje kiedy posłać UPDATE, INSERT czy DELETE do bazy, czyli my biedne
> żuczki nie mamy pojęcia kiedy tak naprawdę wywoła się trigger i kiedy
> ewentualnie można by przystąpić do odświeżania po jego działaniu.

To znowu nie tak. Nawet jeśli polegasz na automacie JPA, to JPA specyfikuje
kiedy będzie wykonany update (transakcje, query, itd). Automat daje się
dodatkowo sterować poprzez FlushMode czy też parametry każdej z implementacji
JPA (oba konfiguracyjnie i/lub programowo). Wreszcie sam deweloper może
wymuszać kiedy dany EntityManager ma zapisać zmiany dla zarządzanych przez
siebie beanów. To ostatnie jest właśnie m.in. na takie przypadki, gdy coś się
dzieje poza kontrolą JPA.
Zresztą, nie jest to zresztą specyficzne dla JPA bo i bez JPA (czy nawet ORM)
takie zagadnienia jakie opisujesz mogą wystąpić...

> Teoretycznie można zrobić EM.flush(), ale niestety tracimy wtedy zaletę
> uruchamiania wszystkich poleceń na końcu w jednej krótkiej transakcji.

Prawda, choć problem który podajesz jest jednym z tych dla których i .refresh
i .flush zostały udostępnione. Ale też nie sądzę, aby był to aż tak częsty
problem...

Waldemar Kot

unread,
Jan 26, 2008, 2:55:19 PM1/26/08
to
Marx <ma...@sorry.too.much.spam.write.to.newsgroup> napisał(a):

> Czy em.flush robi refresh?

Nie. Metoda .flush() zapisuje stan entity do bazy ("synchronizuje entity DO
bazy"). Ale jeśli coś zmieniło atrybut entity poza JPA (np. trigger) albo ma
on wartość domyślną w kolumnie (tj. DEFAULT w DDL - zwłaszcza przy tworzeniu
nowych entity może to wystąpić), to wtedy trzeba używać .refresh().

0 new messages