Duda | Crear POST y DELETE tablas intermedias Spring JPA

Intencity

Hola, estoy empezando con Spring Boot en un sistema RESTful. Tengo dos tablas "usuarios" y "permisos" las cuales tienen una relación de muchos a muchos con una tabla intermedia "usuario_permiso" que solo tiene las llaves foraneas de id_usuario y id_permiso.

Ya he conseguido hacer que me muestre que permisos tiene el usuario desde un GET a "usuarios/{id}/permisos", pero aun no se como hacer para agregar una relación con un POST o borrarla con un DELETE. He probado hacerlo con Querys nativas pero he visto que no se puede hacer INSERT en JPA. ¿Alguna sugerencia de como implementarlo?

Esta es mi entidad Usuario:

spoiler

Esta es mi entidad Permiso

spoiler

Ambas las he mapeado con el NetBeans.

Este es mi controlador del Usuario:

spoiler

Script de la base de datos:

spoiler

Gracias de antemano. Saludos.

desu

1º Veo muchas anotaciones para algo tan sencillo, definir tu las query tampoco lo entiendo.
https://stackoverflow.com/questions/42394095/many-to-many-relationship-between-two-entities-in-spring-boot

#1Intencity:

he visto que no se puede hacer INSERT en JPA

Me perdí.
3º Usa un @JoinTable o crea otra tabla, yo empezaría creando una tabla y hacer los @RequestMapping tranquilamente ahí.
4º Tienes dudas con Spring o como va Rest?

ElCavernero

O dios mio! un @ManyToMany!.

Como consejo, nunca uses ManyToMany es mas engorrosso de manejar.

Por lo que veo quieres hacer que un usuario pueda tener muchos permisos, yo lo haría de la siguiente forma:
En las tablas permisos y usuario no crearía referencias y crearía de manera manual la tabla intermedia llamada por ejemplo PermisosUsuarios, le añadiría como clave primaria un ID que podría ser un Long incremental, además de idUsuario e idPermiso que estarían relacionadas en @ManyToOne cada una con su entidad.

De esta forma si por ejemplo tienes los permisos "Invitado", "Registrado" y "Moderador" con ids 1, 2 y 3 y quieres asignar al usuario con id 1 los permisos 2 y 3 tendrás que crear las tuplas en PermisosUsuarios con idUsuario 1 e idPermisos 2 y otra tupla con el 3.

Para hacer el GET deberás buscar todas las tuplas cuyo idUsuario sea 1.
Con @Query y JPQL sería algo así:
@Query("SELECT DISTINCT p from Permisos p, PermisosUsuarios pu where pu.idPUsuario= ?1")

Espero que te sirva.

Saludos.

1 respuesta
JackSparow

#1 Si la declaras como @ManyToMany, el hecho de agregar/quitar relaciones entre usuario y permisos vienen determinados por los PUT/POST que hagas sobre usuario. (tabla propietaria de la relación).
Es decir, siempre que hagas un PUT/POST sobre usuario, tendrás que pasarle la lista de permisos tal y como la quieres en ese momento.

PUT/POST sobre el recurso usuario:
{
idUsuario : 1,
nombre: "pepito",
permisoCollection : [
 {  idPermiso : 2 },
 {  idPermiso : 3},
 {  idPermiso : 5 } ],
..otros atributos de usuario...
}

En los objetos de permiso, sólo hace falta que pases los id_permiso. Si no existen permisos con esos identificadores, te saltará la excepción de integridad de datos. (ya que tienes declarado la relación implicitamente como CascadeType=[] (cosa que veo ideal en tu caso).

Como comenta #3, las ManyToMany "implica" que no tienes un servicio para la tabla intermedia (cosa que veo normal en tu caso), por lo que no veo mal que lo tengas tal y como lo tienes.

Intencity

Muchas gracias por sus respuestas, al final lo he resuelto de la siguiente manera utilizando los objetos:

@PostMapping("/usuarios/{idUsuario}/permisos")
 public Usuario addPermisoUsuario(@PathVariable(value = "idUsuario") Integer 
    userId, @RequestBody Permiso permiso) {
       Usuario user = userRepository.findOne(userId);
       Permiso permisoExistente = 
       permisoRepository.findOne(permiso.getIdPermiso());
       user.getPermisoCollection().add(permisoExistente);
       return userRepository.save(user);
 }

 @DeleteMapping("/usuarios/{idUsuario}/permisos/{idPermiso}")
 public Usuario deletePermisoUsuario(@PathVariable(value = "idUsuario") 
    Integer userId, @PathVariable(value = "idPermiso") 
    Integer permisoId, @RequestBody Permiso permiso) {
       Usuario user = userRepository.findOne(userId);
       Permiso permisoEliminar = permisoRepository.findOne(permisoId);
       user.getPermisoCollection().remove(permisoEliminar);
       return userRepository.save(user);
 }

Saludos!

1 respuesta
JackSparow

#5 Perfecto.
Cuidado con el NullPointException cuando el usuario no existe. (deberías volver el notFound()).

Usuarios habituales

  • JackSparow
  • Intencity
  • ElCavernero
  • desu