Bidirectional mapping of entities in Hibernate and spring mvc - mysql

I am trying to map two entities User(userid,name,password,address) and Role(roleId,roleName).
I am trying to do bidirectional OneToMany mapping between User and Role.
My User entity:
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="USER_ID"),
inverseJoinColumns = #JoinColumn(name="ROLE_ID")
)
public Set<Role> role;
My Role entity:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
private User user;
Now the corresponding intermediate table created in the database has following attributes.
userrole-> attributes( user_userId,ROLE_ID,USER_ID )
Now when i add set of items to a user. ROLE_ID and USER_ID of userrole table gets populated but user_userId remain null. What is the purpose of user_useerId.Should i manully make it primary key in the table user_userId?
UPDATE:
i did the following editing in Role entity
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name = "ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
And now when i checked the table in the database, the "userrole" table is perfectly ok and contains only (USER_ID,ROLE_ID)
I want to know why shouldn't i map two entities though this way?

In User entity you have declared #OneToMany with Role and also given details about the Join table using #JoinTable.
So if you need bidirectional between User and Role entities, then adding user property in Role entity with #ManyToOne declaration is sufficient, so no need of having #JoinTable once again.
Now coming to user_userId - in your Role entity you have declared the #JoinTable annotation and this annotation needs two column names for Join Table.
#JoinTable(
name="JoinTableName",
joinColumns = #JoinColumn(name="ID_NAME_FOR_THIS_ENTITY"),
inverseJoinColumns = #JoinColumn(name="ID_NAME_FOR_THE_MAPPING_ENTITY")
)
but you have provided one with name ROLE_ID and ignore the second name which is inverseJoinColumns that points to User entity.
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
so hibernate should make a guess about the column name for inverseJoinColumns, so it creates a column with name as combination of the entity name in lower case (which is user in your case) then the identifier in that entity separated by underscore (which is userId I guess based on column name). So finally the column name becomes user_userId.
Update:
If you need bidirectional relationship then you need to declare your entities like this:
#OneToMany(mappedBy="user", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set<Role> role;
Here you are telling to hibernate that User entity has one-to-many relationship with Role entity and the relationship is bi-directional and the Role entity has a property called user.
Now in your Role entity you will give details about the JoinTable like this:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
The property name user in Role entity should match with the mappedBy attribute that you have declared in your User entity for the #OneToMany annotation. Adding the user field to Role entity makes the relationship bi-directional.

A (typical) bidirectional mapping has one side that is maintaining the relationship and the other side follows this relation ship.
This mean when you modifiy the maintaining side of the relation ship and save this change, then it gets stored in the database. While when you modifiy the following side, then nothing gets changed in the database. (it gets only updated when you load the entity).
A mapping would looks like:
#Entity
public class User {
...
#OneToMany(mappedBy="user") //mappedBy makes this side to the following side
public Set<Role> role;
...
}
#Entity
public class Role {
...
#ManyToOne
//#JoinTable... when you need it
private User user;
...
}
But this mapping is strange: because it mean that every User can have serveral roles, but each role belongs to exactly one user. -- If this is not intended, then switch ManyToOne and OneToMany or move over to ManyToMany

Related

Find rows in a unidirectional many-to-many mapping from the non-owning side in JPA/Nativequery/jpql

I have 2 entities that have a unidirectional many-to-many relationship, with a junction table between to keep track of the relationship. These entities are: Cat and Owner. Owner is the owning side of the relationship, so there is no information about owners in Cat. How do I write a nativequery to get all owners when providing a list of cats, the result of owners should be distinct. I tried solving this with Specifications, but OracleDB does not allow query.distrinct(true) on CLOB(Owner has a multiple CLOB fields). The class looks like this:
Public class Owner {
Long id;
#JoinTable(
name= "owner_cat",
JoinColumns = #JoinColumn(name = "Owner_id"),
inverseJoinColumns = #JoinColumn(name = "Cat_id")
)
Set<Cat> cats;
}
Public class Cat {
Long id;
}
Each row in the junction table have Owner id and Cat id.
I would appreciate if anyone can show me how I can solve this with either nativequery/jpql or jpa Specifications.
I tried getting all Owners by providing a list of Cats

Hibernate create relation without relational table

Is it possible to map hibernate entities without using another table that is mapping them ?
When I create #OneToMany and #ManyToOne relation between 2 entities, hibernate always creates another table in the database to map the relation, I would like to map 2 entities directly using column in the mapped entity like this:
"InstalledApp" entity:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class, mappedBy = "id")
private List<InstalledAppPort> ports;
"InstalledAppPort" entity:
#ManyToOne (targetEntity=InstalledApp.class, fetch=FetchType.LAZY)
#JoinColumn(name = "iappId")
private InstalledApp iapp;
When using the code above, the list is always empty and I do not know why. Technically this mapping should work but it is not. No exception thrown.
Solved by using:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class)
#JoinColumn(name = "iappId", referencedColumnName = "id")
private List<InstalledAppPort> ports;

Why #JoinTable(name="user_role") not allowing multiple user_id with role_id

