Como persistir una entidad que tiene asociacion con otras mediante una Lista con JPA

Hola así es pues tengo una duda la cuestion es que yo accedo a mi base de datos y mediante un formulario mando a llamar un servlet para poder persistir una pregunta con una serie de respuestas y la relacion que mantienen es que una pregunta puede tener muchas respuestas (one to many) y muchas respuestas pueden tener una pregunta ( many to one ) pero pues cuando mando a persistir la pregunta solo guarda la pregunta en la base de datos y las respuestas no me las persiste, estuve buscando por google y encontre el tutorial pero ya cambie el tipo de cambio en cascada a: cascade = CascadeType.PERSIST en la entidad Respuesta pero la verdad es que las entidades las he creado con el IDE Netbeans y solo modifique en la anotación @ManyToOne ese parametro pero no funciono alguien me podria ayudar porfavor para saber que hago mal a continuación dejo mi servlet y entidades.

Servlet:

package controller;

import entities.Examen;
import entities.Pregunta;
import entities.Respuesta;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

@WebServlet(name = "RegistraPregunta", urlPatterns = {"/registraPreguntaServlet.do"})
public class RegistraPregunta extends HttpServlet {

    @Resource
    private UserTransaction utx;
    @PersistenceContext
    EntityManager em;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            Pregunta pregunta = new Pregunta();          
            Query q = em.createQuery("Select e FROM Examen e WHERE e.id =:eID");
            q.setParameter("eID", Integer.parseInt(request.getParameter("examenID")));
            Examen examen = (Examen)q.getSingleResult();
            pregunta.setExamenId(examen);
            pregunta.setTexto(request.getParameter("pregunta"));
           
           
            List<Respuesta> respuestas = new ArrayList<Respuesta>();        
           
            String param="respuesta";
            Respuesta resp = new Respuesta();
            for(int i=1;i<8;i++){
                param += i;            
                resp.setTexto(request.getParameter(param));
                respuestas.add(resp);
            }
           
           
            utx.begin();
                //em.persist(pregunta);
                for(Respuesta r:respuestas){
                    em.persist(r);
                }
            utx.commit();
           
            pregunta.setRespuestaList(respuestas);
            request.getRequestDispatcher("index.jsp").forward(request, response);
                                   
        } catch (RollbackException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (HeuristicMixedException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (HeuristicRollbackException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalStateException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NotSupportedException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SystemException ex) {
            Logger.getLogger(RegistraPregunta.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>
}

Entidad Pregunta:

package entities;

import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@Entity
@Table(name = "pregunta")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Pregunta.findAll", query = "SELECT p FROM Pregunta p"),
    @NamedQuery(name = "Pregunta.findById", query = "SELECT p FROM Pregunta p WHERE p.id = :id"),
    @NamedQuery(name = "Pregunta.findByTexto", query = "SELECT p FROM Pregunta p WHERE p.texto = :texto")})
public class Pregunta implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Size(max = 5000)
    @Column(name = "texto")
    private String texto;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "preguntaId")
    private List<Respuesta> respuestaList;
    @JoinColumn(name = "examen_id", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Examen examenId;

    public Pregunta() {
    }

    public Pregunta(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

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

    public String getTexto() {
        return texto;
    }

    public void setTexto(String texto) {
        this.texto = texto;
    }

    @XmlTransient
    public List<Respuesta> getRespuestaList() {
        return respuestaList;
    }

    public void setRespuestaList(List<Respuesta> respuestaList) {
        this.respuestaList = respuestaList;
    }

    public Examen getExamenId() {
        return examenId;
    }

    public void setExamenId(Examen examenId) {
        this.examenId = examenId;
    }

    @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 Pregunta)) {
            return false;
        }
        Pregunta other = (Pregunta) 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 "entities.Pregunta[ id=" + id + " ]";
    }
   
}

Entidad Respuesta:

package entities;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
@Entity
@Table(name = "respuesta")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Respuesta.findAll", query = "SELECT r FROM Respuesta r"),
    @NamedQuery(name = "Respuesta.findById", query = "SELECT r FROM Respuesta r WHERE r.id = :id"),
    @NamedQuery(name = "Respuesta.findByTexto", query = "SELECT r FROM Respuesta r WHERE r.texto = :texto"),
    @NamedQuery(name = "Respuesta.findByCorrecta", query = "SELECT r FROM Respuesta r WHERE r.correcta = :correcta")})
public class Respuesta implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Size(max = 5000)
    @Column(name = "texto")
    private String texto;
    @Column(name = "correcta")
    private Short correcta;
    @JoinColumn(name = "pregunta_id", referencedColumnName = "id")
    @ManyToOne(cascade = CascadeType.PERSIST, optional = false)
    private Pregunta preguntaId;

    public Respuesta() {
    }

    public Respuesta(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

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

    public String getTexto() {
        return texto;
    }

    public void setTexto(String texto) {
        this.texto = texto;
    }

    public Short getCorrecta() {
        return correcta;
    }

    public void setCorrecta(Short correcta) {
        this.correcta = correcta;
    }

    public Pregunta getPreguntaId() {
        return preguntaId;
    }

    public void setPreguntaId(Pregunta preguntaId) {
        this.preguntaId = preguntaId;
    }

    @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 Respuesta)) {
            return false;
        }
        Respuesta other = (Respuesta) 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 "entities.Respuesta[ id=" + id + " ]";
    }
   
}

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Los parametros respuestas en

Los parametros respuestas en tu formulario se llaman: respuesta1, respuesta12, respuesta123 etc? Por que as'i es como estas construyendo la cadena param += i

Tambien al agregar una respuesta solo estas creando insta instancia de objeto y agregandolo muchas veces, la ultima vez con nada porque si lo anterior no fue intencional, estas poniendo en una lista con el mismo objeto con el valor de resp.setTexto(request.getParameter("repuesta1234567"))

Me parece que lo que intentabas era:

for(int i=1;i<8;i++){
     respuesta.add( new Respuesta( request.getParameter("respuesta"+i)));
}

( tendrias que agregar el parametro "texto" al constructor )

Para que esto no te pase de nuevo, separa el código que inserta a la base de datos y el codigo de tu servlet. Asi puedes probar directamente "offline" y ver que esta sucediendo

Imagen de Cid

las variables respuesta

Hola si las variables respuesta asi estan generadas de la "respuesta1" a la "respuesta7" pero ya vi bien que me lanzo una excepcion del sql cuando intenta guardar a la BD y bueno el constructor no lo hice yo, lo genero automaticamente el netbeans segun siguiendo la buenas practicas jajaja. y solo me genero una constructor por defalutl (sin parametros) y otro mas que recibe el id aunque este no me sirve se supone que la base de datos autogenera el id. aqui dejo la excepcion que me tira.

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'pregunta_id' cannot be null
Error Code: 1048
Call: INSERT INTO respuesta (correcta, texto, pregunta_id) VALUES (?, ?, ?)
        bind => [3 parameters bound]
Query: InsertObjectQuery(entities.Respuesta[ id=null ])
        at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:840)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:906)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:592)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:535)
        at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1717)
        at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:253)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:207)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:193)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:342)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:162)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:177)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:472)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:287)
        at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
        at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:844)
        at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:743)
        at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
        at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2871)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1516)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1498)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1449)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:224)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:191)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:136)
        at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3799)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1415)
        at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:636)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1505)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3143)
        at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:346)
        at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:157)
        at org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68)
        at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:435)
        ... 30 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'pregunta_id' cannot be null
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.Util.getInstance(Util.java:386)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1040)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2794)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:831)
        ... 65 more
<code>
Imagen de Cid

Y otro problema

Creo que en el anterior es un problema acerca de como diseñaron la base de datos, lo que queremos hacer es presentar areas de texto para guardar una pregunta con sus respuestas pero todo en el mismo jsp pero para poder persisitir una Respuesta necesito el id de la Pregunta y como se persisten en el mismo servlet pues no puedo tener el id de la Pregunta entonces no se puede persistir la Respuesta, alguien sabe si esta relacion se puede auto-persistir sin que tenga que consultar de manera imperativa el id de la Pregunta y cuando lea todo del formulario persista tanto Pregunta como Respuesta.

El otro problema es que tengo un código parecido para guardar un objeto Examen y si lo hace pero tambien me esta guardando un Examen con valores de atributos nulos, es decir me guarda el Examen que se genera con un formulario de una pagina jsp pero aparte genera uno mas que tiene valore null en los demas atributos pero no se porque hace eso.

Ok, pero entendiste que tu

Ok, pero entendiste que tu estas buscando los parametros llamados:

