#ManyToOne in self-referencing entity with composite key - #IdClass, java, hibernate - java

Spent 3 days looking for a solution and finally I came here for community wisdom.
I have self-referencing entity like follows:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#IdClass(CompositeUserId.class)
#Table(name = "user", schema = "dbo")
public class User implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Id
#Column(name = "first_name")
private String firstName;
#Id
#Column(name = "last_name")
private String lastName;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
private User parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
private Set<User> children;
my CompositeUserId.class:
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class UserCompositeId implements Serializable {
private Integer id;
private String firstName;
private String lastName;
When I try retrieve all data from my user table I get error:
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ...User with id UserCompositeId#19e66569; nested exception is javax.persistence.EntityNotFoundException:
I suppose there might be some kind of mistake in the #JoinColumn block.
Here is the sql query causing the error:
SELECT *
FROM dbo.user ur1 LEFT OUTER JOIN dbo.user ur2 ON ur1.first_name=ur2.first_name AND ur1.parent_id=ur2.id AND ur1.last_name=ur2.last_name
WHERE ur1.first_name='First Name' AND ur1.id=130 AND ur1.last_name='Last Name'
I ensured that the request does not return anything in the database by running it manually, but found out that if I will change id to parent_id it will return data, so again, probably some mistake in #JoinColumn block

You need you use #NotFound(action=NotFoundAction.IGNORE). If there is no records then it will assign null to it.
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
#NotFound(action=NotFoundAction.IGNORE)
private User parent;

The foreign key columns need different names than the primary key columns, e.g. like this (notice the 'parent' prefix in name):
#JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = true)
#JoinColumn(name = "parent_firstname", referencedColumnName = "firstname", updatable = false, insertable = true)
#JoinColumn(name = "parent_lastname", referencedColumnName = "lastname", updatable = false, insertable = true)
#ManyToOne(fetch = FetchType.EAGER)
private UserEntity parent;
Additionally, the insertable value should be true, since otherwise no associations are persisted during the insert.
Be aware that a parent needs to be saved before a child can be associated. Since the parent fk is not updatable, is must be set on a newly created child before it is saved.
Feel free to have a look at this project containing the complete sample code:
Entity:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/main/java/org/demo/UserEntity.java
Integration-Test:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/test/java/org/demo/DemoApplicationTests.java
Also it is good practice to synchronize bidirectional associations:
https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
The sample entity could e.g. use some methods like these:
public void addChild(UserEntity child) {
child.parent = this; // sync owning side of the association
this.children.add(child);
}
public void setParent(UserEntity parent) {
parent.addChild(this);
}

Related

jpa one to one relationship issue when persist

I have 2 models with one to one relationship
#Entity
#Table(name = "Form_Item_Production")
public class FormItemProduction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id", nullable = false)
private Long itemId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
getter and setter
}
The other one
#Entity
#Table(name = "App_Lookup")
public class AppLookup {
#Id
#GeneratedValue
#Column(name = "Lookup_Id", nullable = false)
private Long lookupId;
#Column(name = "Lookup_Name", length = 30, nullable = false)
private String lookupName;
getter and setter
}
When I try to persist to save the formitemproduction values
public boolean insertItem(List<FormItemProduction> f) {
for (FormItemProduction i : f) {
System.out.println("A" + i.getAppLookup().getLookupId()); // prints the correct id of applookup
i.setItemId(null);
entityManager.persist(i);
}
entityManager.flush();
return true;
}
I get this error
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.mamee.factory.security.entity.AppLookup
From my understanding this is unidirectional one to one mapping so I don't quite understand why I'm getting the error detached?
You have itemId which can't be null.
#Column(name = "item_id", nullable = false)
private Long itemId;
and you actually set itemId to null
i.setItemId(null);
So this line
entityManager.persist(i);
Is not able to persist your data.
Fixed by changing from:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
to:
#OneToOne
#JoinColumn(name = "shift_lookup_id", insertable = true, updatable = true)
private AppLookup appLookup;
You have set cascade to ALL:
#OneToOne(cascade = CascadeType.ALL)
Which means you are cascading all operations down to the related field AppLookup.
You can set cascade to none, and you will no longer get your error, but no db operations will be executed for AppLookup field.

HIbernate/JPA Annotation Join ManyToOne with multiple columns but with conditional OR

Let's say I have these Entities
Company,User,Project and Recommendation
But what I want to highlight is the Project and Recommendation table.
Below is the entity sample.
//Project entity
#Column(name = "id")
private String id;
#Column(name = "user_id")
private String userId;
#Column(name = "company_id", nullable = true)
private String companyId;
//Recommendation entity
#Column(name = "id")
private String id;
#Column(name = "user_id")
private String userId;
#Column(name = "company_id", nullable = true)
private String companyId;
#OneToMany(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "user_id", nullable = true, insertable = false, updatable = false),
#JoinColumn(name = "id", referencedColumnName = "company_id", nullable = true, insertable = false, updatable = false)
})
private List<Project> projects;
Above tables are just illustration, sorry if that doesn't really make sense.
I want to join Project table inside Certification table.
They both are not directly related, so I need to join using these 2 columns, userId and companyId.
However, companyId is nullable. And whenever it is null, I still want to join the table only by userId.
What I want to achieve is probably along this line:
Select from Recommendation rec Join Project project
on rec.user_id = project.user_id and (rec.company_id = project.company_id OR rec.company_id is null)
Is it possible to use hibernate / JPA annotations to achieve this?
My project uses Hibernate 5 recently

Hibernate: #PreRemove executed, but records didn't delete

There is a binding entity of many-to-many relation. When I delete dependent entities, I expect this entity to be deleted too. The preRemove method is called, but hibernate does not build a deletion request.
What am I doing wrong?
Please help, been 3 days trying to solve the problem((
#Entity
#Data
#Table(name = "SOME_TABLE_NAME")
#IdClass(ExpertAndRequestId.class)
public class ExpertAndRequest implements Serializable {
#Id
private Long managerId;
#Id
private Long requestId;
private String comment;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "managerId", updatable = false, insertable = false, referencedColumnName = "id")
//#OnDelete(action = OnDeleteAction.CASCADE)
private Manager manager;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "requestId", updatable = false, insertable = false, referencedColumnName = "id")
private ParticipantRequest request;
#PreRemove
public void preRemove(){
// THIS METHOD EXECUTED
}
}

JoinColumn to an foreign key

I'm trying to achieve this with hibernate:
My codes is as follows:
Device model
#Entity
public class Device {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user", nullable = true, referencedColumnName = "ID")
private User user;
...
}
Statistics model
#Entity
public class Statistic {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "fk_device", nullable = false, referencedColumnName = "ID"),
#JoinColumn(name = "fk_device_user", nullable = false, referencedColumnName = "fk_user") })
private Device device;
...
}
However, there's an error saying there's no logical column fk_user in device.
Caused by: org.hibernate.MappingException: Unable to find column with logical name: fk_user in device
I suppose it is because fk_user is a foreign key. But how can I solve this? Thanks.
Based on you schema i would map the Device entity as follows:
public class Devince{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = true)
private User user;
...
}
}
You dont need the referencedColumnName as you are referring to the User entity primary key. If you would refer to non-primary key column, then that would be necessary.
Regarding the Statistic entity:
public class Statistic {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "device_id", nullable = false),
#JoinColumn(name = "device_user_id", nullable = false,
referencedColumnName = "user_id") })
...
}
}
Again, you only need the referencesColumnName in the second #JoinColumn.

JPA/EclipseLink - Undesirable Foreign Key created during table create/update on startup

I have the following #Entity classes
#Entity
public class Asset {
#Id
private long id;
#Enumerated(EnumType.STRING)
private TagType tagType;
private String tagId;
#OneToOne(cascade = CascadeType.ALL, optional = true, orphanRemoval = true)
#JoinColumns(value = {
#JoinColumn(name = "tagId", referencedColumnName = "tagId", nullable = true, insertable = false, updatable = false),
#JoinColumn(name = "tagType", referencedColumnName = "tagType", nullable = true, insertable = false, updatable = false)})
private TagInfo tagInfo;
...
}
and
#Entity
public class TagInfo {
#Id
private String tagId
#Id
private TagType tagType
...
}
When EclipseLink start using "eclipselink.ddl-generation" = "drop-and-create-tables", it creates the ASSET table with the following constraint:
CONSTRAINT FK_ASSET_TAGTYPE FOREIGN KEY(TAGTYPE,TAGID) REFERENCES TAGINFO(TAGTYPE,TAGID))
Is there anyway to have EclipseLink NOT create the above constraint on the Asset table?

Categories