-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: JPA 3-way join relationship with ManyToMany relationship
PostPosted: Thu Jun 30, 2016 10:35 am 
Newbie

Joined: Thu Jun 30, 2016 10:21 am
Posts: 3
Hi all,

I'm trying to implement (Using JPA) a 3-way join between classes User , Team and Role like so : http://img11.hostingpics.net/pics/538305stack.png

I don't want Affectation to be an Entity so i followed this solution : http://stackoverflow.com/a/12136698/6503105.
Here is my entities :

User Entity :

Code:
    @Entity
    public class User implements Serializable {

       //...
   
      @JsonDeserialize(keyUsing = TeamrKD.class)
      @JoinTable(name = "Affectation", joinColumns = @JoinColumn(name = "user", referencedColumnName = "ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "role", referencedColumnName = "ID" , nullable = false))
      @MapKeyJoinColumn(name = "team", referencedColumnName = "ID")
      @ElementCollection
      private Map<Team, Role> rolesByTeam = new HashMap<>();
   
      public static class TeamKD extends StdKeyDeserializer {
   
         TeamKD() {
            super(15, Team.class);
         }
   
         @Override
         public Team _parse(String key, DeserializationContext ctxt) throws JsonMappingException {
            ObjectMapper mapper = new ObjectMapper();
            try {
                mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
               return mapper.readValue(key, Team.class);
               
            } catch (Exception ex) {
               throw ctxt.weirdKeyException(_keyClass, key, ex.getMessage());
            }
         }
      }
       //getters & setters

   }


Team Entity :

Code:
  @Entity
    public class Team implements Serializable {

      @JsonDeserialize(keyUsing = RoleKD.class)
      @JoinTable(name = "Affectation", joinColumns = @JoinColumn(name = "team", referencedColumnName = "ID" , nullable = false), inverseJoinColumns = @JoinColumn(name = "user", referencedColumnName = "ID" , nullable = false))
      @MapKeyJoinColumn(name = "role", referencedColumnName = "ID")
      @ElementCollection
      private Map<Role, User> usersByRole = new HashMap<>();
   
      public static class RoleKD extends StdKeyDeserializer {
         // ...
      }
      
       //...
      @JsonIgnore
      public Map<Role, User> getUsersByRole() {
         return usersByRole;
      }
   
      @JsonSetter
      public void setUsersByRole(Map<Role, User> usersByRole) {
         this.usersByRole = usersByRole;
      }
   
    }


Role Entity :

Code:
@Entity
   public class Role implements Serializable {
      //...
      
      @JsonDeserialize(keyUsing = UserKD.class)
      @JoinTable(name = "Affectation", joinColumns = @JoinColumn(name = "role", referencedColumnName = "ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "team", referencedColumnName = "ID", nullable = false))
      @MapKeyJoinColumn(name = "user", referencedColumnName = "ID")
      @ElementCollection
      private Map<User, Team> teamsByUser = new HashMap<>();
   
      public static class UserKD extends StdKeyDeserializer {
         //...
      }
   
      //...
      
      @JsonIgnore
      public Map<User, Team> getTeamsByUser() {
         return teamsByUser;
      }
   
      @JsonSetter
      public void setTeamsByUser(Map<User, Team> teamsByUser) {
         this.teamsByUser = teamsByUser;
      }
   
   }


When i run the application , 'affectation' table is created with only team and user ids as a primary key !! here is the table structure : http://img15.hostingpics.net/pics/300295db2.png

If i want to insert a new user like so (with a rest service) :

Code:
{"firstName" : "test" , "lastName" : "test" , "email" : "test@test.com" , "rolesByTeam":{  "{ 'id' :2 }": { "id": 2}  }}


It works .The following Json is returned :

Code:
{
      "id": 17,
      "firstName": "ttttfffttttt",
      "lastName": "tttfffttt",
      "email": "ttffft@test.com",
      "activated": true,
      "rolesByTeam": {
      "Team{id=2, name='Team1', code='T1'}": {
            "id": 2,
            "name": null,
            "code": null
         }
      }
   }


But i want to have for a user many roles in a team..

I tested with

Code:
private Map<Team, List<Role>> rolesByTeam = new HashMap<>();

instead of :

Code:
private Map<Team, Role> rolesByTeam = new HashMap<>();


but i'm getting this error :

Code:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1054) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:829) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
      at com.myorg.app.ActrakerApplication.main(ActrakerApplication.java:18) [classes/:na]
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
      at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91]
      at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.3.5.RELEASE.jar:1.3.5.RELEASE]
   Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1249) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:860) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:319) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
      ... 21 common frames omitted
   Caused by: org.hibernate.AnnotationException: Use of @JoinTable.inverseJoinColumns targeting an unmapped class: com.myorg.app.domain.User.rolesByTeam[java.util.List]
      at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1151) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:794) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.annotations.MapBinder$1.secondPass(MapBinder.java:107) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1697) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
      at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
      ... 29 common frames omitted


So how can i implement that ?

And How can i have the 3 ids of User , Team and Role in 'affectation' table as a composite primary key ?

Is there a better way to implement this JPA 3-way join ?

Thank you in advance.


Top
 Profile  
 
 Post subject: Re: JPA 3-way join relationship with ManyToMany relationship
PostPosted: Thu Jun 30, 2016 11:04 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
Keep it simple. I would not use @EllementCollection and Map because it complicates the mapping. Try to map the linked table as explained here.


Top
 Profile  
 
 Post subject: Re: JPA 3-way join relationship with ManyToMany relationship
PostPosted: Thu Jun 30, 2016 2:15 pm 
Newbie

Joined: Thu Jun 30, 2016 10:21 am
Posts: 3
Thank you mihalcea_vlad for your quick response . I don't want to rewrite what hibernate can manage , if it is possible to do it with a Map. If not i will create the association as an entity.


Top
 Profile  
 
 Post subject: Re: JPA 3-way join relationship with ManyToMany relationship
PostPosted: Wed Jul 27, 2016 11:10 am 
Newbie

Joined: Thu Jun 30, 2016 10:21 am
Posts: 3
Hi @mihalcea_vlad .How can i map the 3 entities with a 4th one(linked entity) ? The solution in the link gives only a way to deal with bidirectional many-to-many between 2 entities .
Thank you in advance.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.