respuesta1, respuesta12, respuesta123, respuesta1234, respuesta12345, respuesta123456, respuesta1234567

y que estabas usando el mismo objeto en vez de crear uno nuevo por cada respuesta?

El código generado por netbean si esta correcto y si sigue las mejores practicas, pero no quiere decir que se vuelve intocable, lo puedes escribir tu mismo, o simplemente hacer el setter antes.

El diseño de la base de datos también se ve bien, lo que pasa es que efectivamente no le estas estas diciendo a que pregunta corresponden esas respuestas y la base de datos no la va adivinar. En tu código llamas a em.persist(respuesta) pero no le has dicho a que pregunta corresponde esa respuesta. Intenta llamando antes respuesta.setPreguntas y puede ser que incluso no necesites invocar persist sobre cada respuesta.

El problema del examen suena a un similar mal manejo de los objetos ( te falta crear uno nuevo o asignarlo correctamente )

Asociar la pregunta la pregunta a la respuesta

Revisando un poco tu código te hago las siguientes observaciones.

1.- La propiedad cascade=PERSIST solo va en la entidad que contiene la lista, es decir en Pregunta, debido a que cuando guardes una pregunta las respuestas deben ser guardadas tambien, además no hay forma de que, en tu caso, se guarde una respuesta de forma independiente.
2. La entidad que tienes que guardar es la Pregunta, las respuestas se deben guardar automáticamente
3. Es necesario asociar la pregunta a la respuesta, para que ésta sepa a que pregunta pertenece, es una relación bidireccional, como lo tienes ahora sólo la Pregunta conoce sus respuestas.

            Pregunta pregunta = new Pregunta();          
            Query q = em.createQuery("Select e FROM Examen e WHERE e.id =:eID");
            q.setParameter("eID", Integer.parseInt(request.getParameter("examenID")));
            Examen examen = (Examen)q.getSingleResult();
            pregunta.setExamenId(examen);
            pregunta.setTexto(request.getParameter("pregunta"));
           
           
            List<Respuesta> respuestas = new ArrayList<Respuesta>();        
           
            String param="respuesta";
            //Declara y asigna null a esta variable
            Respuesta resp = null;
            for(int i=1;i<8;i++){
                //Crea una instancia de Respuesta y asignala
                resp = new Respuesta();
                //Usa StringBuilder o el método concat() de la clase String para concatenar cadenas          
                resp.setTexto(request.getParameter(param.concat(i)));
                // Asocia la pregunta a la respuesta
                resp.setPreguntaId(pregunta);
                respuestas.add(resp);
            }
           
            //Asignas las respuestas a la pregunta
            pregunta.setRespuestaList(respuestas);
           
            utx.begin();
            //Guardas la pregunta junto con sus respuestas
            em.persist(pregunta);
               
            utx.commit();
           
           

Para que entiendas el

Para que entiendas el problema de usar la misma instancia en cada respuesta ve este ejemplo:

import java.util.*;
public class Repetidos {
    public static void main( String ... args ) {

        List<Pato> patos = new ArrayList<Pato>();
        Pato pato = new Pato();        
        for( String current : new String[]{"Hugo", "Paco", "Luis"} ) {
            pato.name = current;
            patos.add( pato );
        }
        System.out.println(patos); // guess what?

       
    }
}
class Pato {
    Pato tio;
    String name;
    public String toString() {
        return this.name;
    }
   
}

Jeje y siguiendo con el ejemplo y la analogia, tu null es como preguntarse en ese codigo: Como es que no saben que su tio es Donald?

Imagen de Cid

Ya lo resolvi

Pues realmente el problema quedo resuelto lo que pasa es que no sabia como persistir la entidad Pregunta y al mismo tiempo obtener el dato del id (el id que se autoincrementa) para asignarselo a las entidades Respuestas que tenian ligada la Pregunta pero ya quedo, solo tenía que agregar una llamada al metodo flush() del EntityManager y luego ya con eso podia leer el dato del id desde la entidad Pregunta, que ya tenia el id que genero la BD.

Y como siempre san Google me salvo ahi dejo el link que me ayudo de StackOverflow

Y gracias por tus respuestas.

Imagen de Cid

jajaja y si tenias razon en mis variables respuesta

Un amigo me lo hizo notar y me dije pero que "pendejo soy" jajajaja, apenas llevo unos 2 dias aprendiendo de JPA, y pues son los primeros pasos.