EJB 3 i transakcje

0

Witam.

Robię sobie prostą webową aplikacje z EJB i mam problem z transakcjami.

Mam sobie takiego EJB

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class PlayerDao {
    
    @PersistenceContext(unitName="prodPU")
    private EntityManager em;

    @Resource(mappedName="mail/fastom")
    private Session mailSes;

    @EJB
    private Configuration conf;

    /**
     *
     * @param email
     * @param password
     * @return registered player
     * @throws EntityExistsException - when a user with the passed email
     * already exists
     */
    public Player registerPlayer(String email, String password)
        throws EntityExistsException {
        
        Query q = em.createNamedQuery("findPlayerByEmail");
        q.setParameter("email", email);
        List r = q.getResultList();
        if(r.size() != 0) {
            throw new EntityExistsException(conf.getGlobalResourceBundle()
                .getString("register.mail.exists.error"));
        }

        Player p = new Player();
        //...
        em.persist(p);

        return p;
    }

    public Player registerPlayerWithActivation(String email, String password,
        String urlPreffix) throws EntityExistsException,
        AddressException, MessagingException {

        Player p = registerPlayer(email, password);
        p.setActivationToken(new Random().nextInt());
        em.persist(p);

        sendActivationEmail(p, urlPreffix); // tu dostaje exception

        return p;
    }

i wykorzystuje w servlecie w ten sposób:

@WebServlet(urlPatterns={"/register.html"})
public class RegisterServlet extends HttpServlet {

    @EJB
    private PlayerDao playerDao;

    @Override
    protected void doPost(
        HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        //...

        try {
            try {
                URL serverUrl = new URL(request.getRequestURL().toString());
                Player p = playerDao.registerPlayerWithActivation(
                    request.getParameter("email"),
                    request.getParameter("password"),
                    "http://" + serverUrl.getHost() + "/activation.html?token=");
                //...
            } catch (EJBException ex) {
                throw ex.getCausedByException();
            }
        } catch 
        //...
    }
}

Jak widać w komentarzach dostaję wyjątek przy sendActivationEmail i to jest ok.
Problem w tym że mimo tego wyjątku transakcja nie jest rollback'owana i Player trafia do bazy danych.

Proszę o sugestie co mogę robić nie tak ?

Soft: Glassfish v3 + JavaDB (derby) + Netbeans

Mój persistance.xml:

<persistence-unit name="prodPU" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/__default</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables" /> </properties> </persistence-unit>

W panelu admin Glassfish włączyłem wsparcie dla transakcji dla jdbc/__default

0

A dlaczego ma być rollbackowana?

Dodajesz gracza. Robisz persist() - TU KOŃCZY SIĘ TRANSAKCJA!!! potem wysyłasz email. Należy na odwrót. Najpierw wysłać email i potem zapisać do bazy. Względnie ręcznie zarządzać transakcjami.

0

Koziolek wez ty sie juz nie wypowiadaj na tematy na ktorych sie nie znasz. Od kiedy persists() konczy transakcje?

@Walec: uzywasz domysnego ustawienia transakcji dla metody, ktora jest REQUIRES_NEW. Metoda registerPlayerWithActivation() zaczyna swoja, i wola registerPlayer() - to powoduje zawieszenie aktywnej transakcji, i utworzenie nowej, niezaleznej, na czas trwania metody registerPlayer(). Tam wszystko jest ok i aktywna transakcja JTA sie commituje, stad w bazie masz wpis. Teraz wracasz do registePlayerWithActivation(), i stara transakcja sie aktywuje ponownie, tam dostajesz wyjatek i masz rollback. Czyli problem jest ze masz 2 osobne transakcje, a nie dlatego ze robisz cos przed czyms innym. Metoda registerPlayer() powinna miec REQUIRED - jesli bedzie wolana bezposrednio, stworzy sobie nowa, jesli wolana z metody ktora ma juz transakcje, dolaczy sie do niej.

0
::. napisał(a)

uzywasz domysnego ustawienia transakcji dla metody, ktora jest REQUIRES_NEW. Metoda registerPlayerWithActivation() zaczyna swoja, i wola registerPlayer() - to powoduje zawieszenie aktywnej transakcji, i utworzenie nowej, niezaleznej, na czas trwania metody registerPlayer(). Tam wszystko jest ok i aktywna transakcja JTA sie commituje, stad w bazie masz wpis. Teraz wracasz do registePlayerWithActivation(), i stara transakcja sie aktywuje ponownie, tam dostajesz wyjatek i masz rollback. Czyli problem jest ze masz 2 osobne transakcje, a nie dlatego ze robisz cos przed czyms innym. Metoda registerPlayer() powinna miec REQUIRED - jesli bedzie wolana bezposrednio, stworzy sobie nowa, jesli wolana z metody ktora ma juz transakcje, dolaczy sie do niej.

Dzięki za podpowiedź ale z tego co widzę to nie wszystko.

Dodałem adnotację:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Player registerPlayer(String email, String password)

i mam nadal to samo. Ale dostałem cynka na usenecie że to czy będzie rollback to jeszcze zależy od typu wyjątku.

Jaroslaw Szczepankiewicz: poczytać więcej o wyjątkach aplikacji (wyjątkach definiowanych przez
bean developera), domyślnie wyjątki auto rollbackowane to tylko wyjątki
systemowe typu Runtime, wyjątki usera są domyślnie nie rollbackowane
gdyż domniemuje się że jeżeli uzywa się checked exception to po to żeby
klient mógł wykonać akcje zapobiegawcze (mające na celu zachowanie
spójności tranzakcji), aby były rollbackowane musi być jawne wskazanie
przez developera że wyjątek jest rollbackowany (np. przez odpowiednią
adnotacje w klasie wyjątku)

Ktoś wie może jak w Glassfishu ustawić żeby każdy wyjątek dawał rollback'a na transakcji ?

Koziołek napisał(a)

Robisz persist() - TU KOŃCZY SIĘ TRANSAKCJA!!!

Chyba za bardzo się do opcji AUTO_COMMIT ktoś przyzwyczaił :P

0

EntityExistException daje rollback -> http://java.sun.com/javaee/5/docs/api/javax/persistence/EntityExistsException.html.
Nie mozna ustawic zeby wszystkie dawaly rollback. Ogolnie to jest tak: runtime exception daja rollback, checked nie daja rollbacka bo sa domyslnie uznawane za takie ktore nie sa krytyczne i pozwalaja poprawic costam i probowac ponownie w tej samej transakcji. Mozna to zmieniac za pomoca adnotacji http://java.sun.com/javaee/5/docs/api/javax/ejb/ApplicationException.html. Jesli nie masz kodu wyjatku (jak w przypadku EntityExistsException) to mozesz to zrobic w ejb-jar.xml.
No chyba ze ten wyjatek ktory rzucasz to nie jest ten z biblioteki standardowej?

0

Dzięki za pomoc. Trochę pogrzebałem w dokumentacji deskryptora wdrożenia i nawet udało mi się zrobić aby wszystkie wyjątki dawały rollback :)

