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.pngI 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.pngIf 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.