java – Problems removing @ManyToMany element with @JoinTable


I'm having trouble deleting a @ManyToMany relationship in JPA. In summary, I have 3 tables: USER, PERMISSAO and USUARIO_PERMISSAO which 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 a user that already exists in the base, follows a simplified model:

public class Usuario {

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

        , 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;

public class Permissao {

    private PermissaoPK id;

    private List<Usuario> usuarios;

My problem is with the mapping of the permissions field of the User class, when I insert a new User in the database, adding the permission works, following code that works:

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

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 suppose that a user has 2 permissions and I want to delete one of them, how do I do that? Here's an example that doesn't work :

Usuario usuario = entityManager.find(Usuario.class, "USUARIO_01");

This code above doesn't work, do I have to change the mapping, I don't know, maybe creating a UsuarioPermissao entity? The mapping is the way the reverse engineering engine created it. If I remove a user, the permissions are automatically deleted. My problem is to delete and also add a new permission of a user already registered in the bank. I've also used Cascade and to no avail.

Can you help?

Note: for simplicity I changed the code by the editor, there may be some error that if you put it in the IDE it won't compile, the idea is to show the base. I also tried other ways to remove/add that I thought it was better not to mention them as they didn't work.



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

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

public abstract class BaseEntity {

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


The Usuario entity was like this:

@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 score the following:

  • don't mix business logic with your data model, so don't use the username as the primary key, consider it a unique key and have
  • cascade include: I don't know if you'll need this, it just includes so you don't need to persist each permission separately. For more details on the behavior of each type see the JPA specification
  • since we have a primary key in tb_permissao it doesn't make sense to have two join columns that reference business columns. There is usually more than one join column when it is a foreign key to another table, for example.

The Permissao entity looked like this:

@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 you don't need to map, even in this case I would prefer to do a query.

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


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

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

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

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

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

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();

Removal like this:

final Usuario usuario = em.find(Usuario.class, 1L);

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);

And from a non-existent one like this:

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

As you didn't provide further details in your question, if you don't solve your problem, I ask you to provide more details, such as your PermissaoPK (to find out if you really need it).

Scroll to Top