Am using springboot with hibernate,
My Entity classes looks like below :
#Entity
#Table(name="tbl_user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_Id")
private long userId;
#Column(name="userName")
private String userName;
#Column(name="passWord")
private String passWord;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name="user_role")
private Collection<Role> roleList;
My second entity looks like below :
#Entity
#Table(name="tbl_role")
public class Role {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_Id")
private long roleId;
#Column(name="roleName")
private String roleName;
When I insert first user with role as manager(pkid=1), admin(pkid=2) its success but while I tried to insert 2nd user with role as Manager*pkid=1, admin(pkid=2, serviceUser(pkid=3) it's not allowing me to insert second user with below exception
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2' for key 'UK_ixctfj5iq0enl7iktlpo7wxct'
Can somebody help me why this constraint is getting creating while generating tables, how can i insert 2nd user into DB ?
If you use OnetoMany on role_list you are effectively saying that a single User will point to many Roles and that a Role will point to only one User. This will be enforced with a unique key constraint placed on the join table. If you have the SQL statements printed out you will see it when the schema is created. Something along the lines of:
alter table user_role add constraint UK_ixctfj5iq0enl7iktlpo7wxct unique (role_id)
In your requirement, you also have a single Role used by many Users. Your admin role primary key is 2 and you want to be able to assign it to more than one user. Your relationship is a ManyToMany for the role_list.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="user_role")
private Collection<Role> roleList;
When you change the annotation, you will still have a join table, but no constraint will be added.

JavaEE 1:1 Unidirectional Relationship

I am confused about how the JPA handles a 1:1 unidirectional relationship when I auto generate my tables from my entity classes vs. how I would make those tables if I was creating the SQL tables myself.
My question concerns how the foreign keys are set up.
Assuming I have a Customer entity and an Address entity like this:
#Entity
public class Customer{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
private Address address;
//setters and getters
}
#Entity
public class Address{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String streetName;
//setters and getters
}
If I generate the sql tables from these two entity classes then the Customer table will have a foreign key column referencing the Address table.
Is there a way to have a OneToOne unidirectional relationship where the JPA sticks the foreign key on the target table(the address table) instead of on the Customer table as it does in my code example, or do I have to make it a bidirectional relationship and show more explicit ownership with the #Mapping annotation to achieve this kind of table structure?
To have the foreign key appear on the other table for a 1-1 unidirectional relationship you would need to switch the 'owning' side: place customer in Address and remove address from Customer. Then the foreign key for customer would be in the address table.
First of all, you have to map your address_id(or something like that) from the Customer entity. For example using below code, you are referencing the Address from the Customer.
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="ADDRESS_ID")
private Address address;
While deleting any entry from the address table, you must first delete the entry from the Customer table in order to avoid the foreign key violation error. Then you can delete from Address table. JPA doesn't handle this relationship while deleting any row from table.

Hibernate #OneToOne mapping with reverse reference and CascadeType.ALL on owner does not persist the child

We have the following entity relation ship. User is the parent entity.DistrictUserDetail is the child.
#Entity
#Table(name="USERS")
public class User implements Serializable {
#Id
#Column(name="ID", nullable=false)
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
private String id;
#OneToOne(cascade= {CascadeType.ALL})
#PrimaryKeyJoinColumn(name="ID", referencedColumnName="USER_ID")
public DistrictUserDetail districtUserDetail;
... other properties..
This is the parent entity.
The child entities primary key is a foreign key to User entity, and we have defined its key properties as:
#Entity
#Table(name="DISTRICT_USER_DETAIL")
public class DistrictUserDetail implements Serializable{
#Id
#Column(name="USER_ID")
#GeneratedValue(generator="foreign")
#GenericGenerator(name="foreign", strategy = "foreign",
parameters={#Parameter(name="property",value="user")})
protected String userId;
#OneToOne(mappedBy="districtUserDetail")
User user;
... other properties
So, when we create a new User, and create a new DistrictUser, and set the DistrictUser to the User and save User, we expect that the User is saved and due to cascadeAll, DistrictUser is also saved.
This is our test:
public void testSaveUserWithDistrictUserDetails() throws Exception {
User user = new User();
user.setFirstName("John");
user.setLastName("Doe");
user.setUserName("U11111");
DistrictUserDetail userDetail = new DistrictUserDetail();
userDetail.setIsLoginAllowed(Boolean.TRUE);
userDetail.setIsSuperintendent(Boolean.TRUE);
user.setDistrictUserDetail(userDetail);
User newUser = dao.save(user);
assertTrue(newUser!=null);
assertTrue(newUser.getId()!=null);
}
But we encounter the following hibernate exception:
Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: user
at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:44)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:315)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:283)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:238)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:688)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:670)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:245)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:269)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:217)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:170)
at org.hibernate.engine.Cascade.cascade(Cascade.java:131)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:352)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:283)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:238)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:678)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:662)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:666)
at org.springframework.orm.hibernate3.HibernateTemplate$23.doInHibernate(HibernateTemplate.java:817)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
... 49 more
If User entity can invoke the save of DistrictUserDetail, then why can't DistrictUserDetail see the parent User (and its new id) and use it to save it.
We are using the hibernate version:3.2.7.ga
Underlying database is : MySQL
Any help will be appreciated.
Thanks
userDeatil.user is null, which is what the exception is complaining about.
I would suggest removing the property and not having a bi-directional relationship. Otherwise, user.setDistrictUserDetail() also needs to set the user property.
You should set optional attribute of OneToOne to false like this:
#OneToOne(mappedBy="districtUserDetail", optional = false)
User user;
And also, you should set DistrictUserDetail.user to a not-null entity of course.