【问题标题】:JPA facade fails to create (persist) entity -- why?JPA 外观无法创建(持久)实体——为什么?
【发布时间】:2015-01-15 00:31:04
【问题描述】:

我是trying to use a Facade class,它扩展了一个抽象外观,用 JPA 持久化一个实体。涉及的数据库类有:

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

虽然AbstractFacade 报告成功持久化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$ 

成功并没有反映在数据库中:

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> 

SeleniumWebPageIterator 类正确地迭代链接:

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..
    }

}

数据库中有链接(URL),Selenium 访问了这些网页。但是,页面的源 HTML 不会被保留。

PageFacade 很简单:

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();
    }
}

当然还有扩展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();
    }

}

使用PageJpaController 实际持久化到数据库:

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();
        }
    }

}

&lt;class&gt;JpaController 的代码在另一个项目中运行良好。事实上,这些数据库操作应该是作为库在自己的项目中进行的,但我不认为底层的Page有问题,它是用“Entity Classes from Database...”创建的,同样创建的JpaController也没有问题。我根据a similar scenario添加了外观。

如何解决持久性出错的问题?

我检查了PageJpaController 接收到Page,并且该对象具有字段。然而,出于某种原因,Page 的数据库表仍然不受影响。我不确定如何进一步排除故障。

【问题讨论】:

    标签: jpa orm persistence facade


    【解决方案1】:

    根据你调用的日志:

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

    这意味着:

    • 使用应用程序管理的实体管理器
    • 实体管理器接受持久化操作
    • 实体由持久化上下文(内存结构)管理

    但这并不意味着实体已写入数据库。如果新事务开始,持久性上下文将与事务同步。为了将持久性上下文(托管实体)与数据库同步,您需要调用flushcommit。看起来PageJpaController.create 正确执行了此操作,但它被调用了吗?估计不是。

    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
    

    【讨论】:

    • 我明白你的意思,但不知道如何从AbstractFacade 调用PageJpaController.create。方法是覆盖方法吗?如果所有方法都被覆盖,那将使抽象外观更像一个接口,而不是抽象类。我不明白如何在这个抽象类中使用泛型,以便调用 intended 控制器及其方法。
    • 我没有使用 Selenium 的经验,但对于 façade 模式,我认为您的 AbstractFacade&lt;T&gt; 应该接管 JPAController 的功能,使其方法抽象并最终在 PageFacade 或 @987654331 中覆盖它们@ 类,如果需要的话。然后通过在SeleniumWebPageIterator 中调用外观方法,您可以简化对 JPA 的访问。
    猜你喜欢
    • 2017-09-26
    • 2021-03-24
    • 1970-01-01
    • 2018-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    相关资源
    最近更新 更多