JPA facade fails to create (persist) entity -- why?

1.3k views Asked by At

I'm trying to use a Facade class, which extends an abstract facade, to persist an entity with JPA. The database classes involved are:

src/net/bounceme/dur/selenium/jpa/
├── AbstractFacade.java
├── Feed.java
├── FeedJpaController.java
├── LinkFacade.java
├── Link.java
├── LinkJpaController.java
├── PageFacade.java
├── Page.java
└── PageJpaController.java

While AbstractFacade reports success in persisting a Page:

run:
     [java] [EL Info]: 2014-11-17 14:56:29.301--ServerSession(23999191)--EclipseLink, version: Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd
     [java] [EL Info]: connection: 2014-11-17 14:56:30.276--ServerSession(23999191)--file:/home/thufir/NetBeansProjects/SeleniumIterator/build/classes/_SeleniumIteratorPU login successful
     [java] Nov 17, 2014 2:56:55 PM net.bounceme.dur.selenium.jpa.AbstractFacade create
     [java] INFO: ..persisted!
     [java] Nov 17, 2014 2:57:19 PM net.bounceme.dur.selenium.jpa.AbstractFacade create
     [java] INFO: ..persisted!
^Cthufir@dur:~/NetBeansProjects/SeleniumIterator$ 

That success isn't reflected in the database:

mysql> 
mysql> select * from pages;
Empty set (0.00 sec)

mysql> 
mysql> describe pages;
+---------+-----------+------+-----+-------------------+-----------------------------+
| Field   | Type      | Null | Key | Default           | Extra                       |
+---------+-----------+------+-----+-------------------+-----------------------------+
| id      | int(11)   | NO   | PRI | NULL              | auto_increment              |
| created | timestamp | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| page    | text      | NO   |     | NULL              |                             |
| link_id | int(11)   | NO   |     | 0                 |                             |
| status  | int(11)   | NO   |     | 0                 |                             |
+---------+-----------+------+-----+-------------------+-----------------------------+
5 rows in set (0.05 sec)

mysql> 

The SeleniumWebPageIterator class iterates links correctly:

package selenium;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import net.bounceme.dur.selenium.jpa.Link;
import net.bounceme.dur.selenium.jpa.LinkFacade;
import net.bounceme.dur.selenium.jpa.Page;
import net.bounceme.dur.selenium.jpa.PageFacade;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class SeleniumWebPageIterator {

    private final static Logger log = Logger.getLogger(SeleniumWebPageIterator.class.getName());
    private final PageFacade pageFacade = new PageFacade();
    private final LinkFacade linkFacade = new LinkFacade();

    public SeleniumWebPageIterator() {

    }

    public void processLinks() {
        List<Link> links = linkFacade.findAll();
        for (Link l : links) {
            processLink(l);
        }
    }

    private void processLink(Link l) {
        log.fine(l.toString());
        WebDriver driver = new FirefoxDriver();  //don't display
        driver.get(l.getLink());
        driver.manage().timeouts().implicitlyWait(9, TimeUnit.SECONDS);
        String s = driver.getPageSource();
        createPage(l, s);
        driver.close();
    }

    private void createPage(Link l, String s) {
        Page p = new Page();
        p.setCreated(new Date());
        p.setLinkId(l.getId());
        p.setPage(s);
        pageFacade.create(p);  //page has no id..
    }

}

There are links (URL's) in the database, and the webpages are visited by Selenium. The source HTML for the page isn't persisted, however.

The PageFacade is simple:

package net.bounceme.dur.selenium.jpa;

import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class PageFacade extends AbstractFacade {

    private final static Logger log = Logger.getLogger(PageFacade.class.getName());
    private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("SeleniumIteratorPU");
//    @PersistenceContext(unitName = "SeleniumReaderPU")

    public PageFacade() {
        super(Page.class);
    }

    @Override
    protected EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
}

and extends, of course, AbstractFacade:

package net.bounceme.dur.selenium.jpa;

import java.util.List;
import java.util.logging.Logger;
import javax.persistence.EntityManager;

public abstract class AbstractFacade<T> {

    private final static Logger log = Logger.getLogger(AbstractFacade.class.getName());
    private Class<T> entityClass;

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

    protected abstract EntityManager getEntityManager();

    public void create(T entity) {
        getEntityManager().persist(entity);
        log.info("..persisted!");
    }

    public void edit(T entity) {
        getEntityManager().merge(entity);
    }

    public void remove(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
    }

    public T find(Object id) {
        return getEntityManager().find(entityClass, id);
    }

    public List<T> findAll() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<T> findRange(int[] range) {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(range[1] - range[0] + 1);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    public int count() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

}

which uses PageJpaController to actually persist to the database:

package net.bounceme.dur.selenium.jpa;

import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import net.bounceme.dur.selenium.jpa.exceptions.NonexistentEntityException;

public class PageJpaController implements Serializable {

    public PageJpaController(EntityManagerFactory emf) {
        this.emf = emf;
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Page page) {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.persist(page);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Page page) throws NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            page = em.merge(page);
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = page.getId();
                if (findPage(id) == null) {
                    throw new NonexistentEntityException("The page with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Page page;
            try {
                page = em.getReference(Page.class, id);
                page.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The page with id " + id + " no longer exists.", enfe);
            }
            em.remove(page);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Page> findPageEntities() {
        return findPageEntities(true, -1, -1);
    }

    public List<Page> findPageEntities(int maxResults, int firstResult) {
        return findPageEntities(false, maxResults, firstResult);
    }

    private List<Page> findPageEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(Page.class));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Page findPage(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Page.class, id);
        } finally {
            em.close();
        }
    }

    public int getPageCount() {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Page> rt = cq.from(Page.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

}

The code for the <class>JpaController works fine in another project. In fact, these database operations should probably be in their own project as a library, but I don't believe there's a problem with the underlying Page, which was created with "Entity Classes from Database...", nor the similarly created JpaController. I added the facade, as per a similar scenario.

How do I troubleshoot where the persistence goes wrong?

I've checked that the PageJpaController receives a Page, and that the object has fields. yet, for some reason, the database table for Page remains unaffected. I'm not sure how to troubleshoot further.

1

There are 1 answers

2
wypieprz On

According to the log you are calling:

public void create(T entity) {
    getEntityManager().persist(entity);
    log.info("..persisted!");
}

This means:

  • application-managed entity manager is used
  • persist operation is accepted by the entity manager
  • the entity becomes managed by the persistence context (in-memory structure)

However this does not mean that the entity is written to the database. If a new transaction begins the persistence context becomes synchronized with the transaction. In order to synchronize the persistence context (managed entities) with the database you would need to invoke flush or commit. It looks like PageJpaController.create does this properly but is it invoked? I guess it's not.

em = getEntityManager();
em.getTransaction().begin();  //synchronizes the persistence context with TX
em.persist(page);             //adds page to the persistence context
em.getTransaction().commit(); //flushes persistence context to the database