Java – Hibernate Annotatoin – How to Join Three Tables with many to many relationship

hibernatehibernate-annotationsjavanhibernate-mapping

  1. I have 3 Entity USER, APPLICATION, and ROLE .

    1. I want there IDs to be joined in a table named USER_APP_ROLE(user_ID, application_ID, role_ID) with many to many relationship between them.
    2. Reason I want this structure is- I have requirement of allowing the user to work on multiple application with multiple roles.
    3. I have done the following codes:

    User.java
    @ManyToMany (targetEntity=Role.class)
    @JoinTable(name="USER_APPLICATION_ROLE",
    joinColumns=@JoinColumn(name="USER_ID"),
    inverseJoinColumns=@JoinColumn(name="ROLE_ID"))
    private Collection roles;

      @ManyToMany (targetEntity=Application.class)
      @JoinTable(name="USER_APPLICATION_ROLE",
      joinColumns=@JoinColumn(name="USER_ID"),
      inverseJoinColumns=@JoinColumn(name="APPLICATION_ID"))
      private Collection<Application> applications;
    
      Role.java
      @ManyToMany(mappedBy="roles", targetEntity=User.class)
      private Collection<User> users = new ArrayList<User>();
    
      Application.java
      @ManyToMany(mappedBy="applications", targetEntity=User.class)
      private Collection<User> users = new ArrayList<User>();
    
    1. When I tried to run the following test:

    user.getRoles().add(role1);
    user.getRoles().add(role2);

    role1.getUsers().add(user);
    role1.getUsers().add(user);

    role2.getUsers().add(user);
    role2.getUsers().add(user);

    user.getApplications().add(app1);
    user.getApplications().add(app2);

    app1.getUsers().add(user);
    app2.getUsers().add(user);

    ……

    session.beginTransaction();
    session.save(user);
    session.save(role1);
    session.save(role2);
    session.save(app1);
    session.save(app2);

    I get the following error:
    
    
    
          Hibernate: select seq_cm_user.nextval from dual
           Hibernate: select seq_role.nextval from dual
    Hibernate: select seq_role.nextval from dual
    Hibernate: select seq_application.nextval from dual
    Hibernate: select seq_application.nextval from dual
    Hibernate: insert into CM_USER (EMAIL, FIRST_NAME, LAST_NAME, MIDDLE_NAME, USERNAME, ID) values (?, ?, ?, ?, ?, ?)
    Hibernate: insert into CM_ROLE (DESCRIPTION, ID) values (?, ?)
    Hibernate: insert into CM_ROLE (DESCRIPTION, ID) values (?, ?)
    Hibernate: insert into CM_APPLICATION (CODE, DESCRIPTION, ID) values (?, ?, ?)
    Hibernate: insert into CM_APPLICATION (CODE, DESCRIPTION, ID) values (?, ?, ?)
    Hibernate: insert into USER_APPLICATION_ROLE (USER_ID, APPLICATION_ID) values (?, ?)
    Hibernate: insert into USER_APPLICATION_ROLE (USER_ID, APPLICATION_ID) values (?, ?)
    Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
        at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:114)
        at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:109)
        at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:244)
        at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1179)
        at org.hibernate.action.CollectionRecreateAction.execute(CollectionRecreateAction.java:58)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:188)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
        at com.hp.gdas.capman.HibernateTest.main(HibernateTest.java:73)
    Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("SYSTEM"."USER_APPLICATION_ROLE"."ROLE_ID")
    
        at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343)
        at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10657)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
        ... 14 more
    

    6.I might have doing this in wrong direction.Please help and suggest me any alternative if i m totally doing in wrong way.Any help is appreciated.

thanks.

Best Answer

It seems that you want the USER_APPLICATION_ROLE table to contain the assignment of a Role to a User in an Application. But that's not what your mapping does. What it does is use this table to contain the roles of a user (ignoring the application), and also the applications of a user (ignoring the role).

Your mapping is thus wrong. You need to map the USER_APPLICATION_ROLE table as an entity, since it's not a pure join table between two entities. You could name it UserApplicationRole for example. A User would have a OneToMany association with UserApplicationRole. UserApplicationRole would have a ManyToOne association with User, as well as with Role and with Application. And I strongly suggest you to add an automatically generated ID column to this table, just like for all your entities.