Problems removing element @ManyToMany with @JoinTable

3

I'm having problems deleting a @ManyToMany relationship in JPA. In short, I have 3 tables: USER, PERMISSION, and USER_PERMISSION that is the relationship of the previous ones, the relationship is N to N. The problem is that I'm not sure how to remove or add a new permission to an existing user in the base, follows simplified model:

@Entity
@Table(name="TB_USUARIO")
public class Usuario {

    @Id
    @Column(name="NO_USUARIO", unique=true, nullable=false, length=50)
    private String noUsuario;

    @ManyToMany
    @JoinTable(
        name="TB_USUARIO_PERMISSAO"
        , joinColumns={
            @JoinColumn(name="NO_USUARIO", nullable=false)
            }
        , inverseJoinColumns={
            @JoinColumn(name="NO_METODO", referencedColumnName="NO_METODO", nullable=false),
            @JoinColumn(name="NO_PERMISSAO", referencedColumnName="NO_PERMISSAO", nullable=false))
            }
        )
    private List<Permissao> permissoes;
}



@Entity
@Table(name="TB_PERMISSAO")
public class Permissao {

    @EmbeddedId
    private PermissaoPK id;

    @ManyToMany(mappedBy="permissoes")
    private List<Usuario> usuarios;
}

My problem is with the Permissions field mapping of the User class, when I insert a new User into the database, adding the permission works, p>

Usuario usuario = new Usuario("USUARIO_01");
usuario.getPermissoes().add(permissao); //permissao ja cadastrada no banco
entityManager.persist(usuario);

This code correctly inserts a new user and also the relationship with the permission in the TB_USUARIO_PERMISSAO table. The problem now is to delete the permissions, let's assume that a user has 2 permissions and I want to delete one of them, how do I do that? Here's an example does not work :

Usuario usuario = entityManager.find(Usuario.class, "USUARIO_01");
usuario.getPermissoes().remove(0);
entityManager.merge(usuario);

This code above does not work, do I have to change the mapping, I know, perhaps creating a UserPermission? The mapping is the way the reverse engineering engine created. If I remove a user, the permissions are automatically deleted. My problem is to delete and also add a new permission from a user already registered in the bank. I've also used Cascade and to no avail.

Can you help?

Note: to simplify I changed the code by the editor, may have some error that if put in the IDE will not compile, the idea is to show the base. I also tried other ways to remove / add that I thought it best not to quote since they did not work

Thank you!

    
asked by anonymous 10.01.2016 / 04:40

2 answers

2

First of all I believe that your mapping can be simplified, especially the join table.

To centralize the primary key mapping of entities, I created a BaseEntity , like this:

@MappedSuperclass
public abstract class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

}

The Usuario entity looks like this:

@Entity
@Table(name = "tb_usuario")
public class Usuario extends BaseEntity {

    @Column(name = "no_usuario", unique = true, nullable = false, length = 50)
    private String nome;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) 
    @JoinTable(name = "tb_usuario_permissao", joinColumns = {
                @JoinColumn(name = "id_usuario", nullable = false)
            }, inverseJoinColumns = {
                @JoinColumn(name = "id_permissao", referencedColumnName = "id", nullable = false)
            }
    )
    private Set<Permissao> permissoes;

}

Here we point out the following:

  • Do not mix business logic with your data model, so do not use the user name as the primary key, consider it a unique key, and have
  • inclusion of cascade: I do not know if you will need this, only include not to persist each permission separately. For more details on the behavior of each type see the JPA specification a>
  • Since we have a primary key in tb_permissao it does not make sense to have two join columns that refer to business columns. Usually there is more than one merge column when it is a foreign key to another table, for example.

The entity Permissao , looks like this:

@Entity
@Table(name = "tb_permissao")
public class Permissao extends BaseEntity {

    @Column(name = "no_permissao", unique = true, nullable = false, length = 50)
    private String nome;

    @Column(name = "no_metodo", unique = true, nullable = false, length = 50)
    private String metodo;

}

Unless you really need to know all users have a certain permission do not need to map, even in this case I would prefer to make a query.

Now, the removal part, let's look at this excerpt:

usuario.getPermissoes().remove(0);

Well, with this model above the removal occurred as desired, logically when there are one or more permissions.

As an example (I'm using lombok ), the insertion test looks like this:

final Permissao permissao1 = Permissao.builder().nome("permissao1").metodo("metodo1").build();
em.persist(permissao1);

final Permissao permissao2 = Permissao.builder().nome("permissao2").metodo("metodo2").build();
em.persist(permissao2);

final Usuario usuario = Usuario.builder().nome("Bruno").permissao(permissao1).permissao(permissao2).build();
em.persist(usuario);

And so:

final Permissao permissao3 = Permissao.builder().nome("permissao3").metodo("metodo3").build();
final Permissao permissao4 = Permissao.builder().nome("permissao4").metodo("metodo4").build();

final Usuario usuario = Usuario.builder().nome("César").permissao(permissao3).permissao(permissao4).build();
em.persist(usuario);

Removal like this:

final Usuario usuario = em.find(Usuario.class, 1L);
usuario.getPermissoes().remove(0);
em.merge(usuario);

And adding a new permission to an existing user like this:

final Permissao permissao = em.find(Permissao.class, 1L);
final Usuario usuario = em.find(Usuario.class, 1L);
usuario.getPermissoes().add(permissao);
em.merge(usuario);

And of a still non-existent like this:

final Permissao permissao5 = Permissao.builder().nome("permissao5").metodo("metodo5").build();
final Usuario usuario = em.find(Usuario.class, 1L);
usuario.getPermissoes().add(permissao5);
em.merge(usuario);

As you did not enter further details in your question, if you do not solve your problem, please let me know more details, such as your PermissaoPK (to see if you really need it).

    
10.01.2016 / 20:22
0

Assuming you get the id of the user and permission you want to be removed, delete can be done manually through a query.

Ex:

entityManager.createQuery("DELETE FROM USUARIO_PERMISSAO up WHERE up.usuarioId = :usuarioId AND up.permissaoId = :permissaoId");
  

Because using remove(index) as you are trying to use, even   which was partially functioning, the order of the List<T>   to receive values referring to that user , may end up not   being the desired permission thus having inconsistency when deleting.

In the above solution, the change occurs only in the table that maintains the relationships , having the ids , makes explicit what you want to remove .

    
11.01.2016 / 01:16