Stateless bean, Singleton, JPA nem ment

Fórumok

Van egy Stateless beanem 

@Stateless
public class IdsMB extends BaseMBData implements Serializable {

public int getId(String kodtar) {
    String SQL = "SELECT i FROM Ids AS i WHERE i.id=...
    EntityManager entityManager = getEntityManagerFactory().createEntityManager();
    try {
        entityManager.getTransaction().begin();
        ids = entityManager.createQuery(SELECT_KODTARAK).getResultList();
        ......
        id = entityManager.merge(id);
        entityManager.getTransaction().commit();
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
        try {
            entityManager.getTransaction().rollback();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
    entityManager.close();
    return id.getSzam();
}

Ez teljesen jól működik.

Amint hívásra kerül egy @Singleton beanben

@ManagedBean(name = "valami")
@ApplicationScoped
@Singleton
public class valami

public void status(){
   IdsMB idsMB = new IdsMB();
   idsMB.getId();
}

 

A singleton-on belül látszólag jól működik, nem dob kivételt mentéskor, a felette lévő select is jó adatot olvas mikor újra rá kerül a vezérlés, de az adatbázisban semmi nincs meg ebből?

Mi lehet ennek az oka?

Hozzászólások

Ebből melyik rész írna az adatbázisba?

ket getId fv-ed van... :-)

 

egy parameter nelkuli es egy parameteres...

Minek bonyolítod? A konténer tranzakciókezelése nem elég? Miért nem injektálod az EM-et? Valahol egy em.flush() hiányzik szerintem, vagy explicit flush mode beállítás, ami gondolom AUTO.

Eredetileg így lett elkezdve, és ha bárhol a kódban van egy konténer tranzakciókezelésre vonatkozó annotáció, akkor a többi nem működik, kivétel keletkezik. Nem találtunk megoldást a párhuzamos használatra, de ha lehet és valaki tudja hogyan kell ne tartsa magában. Akarjuk refaktorálni a kódot, de így hogy egyszerre kell elég nehéz időzíteni.

Egyébként a működés a következő. Van egy "A" been @ManagedBean(name = "A"), @ApplicationScoped, @Singleton annotációkkal.

Ebben van több metódus, az egyik @Schedule(hour = "*", minute = "*/15") annotációval megjelölve szóval ezt a GlassFish 15 percenként meghívja. (Lehet ez lesz a kulcs a problémához)

Ebben az időzített metódusban az osztály saját metódusai vannak hívva ahol a metódusokban van 

EntityManager em = Persistence.createEntityManagerFactory(Constants.PERSISTENCE_UNIT_NAME).createEntityManager();

egy egy entitymanager példány és a metódus végén le is van zárva. A metódus valamilyen entitást mert em.merge().

 

Illetve van 

IdsMB

bean amely szintén stateless szintén a fenti módon kér entitymanaer-t és le is van zárva. 

Az az A osztályban példányosítva van és nem @Injectálva. 

100-200 alkalommal fut meg ez az egész "folyamat" minden 15 percben. GF újraindítás az első időzített futtatásnál, 60-70 alkalommal jó, mind az IdsMB, min az A rendesen menti az adatokat, de utána sem a IdsMB sem az A osztály nem menti az adatokat az adatbázisba

Ez gyanúsan olyan, hogy valami erőforrás elfogy a pool-ból. Elindul az időzített folyamat amúgy, amikor már nem menti le? Mert lehet, hogy vár arra, hogy kapjon erőforrást (adatbázis kapcsolatot) és mivel nincs timeout definiálva, a következő schedule időpontban már nem indul el, mert még az előző fut.

Igen elindul. Nem vár semmire. Egy REST kliens hívogat és a visszakapott adatokat kellene beírni az adatbázisba. 

Az elején jól megy minden ez eső időzített futtatás alatt, majd az IdsMB borul meg látszólag, már kiosztott sorszámokat kezd el osztogatni, amit küldeni kell a REST szerviznek ami meg visszaüzen, hogy hát ilyen sorszámmal már küldtünk neki kérést.

De ettől függetlenül nem áll le a folyamat csak valami miatt, gondolom a IdsMB nem menti vissza a sorszámot, viszont minden egyes sorszám generálása előtt bekérdezek az adatbázisba mi volt az utolsó és ott meg úgy tűnik vissza írta az adatbázisba. Ha közben megállítom a program futását és bekérdezek az adatbázisba akkor ott sem látom hogy kiírná. Atán egyszer mintha csak  az IdsMB-ben lévő select a ténylegesen benne lévő adatokat kezdi el felolvasni kvázi elkezd már osztani kiosztott számokat.

De ezt csak ebből az időzített @Schedule annotáióval ellátott metódusból kiindulva produkálja. Ha webes felületen bejelentkeznek ott is több helyen van használva ott még nem vettük észre, hogy ilyet csinálna. 

Elkezdtem ebben a singleton beanben egy időzített @Schedule metódussal annyit csinálni hogy ezerszer lekértem az IdsMB.getId() metódusát. Ebben van flush és commit is már, Egy darabig miután végi futott az időzített függvény visszaírta az adatbázisba az adatot. De csak a végén! Hiába néztem közben mysql consolból az érték nem változott folyamatosan csak az utolsó bejegyzés mikor véget ért. De a harmadik  kör után már a függvény lefutása után sem írta be. Majd a sokadik futás után kaptam egy ilyen exception

Internal Exception: java.sql.SQLException: Error in allocating a connection. Cause: java.lang.RuntimeException: Got exception during XAResource.start:
Error Code: 0
Call: SELECT ID, SZAM FROM IDS WHERE (ID = ?)

majd leállítottam kézzel a GF-t és az utolsó és egyben helyes értéket látom az adatbázisban.

Ez miért lehet vagy mit csinálok rosszul?

Hehe: "Ez gyanúsan olyan, hogy valami erőforrás elfogy a pool-ból."

Ebben van flush és commit is már, Egy darabig miután végi futott az időzített függvény visszaírta az adatbázisba az adatot. De csak a végén!

Úgy érted, hogy a commit() után?

De a harmadik  kör után már a függvény lefutása után sem írta be.

Gondolom a tranzakciók várni kezdtek egymásra. Milyen tranzakciós szintet használsz?

Ez miért lehet vagy mit csinálok rosszul?

Kézzel kérsz el EM-et és azon át JDBC kapcsolatot, majd ezekkel kezelsz tranzakciókat ötletszerűen, aztán a spagettikód mélyén valahol összeakadnak a dolgok. Általános design pattern.

Kézzel kérsz el EM-et és azon át JDBC kapcsolatot, majd ezekkel kezelsz tranzakciókat ötletszerűen, aztán a spagettikód mélyén valahol összeakadnak a dolgok. Általános design pattern.

 

De ha kigyepálom a spagetti kódot, tehát csak annyit hagyok ennek Singleton beannek a @Schedule metódusában, hogy kézzel elkérek egy EntityManagert, majd ötvenezerszer lekérem az adatbázisból az adatot az @Entity -be,

transaction().begin

módosítóm,

visszaírom (merge),

flush(),

commit

majd ha ez lement 50 ezerszer akkor lezárom az entity managert, akkor nem kellene közben mondjuk egy mysql consolban látnom az értékek változását?

Mert most csak a végén látom (bár néha még így sem megy be az adat és a következő futásnál mintha meg sem történt volna az előző) 

Mert most csak a végén látom (bár néha még így sem megy be az adat és a következő futásnál mintha meg sem történt volna az előző) 

Azt kell megkeresned, hogy hol kezdődik és van a tranzakció vége, mert ott fog kiderülni, hogy miért nem kerül be az adatbázisba és ott fog kiderülni, hogy amit olvasol, az milyen tranzakcióban van, mennyire van párhuzamosítva.

> majd ha ez lement 50 ezerszer akkor lezárom az entity managert, akkor nem kellene közben mondjuk egy mysql consolban látnom az értékek változását?

Mar mitol kellene latnod? A tranzakciokezeles pont arrol szol, hogy a tranzakcio lezartaig nem perzisztalunk. Same connection-bol esetleg lathatnad az ertekek modosulasat, de az, hogy van az alkalmazas, meg melle nyitsz egy MySQL console-t... Kicsit nezd at ujra az adatbazis tranzakciok definicojat.

A masik, amin kicsit megutkoztem, hogy a getId() adatbazist ir. Egy get fuggveny. Koca koder vagyok, plane Java teren, de meg en is tudom, hogy van egy olyan iratlan szabaly, hogy get fuggveny maximum audit trailt irhat (esetleg), azt sem onmaga. A get olvas. A saveValami, storeValami, writeValami fuggvenyek irhatnak. Tudom, hogy sokfajta business igeny van, de amikor egy getId -nek tranzakciot kell kezelnie, ott valaki valamit nagyon elrontott. 

En a helyedben megmondom oszinten ugy, ahogy van, nyom nelkul kitorolnem ezt a metodust, es nullarol ujrairnam a quirkek, workaroundok, egyebek nelkul. Mit kell ennek tudni? Egy objektumot visszaadni. Injektalsz egy EM-et, es

TypedQuery<Kodtar> = em.createQuery("SELECT * FROM kodtarak where kodtar.id = " + id, Kodtar.class); Kodtar kt = em.getFirstResult();

Aztan innen ujra kiindulni. Eleve, a kod anonimizalasa soran teljesen inkonzisztens peldakodot hoztal, keptelenseg belole kitalalni, hogy mi mire hivatkozik, raadsul nincs is lezarva a query-nel az idezojel. Es abban igaza van _Franko_ -nak, hogy a leheto legritkabb esetben jo otlet alanyulni az ORM-nek es kezzel futtatgatni SQL-eket, plane custom tranzakciokezelest implementalni egy, a tranzakciokezelest amugy tamogato konyvtarnal. Gyere ra, hogy hol rontod el, mert valahol elrontod, ez tuti.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

Ebben a kicsinyke kódban rengeteg potenciális probléma rejtőzik. Ajánlom, hogy nézesd át a teljes kódod egy profival, és fogadd meg a tanácsait.

Jelen esetben szerintem az a gondod, hogy kevered a konténer-vezérelt tranzakciókat a kézi vezéreltekkel. Ez a problémák melegágya. Javaslom, maradj inkább a konténer által kezelt tranzakcióknál. Ha érdekel, privátban el tudok küldeni neked egy könyvet, ami eléggé alaposan és érthetően elmagyarázza, melyiket hogyan kell jól csinálni, mik a buktatók, stb. (amúgy ingyenes az InfoQ-n, Mark Richards: Java Transaction Design Strategies)

Ha mindenáron új tranzakcióra van szükség egy adott művelet végrehajtásánál, akkor a RequiresNew lesz a barátod.

Szintén nagyon fontos tudni, hogy a catch (Exception e) rohadt veszélyes: alapértelmezetten ugyanis a RuntimeException-ök esetén a tranzakció automatikusan rolllback-elve lesz, a többinél nem. ha továbbengeded a kódot ilyenkor, okozhat meglepetéseket.