質問

In the event of a NoResultException how do I instantiate and persist a Newsgroup entity?

package net.bounceme.dur.usenet.driver;

import java.util.logging.Logger;
import javax.mail.Folder;
import javax.mail.Message;
import javax.persistence.*;
import net.bounceme.dur.usenet.model.Article;
import net.bounceme.dur.usenet.model.Newsgroup;

class DatabaseUtils {

    private static final Logger LOG = Logger.getLogger(DatabaseUtils.class.getName());
    private EntityManagerFactory emf = Persistence.createEntityManagerFactory("USENETPU");
    private EntityManager em = emf.createEntityManager();

    public void persistArticle(Message message, Folder folder) {
        //do all the persistence here?
        String fullNewsgroupName = folder.getFullName();
        Newsgroup newsgroup = null;
        TypedQuery<Newsgroup> query = em.createQuery("SELECT n FROM Newsgroup n WHERE n.newsgroup = :newsGroupParam", Newsgroup.class);
        query.setParameter("newsGroupParam", fullNewsgroupName);
        try {
            newsgroup = query.getSingleResult();
            LOG.info("found " + query.getSingleResult()); //ok
        } catch (javax.persistence.NoResultException e) {
            LOG.info(e + "\ncould not find " + fullNewsgroupName); //ok
            newsgroup = new Newsgroup(folder);
            //it seems like the persist statement never executes...
            em.persist(newsgroup);
        } catch (NonUniqueResultException e) {
            LOG.info("\nshould never happen\t" + fullNewsgroupName); //not ok, should never execute
        }
        //need some mechanism to ensure that newsgroup is never a null reference
        Article article = new Article(message, newsgroup);
        em.persist(article); //never seems to execute..
    }

    public void close() {
        em.close();
        emf.close();//necessary?
    }
}

Now, it seems that, over and over, no results are found for the query:

run:
DEBUG: nntp: newsrc loading /home/thufir/.newsrc
DEBUG: nntp: newsrc load: 5 groups in 30ms
Aug 03, 2012 6:04:47 PM net.bounceme.dur.usenet.driver.FetchBean <init>
INFO: [gwene.com.androidcentral, gwene.com.blogspot.emacsworld, gwene.com.blogspot.googlecode, gwene.com.blogspot.googlereader, gwene.com.economist]
[EL Info]: 2012-08-03 18:04:51.277--ServerSession(28034142)--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
[EL Info]: 2012-08-03 18:04:52.417--ServerSession(28034142)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU login successful
[EL Warning]: 2012-08-03 18:04:52.557--ServerSession(28034142)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'usenet.ARTICLE' doesn't exist
Error Code: 1146
Call: ALTER TABLE ARTICLE DROP FOREIGN KEY FK_ARTICLE_NEWSGROUP_ID
Query: DataModifyQuery(sql="ALTER TABLE ARTICLE DROP FOREIGN KEY FK_ARTICLE_NEWSGROUP_ID")
[EL Warning]: 2012-08-03 18:04:52.572--ServerSession(28034142)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown table 'ARTICLE'
Error Code: 1051
Call: DROP TABLE ARTICLE
Query: DataModifyQuery(sql="DROP TABLE ARTICLE")
[EL Warning]: 2012-08-03 18:04:52.65--ServerSession(28034142)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown table 'NEWSGROUP'
Error Code: 1051
Call: DROP TABLE NEWSGROUP
Query: DataModifyQuery(sql="DROP TABLE NEWSGROUP")
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.emacsworld
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:54 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
[EL Info]: 2012-08-03 18:04:55.344--ServerSession(28034142)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU logout successful
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.DatabaseUtils persistArticle
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 6:04:55 PM net.bounceme.dur.usenet.driver.FetchBean <init>
INFO: **************************done
BUILD SUCCESSFUL (total time: 11 seconds)

How can I ensure that, when no unique Newsgroup exists, that a new entity is persisted to the database? That, at least, is the intent of the code if not the result.

Newsgroup:

package net.bounceme.dur.usenet.model;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import javax.mail.Folder;
import javax.persistence.*;

@Entity
public class Newsgroup implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(Newsgroup.class.getName());
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column //@Unique @UniqueConstraint interface..?
    private String newsgroup;
    @OneToMany(mappedBy = "newsgroup", cascade = CascadeType.PERSIST)
    private Set<Article> articles = new HashSet<>();

    public Newsgroup() {
        //should not create a newsgroup without a Folder
    }

    public Newsgroup(Folder folder) {
        newsgroup = folder.getFullName();//if row already exists, then what?
        LOG.fine(newsgroup);    
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Newsgroup)) {
            return false;
        }
        Newsgroup other = (Newsgroup) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return newsgroup;
    }
}

and Article:

package net.bounceme.dur.usenet.model;

import java.io.Serializable;
import java.util.logging.Logger;
import javax.mail.Message;
import javax.persistence.*;

@Entity
public class Article implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(Article.class.getName());
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column
    private int messageNumber;
    @ManyToOne(cascade = CascadeType.PERSIST)
    private Newsgroup newsgroup;

    public Article() {
    }

    public Article(Message message, Newsgroup newsgroup) {
        messageNumber = message.getMessageNumber();
        this.newsgroup = newsgroup;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Article)) {
            return false;
        }
        Article other = (Article) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "\nmessageNumber\t" + messageNumber;
    }

    public int getMessageNumber() {
        return messageNumber;
    }

    public void setMessageNumber(int messageNumber) {
        this.messageNumber = messageNumber;
    }
}
役に立ちましたか?

解決

To quote Keith & Schincariol in "Pro JPA 2":

If a transaction is not present, then either the modifying operation will throw an exception or the change will simply never be persisted to the data store.

Ensure that interactions that you want persisted, with the EntityManager em, are within the context of a transaction:

public void persistArticle(Message message, Folder folder) {
    em.getTransaction().begin();
    try {
        // your code goes here; possibly current body of persistArticle(..)

        em.getTransaction().commit();
    } catch (final RuntimeException e) {
        if (em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }

        throw e;
    }
}

You'll quickly find this to be common boiler-plate code. Consider using a decorator:

  • Create an interface defining the services/methods offered by DatabaseUtils.
  • Let DatabaseUtils implement that interface.
  • Create a DataUtilsServices (decorator) implementing the interface. It's implementation will contain the code managing the transaction, where the working code ("your code goes here") becomes a call to the same method on an instance of DatabaseUtils.

Use of the decorator, now, to separate the "worker" from the "transactional service" will enable you to easily step into Java EE, Spring, or other container framework that manages transactions for you, in the future. For example, your DatabaseUtilsServices containing all that boiler-plate could very well become part of an EJB decorator:

@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void persistArticle(Message message, Folder folder) {
    return databaseUtils.persistArticle(message, folder);
}

他のヒント

Because JPA does not have an "auto-commit" mode (i.e no transaction). Operations outside of a transaction are supposed to be delayed until the next transaction happens (and yours doesn't).

Obviously some JPA implementations (such as DataNucleus JPA) provide a full auto-commit capability because they consider it to be a very useful facility to ease the job for the user ... so maybe you're just using one of those that doesn't allow that.

Perhaps it would suffice tu insert the following lines right before the line that instanciates a new Article :

if (newsgroup==null) {
    newsgroup = new NewsGroup();
}

Also, you may have to manually persist() that new Newsgroup if the relation from Article to Newsgroup does not have its Cascade property set to PERSIST.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top