Foros del Web » Programación para mayores de 30 ;) » Java »

Problema con JPA

Estas en el tema de Problema con JPA en el foro de Java en Foros del Web. Buenos días, estoy empezando a practicar persistencia con JPA en Netbeans, ya pude hacer persistir una clase simple. Mi problema está cuando quiero hacer persistir ...
  #1 (permalink)  
Antiguo 23/02/2012, 10:10
 
Fecha de Ingreso: febrero-2012
Mensajes: 3
Antigüedad: 12 años, 2 meses
Puntos: 0
Problema con JPA

Buenos días, estoy empezando a practicar persistencia con JPA en Netbeans, ya pude hacer persistir una clase simple.
Mi problema está cuando quiero hacer persistir 2 clases relacionadas, para practicar esto me hice un proyecto de ejemplo en donde tengo una clase Persona y una clase Auto, estas dos clases están relacionadas de manera que una persona pueda poseer N autos, pero al momento de persistir me da el siguiente error:
Código:
Exception in thread "main" java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.
Como para que se den una idea de lo que hice les comento todos los pasos que seguí, basado en una guía que encontré:

En MySQL creé las dos tablas para persistir las clases, estas son sus estructuras

persona:
id de tipo INT, es PK y tiene activado auto increment
nombre de tipo varchar longitud 20
apellido de tipo varchar longitud 20
dni de tipo varchar longitud 8 (en Argentina el numero de dni tiene 8 cifras, por ahora)

auto:
id de tipo INT, PK, auto increment activado
marca tipo varchar longitud 15
modelo tipo varchar longitud 15
patente tipo varchar longitud 7
persona_fk tipo INT y foreign key referenciando al id de persona

En netbeans creé un nuevo proyecto llamado probandoRelacion, luego creé automáticamente las clases entidad a partir de la base de datos (olvidé decir que anteriormente había establecido la conexión a la base de datos en netbeans).
Después creé automáticamente las clases controladoras JPA de entidades.
En cada clase controladora JPA creé un constructor que no recibe ningun parámetro y crea un EntityManagerFactory, este es el constructor de la clase AutoJpaController:
Código:
public AutoJpaController(){  
        this.emf = Persistence.createEntityManagerFactory("probandoRelacionPU");
    }
Agregué la biblioteca Driver MYSQL JDBC al proyecto

En la clase Auto agregué un constructor que solo reciba marca, modelo y patente.

En la clase Persona agregué el siguiente metodo:

Código:
public void comprarAuto(String marca,String modelo, String patente){
        Auto unAuto = new Auto(marca,modelo,patente);
        autoCollection = new Vector<Auto>();
        autoCollection.add(unAuto);
        
    }
También agregué un constructor que reciba solo el nombre, apellido y dni

En la anotacion @OneToMany agregué que se utilice cascade, la anotación y el atributo quedaron así:
Código:
@OneToMany(mappedBy = "personaFk", cascade=CascadeType.ALL)
    private Collection<Auto> autoCollection;
Finalmente, en el metodo main de la clase principal escribí estas líneas:

Código:
public static void main(String[] args) {
        PersonaJpaController controlador = new PersonaJpaController();
        Persona unaPersona = new Persona("Pepito","Perez","12345678");
        unaPersona.comprarAuto("Ford", "Focus", "ASD-123");
        controlador.create(unaPersona);
    }
Les pido disculpas si el post se hizo muy largo, simplemente quise darles todos los detalles como para ayudarlos a ayudarme, hace poco empecé java por mi cuenta así que si tienen alguna sugerencia basada en lo que escribí no duden en decírmelo.

Saludos.
  #2 (permalink)  
Antiguo 23/02/2012, 14:27
 
Fecha de Ingreso: agosto-2011
Ubicación: Madrid
Mensajes: 185
Antigüedad: 12 años, 7 meses
Puntos: 29
Respuesta: Problema con JPA

