简体   繁体   中英

JPA - Hibernate ManyToMany relationship save

Hey people I've been searching over the internet for the following problem without success...

Well... I'm testing whit Hibernate and JPA annotations, I've developed a usual user case ManyToMany relationship. I've tried to save a new User with existing Groups and the test case throws an exception, see test code and exception below*:

pd. looking at the queries the weird behavior in this case is that it's supossed that the logical path in this test the first method look up persisted groups and since they exit already save the new user record in its table and insert the new records in the link table.. but It is like Hibernate assume that Groups are new records and try to insert them in group table and of course this is throwing the duplicate exception... but I haven't figured it out yet...

What can I do? What am I doing wrong here ?

Thanks in advance.

@Test
    public void guardarUsuario(){
        assertThat(servicio, is(notNullValue()));
        Usuario u = new Usuario();
        u.setNombre("Elsy");
        u.setApellido("Cadenas");
        u.setCedula("4375792");     

        List<Grupo> grupos = servicio.buscarGrupos();

        u.getGrupos().addAll(grupos);

        servicio.guardarUsuario(u);


    }

Hibernate: select grupo0_.nombre as nombre1_, grupo0_.descripcion as descripc2_1_ from public.grupo grupo0_
Hibernate: select usuarios0_.idgrupo as idgrupo1_1_, usuarios0_.idusuario as idusuario1_, usuario1_.cedula as cedula0_0_, usuario1_.apellido as apellido0_0_, usuario1_.nombre as nombre0_0_, usuario1_.version as version0_0_ from usuario_grupo usuarios0_ inner join public.usuario usuario1_ on usuarios0_.idusuario=usuario1_.cedula where usuarios0_.idgrupo=?
Hibernate: select usuarios0_.idgrupo as idgrupo1_1_, usuarios0_.idusuario as idusuario1_, usuario1_.cedula as cedula0_0_, usuario1_.apellido as apellido0_0_, usuario1_.nombre as nombre0_0_, usuario1_.version as version0_0_ from usuario_grupo usuarios0_ inner join public.usuario usuario1_ on usuarios0_.idusuario=usuario1_.cedula where usuarios0_.idgrupo=?
Hibernate: select usuario0_.cedula as cedula0_0_, usuario0_.apellido as apellido0_0_, usuario0_.nombre as nombre0_0_, usuario0_.version as version0_0_ from public.usuario usuario0_ where usuario0_.cedula=?
Hibernate: insert into public.usuario (apellido, nombre, version, cedula) values (?, ?, ?, ?)
Hibernate: insert into public.grupo (descripcion, nombre) values (?, ?)
Hibernate: insert into public.grupo (descripcion, nombre) values (?, ?)
2469 [main] WARN  org.hibernate.util.JDBCExceptionReporter  - SQL Error: 0, SQLState: 23505
2469 [main] ERROR org.hibernate.util.JDBCExceptionReporter  - Batch entry 0 insert into public.grupo (descripcion, nombre) values ('usuarios', 'USUARIO') was aborted.  Call getNextException to see the cause.
2469 [main] WARN  org.hibernate.util.JDBCExceptionReporter  - SQL Error: 0, SQLState: 23505
2469 [main] ERROR org.hibernate.util.JDBCExceptionReporter  - ERROR: duplicate key value violates unique constraint "pk_grupo"

User.class

@Entity()
@Table(name = "usuario", schema = "public")
@NamedQuery(name = "Usuario.findByCedula", query = "select u from Usuario u where u.cedula = :cedula")
public class Usuario implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String nombre;
    private String apellido;
    @Id
    @Column(length = 8)
    private String cedula;
    @Version
    private Integer version;
    @JoinTable(name = "usuario_grupo", joinColumns = { @JoinColumn(name = "idusuario", referencedColumnName = "cedula") }, inverseJoinColumns = { @JoinColumn(name = "idgrupo", referencedColumnName = "nombre") })
    @ManyToMany(cascade =  { CascadeType.PERSIST }, fetch = FetchType.LAZY)
    private Set<Grupo> grupos = new HashSet<Grupo>();

    Constructors... Getters and setters...

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cedula == null) ? 0 : cedula.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Usuario other = (Usuario) obj;
        if (cedula == null) {
            if (other.cedula != null)
                return false;
        } else if (!cedula.equals(other.cedula))
            return false;
        return true;
    }

    public void addGrupo(Grupo g) {
        if (!getGrupos().contains(g)) {
            getGrupos().add(g);
        }
        if (!g.getUsuarios().contains(this)) {
            g.getUsuarios().add(this);
        }
    }

Group.class

@Entity
@Table(name="grupo", schema="public")
public class Grupo implements Serializable{


    private static final long serialVersionUID = 1L;
    @Id
    private String nombre;
    private String descripcion;
    @JoinTable(name="usuario_grupo", joinColumns={@JoinColumn(name="idgrupo")}, inverseJoinColumns=@JoinColumn(name="idusuario"))
    @ManyToMany(cascade={CascadeType.PERSIST}, fetch=FetchType.EAGER)
    private Set<Usuario> usuarios = new HashSet<Usuario>(); 

    Constructors... Getters and Setters...

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((descripcion == null) ? 0 : descripcion.hashCode());
        result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Grupo other = (Grupo) obj;
        if (descripcion == null) {
            if (other.descripcion != null)
                return false;
        } else if (!descripcion.equals(other.descripcion))
            return false;
        if (nombre == null) {
            if (other.nombre != null)
                return false;
        } else if (!nombre.equals(other.nombre))
            return false;
        return true;
    }
}

SQL:

USER Table


-- Table: usuario

-- DROP TABLE usuario;

CREATE TABLE usuario
(
  nombre character varying(50),
  apellido character varying(50),
  cedula character varying(8) NOT NULL,
  "version" integer,
  CONSTRAINT pk_usuario PRIMARY KEY (cedula)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE usuario OWNER TO postgres;

GROUP Table

-- Table: grupo

-- DROP TABLE grupo;

CREATE TABLE grupo
(
  nombre character varying(20) NOT NULL,
  descripcion character varying(50),
  CONSTRAINT pk_grupo PRIMARY KEY (nombre)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE grupo OWNER TO postgres;

Link Table

-- Table: usuario_grupo

-- DROP TABLE usuario_grupo;

CREATE TABLE usuario_grupo
(
  idusuario character varying(8) NOT NULL,
  idgrupo character varying(20) NOT NULL,
  CONSTRAINT pk_id_grupo PRIMARY KEY (idusuario, idgrupo),
  CONSTRAINT fk_miembro_grupo FOREIGN KEY (idgrupo)
      REFERENCES grupo (nombre) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT fk_miembro_usuario FOREIGN KEY (idusuario)
      REFERENCES usuario (cedula) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
ALTER TABLE usuario_grupo OWNER TO postgres;

I think your ManyToMany relationship between Usuario and Grupo might be persisting values that are already persisted on the Grupo table. The exception is pretty self-descriptive when it says that the unique constraint for its PK is being broken.

Do not cascade the persist operation and you should be fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM