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.
Related
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);
}
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
}
}
I have tow models:
#Entity
public class Documento implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "appc_contenido_documento", catalog = "appcope",
joinColumns = {
#JoinColumn(name = "id_documento", nullable = false, updatable =
false)}, inverseJoinColumns = {
#JoinColumn(name = "id_contenido", nullable = false, updatable =
false)})
private Set<Contenido> contenidos = new HashSet<Contenido>(0);
}
and
#Entity
public class Contenido implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "appc_contenido_documento", catalog = "appcope",
joinColumns = {
#JoinColumn(name = "id_contenido", nullable = false, updatable =
false)}, inverseJoinColumns = {
#JoinColumn(name = "id_documento", nullable = false, updatable =
false)})
private Set<Documento> documentos = new HashSet<Documento>(0);
}
I donĀ“t find an example this, only I find examples with relationship one to many
How can I select all the documents of a content with CriteriaQuery?
It is usually a good idea to reduce your many to many relationship to one to many. This usually can be done by introducing a bridging entity. It maybe worth adding such a table here.
I have an ER relationship from a legacy DB (MS SQL Server based as below
The way that I'm currently trying to convert this to the JPA 2.1 style is as below
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Orders implements Serializable {
#Id
#GeneratedValue
#Column(name = "OrderNumber", nullable = false)
private Integer orderNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "OrderHistory",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
}
)
private List<OrderHistory> orderHistory;
----Other properties, Getters and Setters
}
#Entity
#PrimaryKeyJoinColumn(name = "OrderNumber")
public class SpecialOrders extends Orders implements Serializable {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#Fetch(FetchMode.SELECT)
#OrderBy("sequenceNumber ASC")
private List<Items> items;
----Other properties, Getters and Setters
}
#Entity
#IdClass(ItemsPK.class)
public class Items implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "CustomOptions",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false),
#JoinColumn(name = "SequenceNumber", referencedColumnName = "SequenceNumber", nullable = false)
}
)
private List<CustomOptions> customOptions;
----Other properties, Getters and Setters
}
#Entity
public class ItemsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
----Getters and Setters
}
#Entity
#IdClass(CustomOptionsPK.class)
public class CustomOptions implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Other properties, Getters and Setters
}
public class CustomOptionsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Getters and Setters
}
With the above code, I see that hibernate does below
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
Attemps to INSERT into Items table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
Subsequently, If add a "Simple" primary key to Items table and make the orderNumber as a NULLable column, then the below happens:
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
INSERTs into Items table with orderNumber as NULL value and gets the generated id of the Items table row.
Updates the row of Items table with the orderNumber from parent, using the retrieved id for Items.
Attemps to INSERT into CustomOptions table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
As per the above sequence, it seems that:
Composite Primary key doesnt seem to be working correctly or not supported.
Hibernate is handling the OneToMany relationship inefficiently by issuing an INSERT followed by an UPDATE, instead of just an insert.
Any idea if my understanding is correct? The only way of fixing this issue seems to be that I need to remove the composite primary key and replace it with a simple one.
I'm trying to create a join table with #JoinTable from 3 different entities. Below is the code sample I'm using. While creating join table I'm getting below error. Please help to resolve.
There are 3 entities in my design. Credential, Category and Tenant.
Now I'm trying to make a join table that will contain the pk of these 3 tables using #ManyToMany annotation between them. Below is the relationship which I'm trying to make.
#Entity
#Table(name = "CREDENTIALS")
public class CredentialsEntity {
/** The credential id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int credentialId;
#ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "CATEGORY_HAS_CREDENTIALS",
joinColumns = {
#JoinColumn(table="CATEGORY", name = "CATEGORY_ID", referencedColumnName = "ID"),
#JoinColumn(table ="TENANT", name = "TENANT_ID", referencedColumnName = "ID")},
inverseJoinColumns = #JoinColumn(table="CREDENTIALS", name = "CREDENTIAL_ID", referencedColumnName = "ID"))
private List<TenantEntity> tenantEntities;
}
==========================
#Entity
#Table(name = "TENANT")
public class TenantEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int tenantId;
#ManyToMany(mappedBy = "tenantEntities", cascade = CascadeType.ALL)
private List<CredentialsEntity> credentialsEntities;
}
==========================
#Entity
#Table(name = "CATEGORY")
public class NodeCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int categoryId;
#ManyToMany(cascade = CascadeType.ALL)
private List<CredentialsEntity> credentialsEntities;
}
Caused by:
org.hibernate.AnnotationException: Cannot find the expected secondary
table: no TENANT available for
com.aricent.aricloud.entity.CredentialsEntity at
org.hibernate.cfg.Ejb3Column.getJoin(Ejb3Column.java:363) at
org.hibernate.cfg.Ejb3Column.getTable(Ejb3Column.java:342) at
org.hibernate.cfg.Ejb3Column.checkPropertyConsistency(Ejb3Column.java:584)
at
org.hibernate.cfg.annotations.CollectionBinder.buildCollectionKey(CollectionBinder.java:1018)
at
org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1336)
EDIT
I'm able to do the jointable like below, as mentioned in link
is this correct approach or I'm doing something wrong?
#ManyToMany(cascade={CascadeType.ALL})
#JoinTable(name = "CATEGORY_HAS_CREDENTIALS",
joinColumns = {
#JoinColumn(name = "CREDENTIAL_ID", referencedColumnName = "ID")},
inverseJoinColumns = #JoinColumn(table = "CATEGORY", name = "CATEGORY_ID", referencedColumnName = "ID"))
#MapKeyJoinColumn(name = "TENANT_ID", referencedColumnName ="ID")
#ElementCollection
private Map<TenantEntity, NodeCategory> tenantCategoryMap = new HashMap<TenantEntity, NodeCategory>();