【问题标题】:JPA not loading collections with inheritanceJPA 不使用继承加载集合
【发布时间】:2015-07-10 09:08:07
【问题描述】:

我有以下问题: 当我加载“HtmlPage”对象时,集合“标签”的大小始终为 0。集合“参数”工作正常。该数据库也有正确的(我猜)条目。

这是我的课程:

@IdClass(HtmlPageID.class)
@Entity
public class HtmlPage implements Serializable {


    /**
     * The parameters of this HTML page. (e.g. www.google.com/#q=test&tbas=0
     * will result in: q=test; tbase=0)
     */
    @OneToMany(mappedBy = "page", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private LinkedList<HttpParam> parameter;
    /**
     * A Collection of all  HTML tags.
     */
    @OneToMany(mappedBy = "htmlPage", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<WebsiteTag> tags;

}

@IdClass(WebsiteTagID.class)
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class WebsiteTag implements Serializable {


    /**
     * The name of the tag (e.g. div for the DIV tag).
     */
    @Id
    @Column(length = 2048)
    protected String tagName;
    /**
     * The associated HTML page.
     */
    @Id
    @ManyToOne
    protected HtmlPage htmlPage;
}

如果我在抽象类中使用“@MappedSuperclass”(我应该这样做),我会得到以下异常: 主键类[null]和实体bean类[class website.WebsiteTag]中的主键字段或属性的名称必须对应,并且它们的类型必须相同。此外,请确保您已为 XML 中的相应属性指定 ID 元素和/或在实体类的相应字段或属性上指定 @Id。

public class WebsiteTagID implements Serializable {

    /**
     * The name of the tag (e.g. div for the DIV tag).
     */
    @Id
    @Column(length = 2048)
    protected String tagName;
    /**
     * The associated HTML page.
     */
    @Id
    @ManyToOne
    protected HtmlPageID htmlPage; }

以下是我数据库中的一些条目:

HTMLPAGE

完整路径 |状态 |网址 /s/ref=nb_sb_noss/field-keywords=smartphone |感染 | www.amazon.com

网站标签

[EMPTY] 我猜应该不是这样吧??

IFRMAETAG

标签名 |状态 |完整路径 |网址
空 0.765217383991997 |感染 | /s/ref=nb_sb_noss/field-keywords=smartphone | www.amazon.com


这是一个 JPA 查询。如果我执行此操作,我会得到我正在寻找的结果。但它没有添加到集合中。

    [EL Finest]: query: 2015-04-30 16:28:57.834--ServerSession(23649981)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(name="tags" referenceClass=WebsiteTag sql="SELECT TAGNAME, STATUS, COMPLETEPATH, URL FROM WEBSITETAG WHERE ((URL = ?) AND ((STATUS = ?) AND (COMPLETEPATH = ?)))")
    [EL Finest]: query: 2015-04-30 16:28:57.844--ServerSession(23649981)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(name="tags" referenceClass=WebsiteTag )
    [EL Finest]: connection: 2015-04-30 16:28:57.844--ServerSession(23649981)--Connection(5106948)--Thread(Thread[main,5,main])--Connection acquired from connection pool [default].
    [EL Fine]: sql: 2015-04-30 16:28:57.844--ServerSession(23649981)--Connection(5106948)--Thread(Thread[main,5,main])--SELECT TAGNAME, HREF, INLINKS, ISEXTERNAL, ISJAVASCRIPT, STATUS, COMPLETEPATH, URL FROM HYPERLINKTAG WHERE ((URL = ?) AND ((COMPLETEPATH = ?) AND (STATUS = ?)))
        bind => [www.amazon.com, INFECTED, /s/ref=nb_sb_noss/field-keywords=smartphone]

    [EL Finest]: connection: 2015-04-30 16:28:57.854--ServerSession(23649981)--Connection(5106948)--Thread(Thread[main,5,main])--Connection released to connection pool [default].
    [EL Finest]: query: 2015-04-30 16:28:57.854--ServerSession(23649981)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(name="tags" referenceClass=WebsiteTag )
    [EL Finest]: connection: 2015-04-30 16:28:57.854--ServerSession(23649981)--Connection(5106948)--Thread(Thread[main,5,main])--Connection acquired from connection pool [default].
    [EL Fine]: sql: 2015-04-30 16:28:57.864--ServerSession(23649981)--Connection(5106948)--Thread(Thread[main,5,main])--SELECT TAGNAME, HEIGHT, HEIGHTINP, INLINKS, ISEXTERNAL, ISJAVASCRIPT, SRC, WIDTH, WIDTHINP, STATUS, COMPLETEPATH, URL FROM IFRAMETAG WHERE ((URL
= ?) AND ((COMPLETEPATH = ?) AND (STATUS = ?)))
        bind => [www.amazon.com, INFECTED, /s/ref=nb_sb_noss/field-keywords=smartphone]

我真的不知道如何解决这个问题。提前致谢!

编辑 1

这里有一些我如何管理我的收藏的示例代码:

  public HtmlPage extracHtmlpage(Webpage page, String path, String htmlCode) throws MalformedURLException, UnsupportedEncodingException {
        HtmlPage htmlPage = new HtmlPage(page, path, null, null)

        /*  Extract the GET Params */
        // If there is one '=' we know that there is at least one GET parameter with a value.
        if (path.contains("=")) {
            htmlPage.setParameter(this.extractGetParams(htmlPage, path));
        }
        /* Handle all the HTML tags.*/
        htmlPage.setTags(this.extractHtmlElements(htmlPage, htmlCode));
        htmlPage.setCompletePath(new URL(corrPath).getPath());
        return (htmlPage);
    }

private List<WebsiteTag> extractHtmlElements(HtmlPage htmlPage, String htmlCode) {
    List<WebsiteTag> tags = new LinkedList<>();
    LinkedList<HyperLinkTag> links = new LinkedList<>();
    LinkedList<IFrameTag> iFrames = new LinkedList<>();
    //Loop over all the html elements
    Document doc = Jsoup.parse(htmlCode);
    /*Handle links*/
    Elements rawLinks = doc.select("a");
    for (Element link : rawLinks) {
        HyperLinkTag ref = new HyperLinkTag(link.attr("href"), htmlPage);
           links.add(ref);

    }
    tags.addAll(links);
    /*Handle iframes*/
    Elements rawIFrames = doc.select("iframe");
    for (Element iFrame : rawIFrames) {
        IFrameTag tag = this.extractIFrameTag(iFrame, htmlPage);
        iFrames.add(tag);
   }
    tags.addAll(iFrames);
    return (tags);
}

最后:

private IFrameTag extractIFrameTag(Element iFrame, HtmlPage page) {
        String width = iFrame.attr("width").replace("%", "").replace("px", "");
        boolean wP = iFrame.attr("width").contains("%");
        String height = iFrame.attr("height").replace("%", "").replace("px", "");
        boolean hP = iFrame.attr("height").contains("%");
        String src = iFrame.attr("src");

        IFrameTag tag = new IFrameTag(new Integer(width), wP,
                new Integer(height),hP, src, page);
        return (tag);
}

我看不出这在什么时候会不一致。

编辑 2

我的 DAO 代码非常简单

public void commitWebpage(HtmlPage page) {
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        em.persist(page);
        tx.commit();
}

public void updateHtmlPage(HtmlPage page) {
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    em.merge(page);
    tx.commit();    
}

我这样坚持/更新:

   HtmlPage webpage = this.extracHtmlpage(page, path, htmlCode)
   insertData(webpage);

插入代码

public void insertData(HtmlPage website) {
    WebpageDAO dao = new WebpageDAO();
    if (dao.getWebpage(website) == null) {
        dao.commitWebpage(website);
    } else {
        dao.updateHtmlPage(website);
    }
}

不工作的是:

EntityManagerFactory emf =    Persistence.createEntityManagerFactory("TestDataPU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query query = em.createQuery("SELECT w FROM Webpage w");
List<Webpage> results = (List<Webpage>) query.getResultList();
tx.commit();

在“结果”中,我得到了我的 HtmlPages(是的!)集合“参数”在那里并且具有正确的值。但是集合“标签”的大小为 0(呃!)。当我坚持它时,HTMLPages 有“标签”。

【问题讨论】:

  • 您不能将 WebsiteTag 标记为 mappedSuperclass,然后使用 OneToMany 映射到它,因为 mappedSuperclass 不是实体。从 JPA 的角度来看,它只是由完全不相关的子类继承的一堆映射。如果要映射到它,它需要是一个实体及其通过JPA继承关联的子类。
  • 另外,映射集合不能是 LinkedList 类型。必须使用接口:List。

标签: java jpa inheritance


【解决方案1】:

需要更多信息才能真正说明问题所在,但我认为您在某些关键点上做出了一些错误假设,这些假设可能导致您预期不同(但错误)的行为。

首先,mappedsuperclass 只是一种将一些常见的映射映射到父级以便它们可以重用的方法。从映射超类扩展的任何实体实际上彼此之间没有关系(从 JPA/数据库的角度来看),因此确实没有办法映射到映射超类;它只是不是一个实体。

现在到 table-per-class 继承。这意味着继承层次结构中的每个实体都有自己的表,其中包含该类的实例存在所需的所有字段。 WEBSITETAG 本身,因为 WebsiteTag 是抽象的,它不应该有任何数据 - 甚至不需要存在。 WebsiteTag 的任何子类都将拥有自己的表,其中包含与 WEBSITETAG 相同的字段和它们可能扩展的任何父实体类,以及它们可能具有的其他映射的任何字段。此继承模型中的实体实例在其对应表中只会有一行。这与联接表继承不同,其中 IfrmaeTag 实体实例将在 WEBSITETAG 表中具有一行,在 IFRMAETAG 中具有一行,其中 IFRMAETAG 表仅包含不在 WEBSITETAG 表中的字段/数据。如果您希望数据存在于 WEBSITETAG 表中,也许这就是您应该考虑的继承策略。

对于 JPA 查询返回数据但 HtmlPage.tags 集合为空:这在双向关系中很常见,其中应用程序仅对关系的一侧进行更改。 JPA 有一个缓存,当您进行更改时,这些普通的 java 对象不会自动为您调整。当您创建一个 WebsiteTag 并设置它的 htmlPage 时,您还需要将该 WebsiteTag 添加到 HtmlPage 的标签列表中。如果您不这样做,除非您强制从数据库重新加载 HtmlPage - 刷新、清除缓存或重新启动服务器,否则集合将不会显示此更改。维护您的收藏通常效率更高,但如果需要,您可以调用 em.refresh(htmlPage)。

【讨论】:

  • 我现在正在使用列表对象。这不会改变任何事情。抽象类是一个普通的实体。买我的问题就在那里。我也很确定,我的 HtmlPage 对象是一致的。
  • 我不确定您所说的“我正在使用列表对象”是什么意思。您是否尝试过调用 em.refresh(htmlPage) 然后检查您的列表是否已填充?
  • Ps 我的前两段并不是针对您的具体问题,而是与您帖子中可见的其他一些问题和项目有关。特别是关于 WEBSITETAG 表中是否应该有数据的问题。
  • 调用 em.refresh(htmlPage) 没有帮助。
  • 看起来您正在 htmlPage 中设置新标签,但之后您没有显示您对 htmlPage 对象所做的事情。您显示的代码中的列表是否正确填充?它是否以某种方式合并/持续存在?也许我们应该退后一步 - 显示查询代码以及究竟什么是空的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
相关资源
最近更新 更多