Felipe Mosso
Felipe Mosso

Reputation: 3927

JPA many to many relation not inserting into generated table

I have a many-to-many relation in my project and although I'm able to write in my two Entities table, the relational table does not get anything written.

Here's how I'm declaring this using JPA annotations:

Professor.java

@Entity
@Table(name = "Professor")
public class Professor implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "idProfessor", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "ALUNO_PROFESSOR",
            joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
            inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
    private List<Aluno> alunoList;

    // getters and setters
}

Aluno.java

@Entity
@Table(name = "Aluno")
public class Aluno implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "idAluno", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToMany(mappedBy = "alunoList", fetch = FetchType.EAGER)
    private List<Professor> professorList;

    // getters and setters
}

And here is the service layer to insert into database:

@Autowired
private AlunoDao alunoDao;

@Autowired
private ProfessorDao professorDao;

@RequestMapping(value = RestUriConstants.SUBMETER, method = RequestMethod.POST)
public @ResponseBody JsonResponse submeter(@RequestBody final Aluno aluno) {

    Professor professor = professorDao.find(1);
    aluno.setProfessorList(Arrays.asList(professor));
    alunoDao.persist(aluno);

    ...
}

In this case, please consider that I already have an entry with id "1" for Professor.

As I said, it does write on Aluno and Professor table but does NOT write anything into ALUNO_PROFESSOR table.

I've already taken a look at these three kind of similiar questions but none of them could help me:

Hibernate and Spring: value of many-to-many not inserted into generated table

JPA many-to-many persist to join table

How to persist @ManyToMany relation - duplicate entry or detached entity

EDIT - Adding more code snippets

JpaAlunoDao.java

@Repository
public class JpaAlunoDao implements AlunoDao {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void persist(Aluno aluno) {
        em.persist(aluno);
    }
}

JpaExercicioDao.java

@Repository
public class JpaExercicioDao implements ExercicioDao {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void persist(Exercicio exercicio) {
        em.persist(exercicio);
    }
}

Upvotes: 3

Views: 22012

Answers (5)

Some times the problem is in the way that you insert the values. I explain with an example.

User user = userFacade.find(1);      
Post post = new Post("PRUEBA");
user.addPostCollection(post);
post.addUserCollection(user);
postFacade.create(post);

You have to add the post in postCollection and the user in userCollection. You have two add the correspond entity in the collections of the two entities.

Class USER

  public void addPostCollection(Post post) {
    if(postCollection == null){
        postCollection = new ArrayList<Post>();
    }
    postCollection.add(post);
}

@ManyToMany(mappedBy = "userCollection")
private Collection<Post> postCollection;

Class Post

    public void addUserCollection(User user){
        if(userCollection == null){
            userCollection = new ArrayList<User>();
        }
        userCollection.add(user);
    }

 @JoinTable(name = "USER_POST_R", joinColumns = {
 @JoinColumn(name = "POSTID", referencedColumnName = "ID")}, inverseJoinColumns = {
 @JoinColumn(name = "USERID", referencedColumnName = "ID")})
    @ManyToMany
    private Collection<User> userCollection;

Also, it is important to instance the list, for example userCollection = new ArrayList(). If you do not, the value won´t insert.

Upvotes: 0

kafkas
kafkas

Reputation: 513

...

Normally, Hibernate holds the persistable state in memory. The process of synchronizing this state to the underlying DB is called flushing.

When we use the save() method, the data associated with the save operation will not be flushed to the DB unless and until an explicit call to flush() or commit() method is made.

If we use JPA implementations like Hibernate, then that specific implementation will be managing the flush and commit operations.

One thing we have to keep in mind here is that, if we decide to flush the data by ourselves without committing it, then the changes won't be visible to the outside transaction unless a commit call is made in this transaction or the isolation level of the outside transaction is READ_UNCOMMITTED.

...

From Difference Between save() and saveAndFlush() in Spring Data JPA by baeldung: https://www.baeldung.com/spring-data-jpa-save-saveandflush

employeeRepository.saveAndFlush(new Employee(2L, "Alice"));

or

  employeeRepository.save(new Employee(2L, "Alice"));
  employeeRepository.flush();

Upvotes: 1

Vahe Gharibyan
Vahe Gharibyan

Reputation: 5683

Its not necessary to set many-to-many relationship on both entities.

Just remove session.setFlushMode(FlushMode.MANUAL);

By default HibernateTemplate inside of Spring set FlushMode.MANUAL

this is source code from HibernateTemplate.

 if (session == null) {
            session = this.sessionFactory.openSession();
            session.setFlushMode(FlushMode.MANUAL);
            isNew = true;
        }

Upvotes: 0

Kien Ngo
Kien Ngo

Reputation: 41

I have the same issue. I swapped where the full mapping declare to the class that we will use save() function on. In your case:

public class Aluno {
  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(name = "ALUNO_PROFESSOR",
        joinColumns = @JoinColumn(name = "idAluno"),
        inverseJoinColumns = @JoinColumn(name = "idProfessor")
  private List<Professor> professorList;
}

public class Professor {
  @ManyToMany(fetch = FetchType.EAGER, mappedBy = "professorList",)  
  private List<Aluno> alunoList;
}

and It worked fine.

Upvotes: 4

manish
manish

Reputation: 20135

Try this:

public class Professor {
  @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  @JoinTable(name = "ALUNO_PROFESSOR",
        joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
        inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
  private List<Aluno> alunoList;
}

public class Aluno {
  @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  @JoinTable(name = "ALUNO_PROFESSOR",
        joinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"),
        inverseJoinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"))
  private List<Professor> professorList;
}

This will ensure that the metadata for the many-to-many relationship is available on both the entities and that operations on either side of the relationship are cascaded to the other side.

I also suggest replacing FetchType.EAGER with FetchType.LAZY for better performance because this has the potential of loading a very large dataset.

Upvotes: 15

Related Questions