JSF JSF + EclipseLink + GenericDAO Thread Safe?

Todo

Aktives Mitglied
Habe mal eine Frage, da man sowas natürlich nicht im Studium beigebracht bekommt.

Ich möchte bald meine erste "echte" Webapplikation veröffentlichen, leider bin ich mir extrem unsicher ob man das alles so machen kann. Im Studium habe ich zwar schon mit EclipseLink und Tomcat gearbeitet, leider wurde das große Thema Thread Safe nie wirklich behandelt.

Meine GenericDao sieht so aus, etwas von Hibernate Tutorial abgeguckt:
Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

public abstract class GenericDAO<T> implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("WebApplication4PU");
    private EntityManager em;
    private Class<T> entityClass;

    public void beginTransaction() {
        em = emf.createEntityManager();
        em.getTransaction().begin();
    }

    public void commit() {
        em.getTransaction().commit();
    }

    public void rollback() {
        em.getTransaction().rollback();
    }

    public void closeTransaction() {
        em.close();
    }

    public void commitAndCloseTransaction() {
        commit();
        closeTransaction();
    }
    
    public boolean isEntityManagerNull() {
        return (em == null);
    }
    
    public boolean isTransactionActive() {
        return em.getTransaction().isActive();
    }

    public void flush() {
        em.flush();
    }

    public void joinTransaction() {
        em = emf.createEntityManager();
        em.joinTransaction();
    }

    public GenericDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public void save(T entity) {
        em.persist(entity);
    }

    public void delete(T entity) {
        T entityToBeRemoved = em.merge(entity);

        em.remove(entityToBeRemoved);
    }

    public T update(T entity) {
        return em.merge(entity);
    }

    public T find(long entityID) {
        return em.find(entityClass, entityID);
    }

    public T findReferenceOnly(int entityID) {
        return em.getReference(entityClass, entityID);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public List<T> findAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return em.createQuery(cq).getResultList();
    }

    protected Collection<T> findResult(String namedQuery, Map<String, Object> parameters) {
        Collection<T> result = null;

        try {
            Query query = em.createNamedQuery(namedQuery);

            if (parameters != null && !parameters.isEmpty()) {
                populateQueryParameters(query, parameters);
            }

            result = query.getResultList();

        } catch (NoResultException e) {
            System.out.println("No result found for named query: " + namedQuery);
        } catch (Exception e) {
            System.out.println("Error while running query: " + e.getMessage());
            e.printStackTrace();
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    protected T findOneResult(String namedQuery, Map<String, Object> parameters) {
        T result = null;

        try {
            Query query = em.createNamedQuery(namedQuery);

            if (parameters != null && !parameters.isEmpty()) {
                populateQueryParameters(query, parameters);
            }

            result = (T) query.getSingleResult();

        } catch (NoResultException e) {
            System.out.println("No result found for named query: " + namedQuery);
        } catch (Exception e) {
            System.out.println("Error while running query: " + e.getMessage());
            e.printStackTrace();
        }

        return result;
    }

    private void populateQueryParameters(Query query, Map<String, Object> parameters) {
        for (Entry<String, Object> entry : parameters.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
    }
}

Habe mich für viele DAOs entschieden, daher sieht eine DAO so aus:
Java:
public class AccountDAO extends GenericDAO<Account> {

    private static final long serialVersionUID = 1L;

    public AccountDAO() {
        super(Account.class);
    }

    public void accountHinzufuegen(Account account) {
        save(account);
    }

    public Account findAccountByKundennummer(String kundennummer) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("kundennummer", kundennummer);

        return findOneResult(Account.FIND_BY_KUNDENNUMMER, parameters);
    }
}


Die Facaden steuern meine Transaktion, da die nicht JTA sind, sonder Resource-Local.
Eine passende Facade sieht so aus:
Java:
public class AccountFacade implements Serializable {

    private AccountDAO accountDAO = new AccountDAO();
    private static final Logger LOG = FileLogger.getLogger(AccountFacade.class.getName());

public void accountHinzufuegen(Account account) {
        try {
            accountDAO.beginTransaction();
            accountDAO.accountHinzufuegen(account);
            accountDAO.commitAndCloseTransaction();
        } catch (RollbackException re) {
            LOG.log(Level.SEVERE,
                    "Commit schlug fehl", re);
        } catch (PersistenceException pe) {
            LOG.log(Level.SEVERE,
                    "Entity-Manager meldet einen Fehler", pe);
            accountDAO.rollback();
        } catch (Throwable th) {
            LOG.log(Level.SEVERE,
                    "Unerwarteter Fehler", th);
            if (!accountDAO.isEntityManagerNull() && accountDAO.isTransactionActive()) {
                accountDAO.rollback();
            }
        }
    }
}

Ablauf:
Eine JSF Seite arbeitet mit eine ManagedBean, wobei in der ManagedBean eine Entity erstellt wird.
Wenn diese fertig ausgefüllt ist wird eine Aktion ausgeführt. In dieser Aktion-Methode wird dann eine Facade erstellt und das Objekt der Facade übergeben. Die Facade startet eine Transaktion und versucht das Objekt in der Datenbank zu speichern. Alleine klappt das natürlich auch alles wunderbar. Habe aber gelesen, das der EntityManager nicht ThreadSafe ist. Wie testen man sowas, also wie testen man eine Webanwendung auf Thread Safe? VIelen Dank für eure Hilfe.
 
N

nillehammer

Gast
Wenn ich mir Deinen Code so anschaue, holst Du Dir für jede Abfrage einen EntityManager. Das ist richtig so und ist damit threadsafe.

Überlege, ob Du mehr Funktionalität in dein GenericDao packen kannst. Die Methode accountHinzufuegen könnte man so umbauen, dass sie generisch jegliche Art von Entity speichert. Das spart dir etwas Schreibarbeit und Du musst in den Subklassen nicht immer dran denken, beginTransaction aufzurufen. Klassische Methoden, die in einem GenericDao gut aufgehoben wären, sind:
Java:
public E saveEntity(E entity);

public E removeEntity(E entity);

// hier wäre sogar denkbar, statt des Long auch einen generischen
// id-Parameter zu definieren, wobei das nur Sinn macht, wenn
// eine Deiner Entities etwas anderes als Long als id verwendet.
public E findEntityById(Long id);

public List<E> loadAll();
Als weiteren Tipp hätte ich noch, dass Du für Deine DAOs Interfaces definierst und gegen diese programmierst. So ist zumindest das Standardvorgehen, wenn man sich die diversen Dokus und Tuts im Internet anschaut.
 
Zuletzt bearbeitet von einem Moderator:

Todo

Aktives Mitglied
Danke erstmal, werde es mal noch ein wenig verfeinern, hört sich aber soweit logisch und plausibel an.

Vielen Dank erstmal
 

Ähnliche Java Themen

Neue Themen


Oben