Puedes poner el código de tu clase Auto? Si has hecho un mappedBy, dentro de la anotación @OneToMany en tu clase Persona, en tu clase Auto tendrás que tener un @ManyToOne()
  #3 (permalink)  
Antiguo 23/02/2012, 14:53
 
Fecha de Ingreso: febrero-2012
Mensajes: 3
Antigüedad: 12 años, 2 meses
Puntos: 0
Respuesta: Problema con JPA

Gracias por tu respuesta, este es el código de mi clase Auto:
Código:
package probandorelacion;

import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Ricardo
 */
@Entity
@Table(name = "auto")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Auto.findAll", query = "SELECT a FROM Auto a"),
    @NamedQuery(name = "Auto.findById", query = "SELECT a FROM Auto a WHERE a.id = :id"),
    @NamedQuery(name = "Auto.findByMarca", query = "SELECT a FROM Auto a WHERE a.marca = :marca"),
    @NamedQuery(name = "Auto.findByModelo", query = "SELECT a FROM Auto a WHERE a.modelo = :modelo"),
    @NamedQuery(name = "Auto.findByPatente", query = "SELECT a FROM Auto a WHERE a.patente = :patente")})
public class Auto implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @Column(name = "marca")
    private String marca;
    @Basic(optional = false)
    @Column(name = "modelo")
    private String modelo;
    @Basic(optional = false)
    @Column(name = "patente")
    private String patente;
    @JoinColumn(name = "persona_fk", referencedColumnName = "id")
    @ManyToOne
    private Persona personaFk;

    public Auto() {
    }

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

    public Auto(String marca, String modelo, String patente) {
        this.marca = marca;
        this.modelo = modelo;
        this.patente = patente;
    }

    public Auto(Integer id, String marca, String modelo, String patente) {
        this.marca = marca;
        this.modelo = modelo;
        this.patente = patente;
    }

    public Integer getId() {
        return id;
    }

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

    public String getMarca() {
        return marca;
    }

    public void setMarca(String marca) {
        this.marca = marca;
    }

    public String getModelo() {
        return modelo;
    }

    public void setModelo(String modelo) {
        this.modelo = modelo;
    }

    public String getPatente() {
        return patente;
    }

    public void setPatente(String patente) {
        this.patente = patente;
    }

    public Persona getPersonaFk() {
        return personaFk;
    }

    public void setPersonaFk(Persona personaFk) {
        this.personaFk = personaFk;
    }

    @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 Auto)) {
            return false;
        }
        Auto other = (Auto) 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 "probandorelacion.Auto[ id=" + id + " ]";
    }
    
}
Efectivamente, se encuentra la anotación @ManyToOne

EDITO: Por las dudas también dejo el código de la clase Persona

Código:
package probandorelacion;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.util.*;

/**
 *
 * @author Ricardo
 */
@Entity
@Table(name = "persona")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Persona.findAll", query = "SELECT p FROM Persona p"),
    @NamedQuery(name = "Persona.findById", query = "SELECT p FROM Persona p WHERE p.id = :id"),
    @NamedQuery(name = "Persona.findByNombre", query = "SELECT p FROM Persona p WHERE p.nombre = :nombre"),
    @NamedQuery(name = "Persona.findByApellido", query = "SELECT p FROM Persona p WHERE p.apellido = :apellido"),
    @NamedQuery(name = "Persona.findByDni", query = "SELECT p FROM Persona p WHERE p.dni = :dni")})
