3

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);
    }
}
Community
  • 1
  • 1
Felipe Mosso
  • 3,907
  • 11
  • 38
  • 61

5 Answers5

15

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.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
manish
  • 19,695
  • 5
  • 67
  • 91
  • thanks for your help! Unfortunetelly it now gives me an exception: "org.hibernate.PersistentObjectException: detached entity passed to persist: com.personal.online.model.Professor" when I call persist for the "aluno" object with the professor associated.. are you sure is it normal to add JoinTable annotation in both entities? Because every single tutorial I've seen just used this annotation in one of the entities – Felipe Mosso Feb 27 '15 at 02:18
  • I have taken the code from my production application except for the entity names so I am sure it works. Are you sure you have copied everything, including the cascade type? It is preferable to provide the JoinTable annotation on only one side if that side is clearly the owner of the relationship. However, from your entity names it does not seem like any of the entities has higher priority than the other. In such cases where both entities are equal, it is better to annotate both the sides. – manish Feb 27 '15 at 02:24
  • yes, I just copied and pasted your code here.. The first class refers to Professor and the second to Aluno actually, right? Anyway, I think this exception is related to the id of my entities.. May I ask you to check in your production application if there is any difference between the declarations of those id's? – Felipe Mosso Feb 27 '15 at 02:34
  • Between `professorDao.find(1)` and `alunoDao.persist(aluno)` do you have the same Hibernate `Session`? If `professorDao.find` and `alunoDao.persist` do something like `sessionFactory.getSession()`, both will get a different `Session`. So, when you call `alunoDao.persist`, the Professor instance will not be attached with the `Session` obtained inside `alunoDao.persist` and you will get an exception to the effect of the Professor instance being a detached entity instance. You could get rid of `referencedColumnName` because the reference is assumed to be to the primary key. – manish Feb 27 '15 at 04:17
  • I'm not sure if I have the same Hibernate session.. I'm new with JPA + Hibernate but I've edited my question adding some more of code.. Please re-take a look at the service code (I use Autowired annotation) and the DaoImplementation. Please kindly let me know if this helps in something :) – Felipe Mosso Feb 27 '15 at 23:00
  • I am afraid I have given you all the information I could. You can take a look at a [sample](https://github.com/manish-in-java/spring-orm-relation) I have created with your code that shows that the changes I suggested work as expected. You now seem to have run into a problem that is specific to your application. It is therefore a debugging problem now that only you can diagnose and is beyond the scope of an SO question. – manish Feb 28 '15 at 03:05
  • 1
    I finally discovered the problem, you were right, the error was in my application. That "PersistentObjectException" happened because I should use EntityManager.merge instead of persist. This link helped me: http://stackoverflow.com/questions/1139496/how-can-you-replicate-hibernates-saveorupdate-in-jpa Thank you for helping me out with this issue, I already accepted your answer! – Felipe Mosso Feb 28 '15 at 22:56
4

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.

Kien Ngo
  • 41
  • 3
1

...

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();
kafkas
  • 475
  • 1
  • 4
  • 11
0

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;
        }
Vahe Gharibyan
  • 5,277
  • 4
  • 36
  • 47
0

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.