web-dev-qa-db-fra.com

un nouvel objet a été trouvé grâce à une relation non marquée en cascade PERSIST

En essayant d'obtenir une relation @OneToMany entre Article et HeaderField, le mappage n'est probablement pas tout à fait correct, ce qui entraîne:

init:
Deleting: /home/thufir/NetBeansProjects/USENET/build/built-jar.properties
deps-jar:
Updating property file: /home/thufir/NetBeansProjects/USENET/build/built-jar.properties
compile:
run:
DEBUG: nntp: newsrc loading /home/thufir/.newsrc
DEBUG: nntp: newsrc load: 1 groups in 31ms
[EL Info]: 2012-07-31 02:05:05.677--ServerSession(8979162)--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
[EL Info]: 2012-07-31 02:05:06.778--ServerSession(8979162)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU login successful
[EL Warning]: 2012-07-31 02:05:06.903--ServerSession(8979162)--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.HEADERFIELD' doesn't exist
Error Code: 1146
Call: ALTER TABLE HEADERFIELD DROP FOREIGN KEY FK_HEADERFIELD_ARTICLE_ID
Query: DataModifyQuery(sql="ALTER TABLE HEADERFIELD DROP FOREIGN KEY FK_HEADERFIELD_ARTICLE_ID")
[EL Warning]: 2012-07-31 02:05:06.916--ServerSession(8979162)--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-07-31 02:05:07.033--ServerSession(8979162)--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")
[EL Warning]: 2012-07-31 02:05:07.122--ServerSession(8979162)--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 'HEADERFIELD'
Error Code: 1051
Call: DROP TABLE HEADERFIELD
Query: DataModifyQuery(sql="DROP TABLE HEADERFIELD")
[EL Warning]: 2012-07-31 02:05:08.921--UnitOfWork(26970615)--Java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: null.
Jul 31, 2012 2:05:08 AM net.bounceme.dur.usenet.driver.Main main
SEVERE: null
javax.persistence.RollbackException: Java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: null.
    at org.Eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.Java:102)
    at org.Eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.Java:63)
    at net.bounceme.dur.usenet.driver.Main.persistArticle(Main.Java:67)
    at net.bounceme.dur.usenet.driver.Main.<init>(Main.Java:43)
    at net.bounceme.dur.usenet.driver.Main.main(Main.Java:24)
Caused by: Java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: null.
    at org.Eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.discoverUnregisteredNewObjects(RepeatableWriteUnitOfWork.Java:302)
    at org.Eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.Java:695)
    at org.Eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.Java:1482)
    at org.Eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.Java:265)
    at org.Eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.Java:1135)
    at org.Eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.Java:84)
    ... 4 more

[EL Info]: 2012-07-31 02:05:09.332--ServerSession(8979162)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU logout successful
BUILD SUCCESSFUL (total time: 9 seconds)

Article:

package net.bounceme.dur.usenet.model;

import Java.io.Serializable;
import Java.util.AbstractMap.SimpleEntry;
import Java.util.ArrayList;
import Java.util.Enumeration;
import Java.util.List;
import Java.util.logging.Level;
import Java.util.logging.Logger;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
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 String subject;
    @OneToMany(mappedBy = "article", cascade = CascadeType.PERSIST)
    private List<HeaderField> headerFields = new ArrayList<>();

    public Article() {
    }

    public Article(Message message) {
        try {
            subject = message.getSubject();
            Enumeration e = message.getAllHeaders();

            while (e.hasMoreElements()) {
                Header header = (Header) e.nextElement();
                @SuppressWarnings("unchecked")
                SimpleEntry nameValue = new SimpleEntry(header.getName(), header.getValue());
                HeaderField headerField = new HeaderField(nameValue);
                headerFields.add(headerField);
            }
        } catch (MessagingException ex) {
            Logger.getLogger(Article.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    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 subject;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }
}

HeaderField:

package net.bounceme.dur.usenet.model;

import Java.io.Serializable;
import Java.util.AbstractMap.SimpleEntry;
import Java.util.logging.Logger;
import javax.persistence.*;

@Entity
public class HeaderField implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(HeaderField.class.getName());
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @ManyToOne
    private Article article = new Article();
    @Column
    private String headerName;
    @Column
    private String headerValue;

    public HeaderField() {
    }

    public HeaderField(SimpleEntry nameValue) {
        headerName = nameValue.getKey().toString();
        headerValue = nameValue.getValue().toString();
    }

    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 HeaderField)) {
            return false;
        }
        HeaderField other = (HeaderField) 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 "\n\nheaderName\t" + headerName + "\nheaderValue\t" + headerValue;
    }

    public String getHeaderName() {
        return headerName;
    }

    public void setHeaderName(String headerName) {
        this.headerName = headerName;
    }

    public String getHeaderValue() {
        return headerValue;
    }

    public void setHeaderValue(String headerValue) {
        this.headerValue = headerValue;
    }
}

Les entités ont été mises à jour pour refléter la suggestion d'utiliser CascadeType.PERSIST qui, apparemment, permettrait de corriger ce message d'erreur particulier. Je travaille sur la génération d'un journal plus utile pour suivre ce qui se passe.

30
Thufir

Ce que vous aviez probablement fait, c'est que vous avez créé une nouvelle instance de Article et de nouvelles instances de HeaderField. Ces instances de HeaderField ont ensuite été associées à Article. 

Après cette tentative de persistance, cet article échoue car, comme le dit le message d'erreur, il fait référence à de nouveaux objets et la relation n'est pas marquée comme PERSIST. De plus, selon vos journaux, ces instances de HeaderField ne possèdent pas headerName et headerValue set.

Vous avez deux options:

  1. persiste aussi d'autres instances référencées depuis Article via em.persist
  2. cascade, opération persistante d’Article à HeaderFields avec les options suivantes

    OneToMany(mappedBy = "article", cascade = CascadeType.PERSIST)  
    private List<HeaderField> someOrAllHeaderFields = new ArrayList<>();
    

De plus, vous ne devez pas supprimer le constructeur no-arg. JPA implementation appelle toujours ce constructeur lorsqu’il crée une instance. 

Mais vous pouvez faire en sorte que le constructeur sans argument soit protégé. Dans la spécification JPA 2.0, cela est dit avec les mots suivants:

La classe d'entité doit avoir un constructeur no-arg. La classe d'entité peut avoir d'autres constructeurs aussi. Le constructeur no-arg doit être public ou protégé.

53
Mikko Maunu

ajoutez simplement CascadeType.ALL sur votre relation.

OneToMany(mappedBy = "article", cascade = CascadeType.ALL)  
private List<HeaderField> someOrAllHeaderFields = new ArrayList<>();
0
TanvirChowdhury