public class Persona implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @Column(name = "nombre")
    private String nombre;
    @Basic(optional = false)
    @Column(name = "apellido")
    private String apellido;
    @Basic(optional = false)
    @Column(name = "dni")
    private String dni;
    @OneToMany(mappedBy = "personaFk", cascade=CascadeType.ALL)
    private Collection<Auto> autoCollection;

    public Persona() {
    }

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

    public Persona(Integer id, String nombre, String apellido, String dni) {
        this.nombre = nombre;
        this.apellido = apellido;
        this.dni = dni;
    }

    public Integer getId() {
        return id;
    }

    public Persona(String nombre, String apellido, String dni) {
        this.id = 2;
        this.nombre = nombre;
        this.apellido = apellido;
        this.dni = dni;
    }

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

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getApellido() {
        return apellido;
    }

    public void setApellido(String apellido) {
        this.apellido = apellido;
    }

    public String getDni() {
        return dni;
    }

    public void setDni(String dni) {
        this.dni = dni;
    }

    @XmlTransient
    public Collection<Auto> getAutoCollection() {
        return autoCollection;
    }

    public void setAutoCollection(Collection<Auto> autoCollection) {
        this.autoCollection = autoCollection;
    }

    @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 Persona)) {
            return false;
        }
        Persona other = (Persona) 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 "probandorelacion.Persona[ id=" + id + " ]";
    }
    
    public void comprarAuto(String marca,String modelo, String patente){
        Auto unAuto = new Auto(marca,modelo,patente);
        autoCollection = new LinkedList<Auto>();
        autoCollection.add(unAuto);
        
    }
}

Última edición por Selva123; 23/02/2012 a las 15:07
  #4 (permalink)  
Antiguo 23/02/2012, 15:39
 
Fecha de Ingreso: agosto-2011
Ubicación: Madrid
Mensajes: 185
Antigüedad: 12 años, 7 meses
Puntos: 29
Respuesta: Problema con JPA

Código PHP:
public static void main(String[] args) {
        
PersonaJpaController controlador = new PersonaJpaController();
        
Persona unaPersona = new Persona("Pepito","Perez","12345678");
        
unaPersona.comprarAuto("Ford""Focus""ASD-123");
        
controlador.create(unaPersona);
    } 
Viendo el mensaje de error que te da (PK en un objeto), creo que lo que puede estar pasando es que tu creas tu objeto de la clase Persona. Hasta que no hagas commit(), no se asigna su PK, al ser esta auto asignada, por lo que al intentar relacionar tu objeto de la clase Auto con tu objeto de la clase Persona, se encuentra con una PK=null.

Puedes forzar a que se genere, si no recuerdo mal con flush():
http://docs.oracle.com/javaee/5/api/...er.html#flush()

En tu clase Persona puedes tener éste método para comprar un auto.

Código PHP:
public void comprarAuto(Auto a){
    
listaAutos.add(a);
    
//Ahora necesitamos que el Auto sepa quien es su dueño, creando una relación
    
a.setPersonaPropietaria(this);

Esta última línea se ha hecho porque estás usando una relación bidireccional, o sea, una Persona tiene muchos Autos, y un Auto pertenece a una Persona, así que cada vez que añadas o borres un Auto de una Persona, tienes que crear o eliminar la relación con Persona.

No es muy normal el crear un Auto para añadirlo. Si estamos en una web, tu estás viendo una lista de Auto's, y tu eliges el que quieres comprar, pero el objeto Auto ya está guardado en la bd previamente.

Ten cuidado también con constructores como este:

Código PHP:
 public Persona(Integer id) {
        
this.id id;
    } 
, ya que tu PK se genera automáticamente. Puede darte problemas si le das tú el id.

Aquí tienes un tutorial muy bueno para aprender JPA:
http://schuchert.wikispaces.com/JPA+...etting+Started

Un saludo.
  #5 (permalink)  
Antiguo 23/02/2012, 17:37
 
Fecha de Ingreso: febrero-2012
Mensajes: 3
Antigüedad: 12 años, 2 meses
Puntos: 0
Respuesta: Problema con JPA

Muchas gracias por tu respuesta, pude solucionar (o al menos, esquivar) el problema, hice lo que me habías sugerido, primero creé la persona y el auto y los hice persistir, luego hice que esa persona adquiera el auto y así pude guardar la relación en la BD.
Te agradezco por la ayuda que me diste.

Etiquetas: jpa, netbeans
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 16:56.