Dodałem do ejb-jar.xml taki wpis:

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee" 
         version = "3.1"
         xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">

      <assembly-descriptor>
          <application-exception>
              <exception-class>java.lang.Exception</exception-class>
              <rollback>true</rollback>
              <inherited>true</inherited>
          </application-exception>
      </assembly-descriptor>
</ejb-jar>

Nie wiem czy to jest przenośne zachowanie ale przynajmniej działa na Glassfishu

0

Czegos nowego mnie nauczyles, dzieki ;d
A mozesz powiedziec czy ten Twoj EntityExistsException to byl z API Javy EE czy jakis Twoj wlasny? Bo jesli z Javy EE, to dziwi mnie ze musisz specjalnie ustawiac zeby dawal rollback.

0
::. napisał(a)

A mozesz powiedziec czy ten Twoj EntityExistsException to byl z API Javy EE czy jakis Twoj wlasny? Bo jesli z Javy EE, to dziwi mnie ze musisz specjalnie ustawiac zeby dawal rollback.

EntityExistsException dawał rollback. Problem miałem z MessagingException. Nie doszedł mail do użytkownika nie ma co zatwierdzać rejestracji bo jej nigdy nie aktywuje.

Generalnie w moim systemie nie chce mieć logiki biznesowej nad fasadą EJB'ków dlatego też chce aby każdy nie obsłużony wyjątek w metodzie biznesowej by traktowany jako unrecoverable. W ten sposób w kliencie webowym mogę tylko przejmować się widokiem i interakcją z użytkownikiem.

0

Aaa racja racja. Teraz rozumiem. No nic, dzieki ze nauke ;d

1 użytkowników online, w tym zalogowanych: 0, gości: 1