Is it possible to have the same "OneToOne"-Relationship twice in hibernate? - java

I didn't know how to describe my question in the title but I hope it will do.
So here is my situation.
I use hibernate to map my entities to db tables.
I got one entity like this:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
}
And my CustomEntity
#Entity
#Table(name = "EX.EXAMPLE2")
public class CustomEntity
{
private Entity entity;
public static final String ENTITY = "entity";
#OneToOne
#JoinColumn(name = "ID_ENTITY", nullable = true)
public Entity getEntity()
{
return this.ntity;
}
}
So here is my question: Is it possible to add another CustomEntity relation to Entity? And how do I map it?
Example what I mean:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
private CustomEntity customEntity2;
public static final String CUSTOM_ENTITY2 = "customEntity2";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
}
I only managed it by changing customEntity to a list in Entity.
Greetings

Yes, that is perfectly normal situation. You just need two fields with different mappedBy`, one for each relation
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY1, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY2, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "entity_2_id")
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
And two fields in CustomEntity, one for each mapping
#OneToOne
#JoinColumn(name = "ID_ENTITY_1", nullable = true)
public Entity getEntity1()
{
return this.entity1;
}
#OneToOne
#JoinColumn(name = "ID_ENTITY_2", nullable = true)
public Entity getEntity2()
{
return this.entity2;
}

Related

Hibernate find method sometimes returns proxy

Lately my project encountered a really odd issue with JPA findOne(id) returning a proxy object instead of a full object.
Here is the scenario. Consider the entities and their connections shown below.
#Table(name = "HOUSE")
#Entity
#EqualsAndHashCode
#Setter
#ReadPermission(expression = "user has rights for template snapshots AND has filter")
public class HouseEntity extends VersionedEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "house", fetch = FetchType.LAZY, orphanRemoval = true)
public List<RoomEntity> getRooms() {
return rooms;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "template", fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "ROOMS")
public class Room {
#ManyToOne(fetch = FetchType.LAZY)
public HouseEntity getHouse() {
return house;
}
#OneToMany(mappedBy = "room", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "TABLES")
public class TableEntity{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "HOUSE_ID")
public HouseEntity getHouse() {
return template;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ROOM_ID")
public RoomEntity geRoom() {
return room;
}
As you can see, House has Tables and Rooms, Rooms also has Tables, and each child entity has a connection to its parents.
Add a table to the HouseEntity
Remove the table from the HouseEntity immediately after.
For 1, the houseRepository.findById gets my HouseEntity wrapped in proxy, as if it is lazy loaded.
The weird part is that if I do:
Add a table to a RoomEntity, which is a child of HouseEntity.
Remove the table from RoomEntity.
Then houseRepository.findById returns the HouseEntity without the proxy.
My question here is, why would this happen? Why would the findById method return a proxyed entity in this case? I need to have access to the normal entity without the proxy directly, even if the proxy has the entity populated in the target.

Hibernate not finding map property

So I am getting the exception:
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: se.mulander.cosmos.movies.model.Cast.starredIn in se.mulander.cosmos.movies.model.ExtendedMovie.cast
But I can't really figure out why.
The two objects that I am going to map are:
#Entity
#Table(name = "cast")
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast
{
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "movie_id")
public ExtendedMovie starredIn;
}
and
#Entity
#Table(name = "extended_movie")
public class ExtendedMovie
{
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "starredIn", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
public List<Cast> cast = new ArrayList<>();
}
I have stripped them of some other properties, but in essence this is the relationship that is not working.
So what I don't get is why it says that it is an unknown property, as the property is public and hibernate shouldn't have any problems mapping it.
what is it that I am missing here?
Try something like:
ExtendedMovie :
#Entity
public class ExtendedMovie implements Serializable {
private static final long serialVersionUID = 6771189878622264738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "cast_id", referencedColumnName = "id")
private Set<Cast> cast;
public Set<Cast> getCast() {
return cast;
}
public void setCast(Set<Cast> cast) {
this.cast= cast;
}
}
Cast:
#Entity
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast implements Serializable {
private static final long serialVersionUID = 6771189878622265738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
//Remove extendedmovie from here
//other property getter and setters here
}
This will establish a one-to-many relationship between ExtendedMovie and Cast.

JPA OneToMany MappedBy relationships

I have three classes, Site, GoupIP and IP
A Site has one or many GrouIPs.
A GroupIP has one or many IPs.
Here is the code:
Site
#Entity
#Table(name = "site")
public class Site implements Serializable {
private Set<GroupIp> groups;
#OneToMany(mappedBy = "site", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<GroupIp> getGroups() {
return groups;
}
public void setGroups(Set<GroupIp> groups) {
this.groups = groups;
}
}
GroupIP
#Entity
#Table(name = "groupip")
public class GroupIp implements Serializable {
private Set<Ip> ips;
private Site site;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "site_id")
public Site getSite() {
return site;
}
#OneToMany(mappedBy = "groupip", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<Ip> getIps() {
return ips;
}
public void setIps(Set<Ip> ips) {
this.ips= ips;
}
}
IP
#Entity
#Table(name = "ip")
public class Ip implements Serializable {
private GroupIp groupIp;
#ManyToOne(targetEntity = GroupIp.class,cascade = CascadeType.MERGE)
#JoinColumn(name = "groupip_id", nullable=false)
public GroupIp getGroupIp() {
return groupIp;
}
public void setGroupIp(GroupIp groupIp) {
this.groupIp = groupIp;
}
}
On GroupIp class, I m getting:
In attribute 'ips', the "mapped by" value 'groupip' cannot be resolved to an attribute on the target entity.
Whats wrong on my code ??
The mappedBy name that you have to put in the relationship is the name of the class attribute, not the table name.
So put #OneToMany(mappedBy = "groupIp",... (note the uppercase) instead of #OneToMany(mappedBy = "groupip",...

Hibernate throws "Repeated column in mapping for entity" when using OrderColumn with two lists

I have a tricky problem to let hibernate order two list of the same entity. With some code it could be easier to understand what I wanna do.
#MappedSuperclass
public abstract class AbstractParent {
List<CommonObject> commonObjects;
public abstract List<CommonObject> getCommonObjects();
}
#Entity
#Table
public class Child1 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child1_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
#Entity
#Table
public class Child2 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child2_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
But because hibernate handle the mapping of the column "sort_index", it's mapped twice (for Child1 and Child2). So I get this error :
Caused by: org.hibernate.MappingException: Repeated column in mapping
for entity ... column: sort_index (should be mapped with
insert="false" update="false")
I know that I can resolve this problem if I put two different columns for sorting. But I would like to know if someone has a better solution to give me.
Thanks,
I added a test to replicate your issue on GitHub and it works after modifying the mappings to this:
#MappedSuperclass
public abstract class AbstractParent {
public abstract List<CommonObject> getCommonObjects();
}
#Entity(name = "Child1")
public class Child1 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_1_common_objects", joinColumns = #JoinColumn(name="child1_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "Child2")
public class Child2 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_2_common_objects", joinColumns = #JoinColumn(name="child2_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "CommonObject")
public class CommonObject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}

Hibernate create alias on many to many list

I have four class; UserGroup, UserAccount, Role, UserGroupRoleRelation and my db is IBM DB2
#Entity
#Table(name = "USER_GROUP")
public class UserGroup implements Serializable {
#Id
#Column(name = "USER_GROUP_ID")
#GeneratedValue
private Long id;
......
..
#OneToMany(mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserGroupRoleRelation> userAccountsRole = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "ROLE")
public class Role implements Serializable {
#Id
#Column(name = "ROLE_ID")
#GeneratedValue
private Long id;
......
#OneToMany(mappedBy = "role")
private List<UserGroupRoleRelation> userAccountInGroup = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "USER_GROUP_ROLE_LINE", uniqueConstraints = #UniqueConstraint(columnNames = { "ROLE_ID", "USER_GROUP_ID" }))
public class UserGroupRoleRelation {
#Id
#GeneratedValue
#Column(name = "RELATION_ID")
private Long relationId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_ACCOUNT_USER_GROUP_ROLE_LINE", joinColumns = { #JoinColumn(name = "RELATION_ID") }, inverseJoinColumns = { #JoinColumn(name = "USER_ID") }, uniqueConstraints = #UniqueConstraint(columnNames = { "USER_ID", "RELATION_ID" }))
private List<UserAccount> userAccountList = new ArrayList<UserAccount>();
#ManyToOne
#JoinColumn(name = "USER_GROUP_ID")
private UserGroup userGroup;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
}
#Entity
#Table(name = "USER_ACCOUNT")
public class UserAccount implements Serializable {
#Id
#Column(name = "USER_ID")
#GeneratedValue
private Long id;
.....
#ManyToMany(mappedBy = "userAccountList", cascade = CascadeType.ALL)
private List<UserGroupRoleRelation> rolesInGroup = new ArrayList<UserGroupRoleRelation>();
}
I wanna find usergroups of a useraccount and i prepared a method with criteria. its like;
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.add(Restrictions.eq("userAccountsRole.userAccountList", userAccount));
return criteria.list();
}
But when i try to get result of that method, DB2 gives to me DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=3.63.75
Probably its about creating alias on many to many relation. I dont know what should i do to create alias on many to many. How can I get result of that function?
Thank
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.createAlias("userAccountsRole.userAccountList", "userAccountList");
criteria.add(Restrictions.eq("userAccountList.id", userAccount.getId()));
return criteria.list();
}
It works for me. I mean criteria on "id". But I don't understand why I cant check equality on object instead of id when there is ManyToMany list
It is not of creating alias. You are passing an object to hibernate on which it can not make any criteria. You need to create bidirectional mapping for that.Or else if you your requirement is just to fetch the the list of UserAccountList of particular UserGroup class you can follow the below code.
#Override
#Transactional
public List<UserGroup> findUserGroupOf(long userGroupId) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.add(Restrictions.eq("id",userGroupId));
criteria.createAlias("userAccountsRole", "uar");
criteria.setFetchMode("uar.userAccountList",FetchMode.JOIN);
return criteria.list();
}

Categories