Map two entities using a shared foreign key column in hibernate - java

I have four entities to map together, "Association", "Account", "Transaction" and "TransactionEvent". The id of Association is a simple integer id. Account and Transaction each have embedded id's consisting of a mapping to an Association and a number.
TransactionEvent should have an embedded id consisting of one Account and one Association. Now, each of those are mapped to an Association, and I want it to be the same Association for one TransactionEvent.
JPA Annotations is used for the Hibernate mapping, but I cannot make this work. I have tried forcing the same column name for the Association key, but Hibernate complains about repeated columns.
Is this possible to solve, or am I not thinking straight?
Here are the annotated classes, but I trimmed away getters/setters and non-id columns, annotations from the javax.persistence namespace:
#Entity
public class Association implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private long id;
}
#Embeddable
public class AccountPK implements Serializable {
#ManyToOne(optional=false)
private Association association;
#Column(nullable=false)
private int number;
}
#Embeddable
public class TransactionPK implements Serializable {
#ManyToOne
private Association association;
#GeneratedValue(strategy=GenerationType.AUTO)
private long number;
}
#Embeddable
public class AccountEventPK implements Serializable {
#ManyToOne(optional=false)
#JoinColumns({
#JoinColumn(name="association_id", referencedColumnName="association_id"),
#JoinColumn(name="account_number", referencedColumnName="number")
})
private Account account;
#ManyToOne(optional=false)
#JoinColumns({
#JoinColumn(name="association_id", referencedColumnName="association_id"),
#JoinColumn(name="transaction_number", referencedColumnName="number")
})
private Transaction transaction;
}
Actual Account, Transaction and AccountEvent entities are on the form
#Entity
public class Account implements Serializable {
#EmbeddedId
private AccountPK id;
}

I don't have much experience with placing associations directly in the embedded id component since this is not supported by JPA but is Hibernate specific.
As an alternative my suggestion would be to use the approach described in the Composite Primary Keys section of the JPA wikibook:
(...) JPA 1.0 requires that all #Id
mappings be Basic mappings, so if
your Id comes from a foreign key
column through a OneToOne or
ManyToOne mapping, you must also
define a Basic #Id mapping for the
foreign key column. The reason for
this is in part that the Id must be a
simple object for identity and caching
purposes, and for use in the IdClass
or the EntityManager find() API.
Because you now have two mappings for
the same foreign key column you must
define which one will be written to
the database (it must be the Basic
one), so the OneToOne or ManyToOne
foreign key must be defined to be
read-only. This is done through
setting the JoinColumn attributes
insertable and updatable to false,
or by using the
#PrimaryKeyJoinColumn instead of the
#JoinColumn.
A side effect of having two mappings
for the same column is that you now
have to keep the two in synch. This is
typically done through having the set
method for the OneToOne attribute
also set the Basic attribute value to
the target object's id. This can
become very complicated if the target
object's primary key is a
GeneratedValue, in this case you
must ensure that the target object's
id has been assigned before relating
the two objects.
(...)
Example ManyToOne id annotation
...
#Entity
#IdClass(PhonePK.class)
public class Phone {
#Id
#Column(name="OWNER_ID")
private long ownerId;
#Id
private String type;
#ManyToOne
#PrimaryKeyJoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
private Employee owner;
...
public void setOwner(Employee owner) {
this.owner = owner;
this.ownerId = owner.getId();
}
...
}
This looks like to be what you're looking for (and maybe less complicated). I'd try to implement this solution (incrementally).

Related

Hibernate-OneToMany mapping for existing DB Tables

I am trying to join to Hibernate Entities in a OneToOne Mapping. I am able to fetch the data for a given primary key from the Main Entity, the joining entity, however, returns null. I am new to hibernate and any help will be appreciated.
I have two Tables,
PT_CORE
Primary Key: ptId - Integer;
Foreign Key: stId(ST_AUX) - Integer;
Columns: ptId, ptName
ST_AUX
Primary Key: stId;
Columns: stId, stName
The two tables get populated by other applications and mine is a read-only operation.
Below is my first Entity class(PtCore.java)
#Entity
#Table(name="PT_CORE")
public class PtCore implements Serializable{
#Id
#Column(name="ptId", nullable = false)
private int id;
#Column(nullable=false)
private int stId; //The Foreign key column
#OneToOne
#JoinTable( name = "core_aux", joinColumns = {#JoinColumn(Name="ptId")},
inverseJoinColumns = {#JoinColumn(Name="stId")}
)
private StAux staux;
//Getters, setters and toString() for above
}
StAux is another Entity, defined as below,
#Entity
#Table(name="ST_AUX")
public class StAux implements Serializable {
#Id
#Column(nullable=false)
private Integer stId;
#OneToOne
private PtCore ptcore;
#Column
private String stName;
//Getters, Setters and toString follow.
}
I do below in the Service method:
PtCore obj = (PtCore) session.get(PtCore.class,1);
System.out.println(obj);
In the Results, I get the value of ptName, but the stAux class variables are null, Indicating that the join does not work as expected.
First of all you have the mapping information existing in your PT_CORE. And I assume it is something like FOREIGN KEY (stid) REFERENCES (stid). If you want to use existing schema and existing data I guess there is no mapping table core_aux really existing. At least you did not mention it. However it is visible as #JoinTable annotation but still there is this above mentioned foreign key which seems to be the real mapping (so again not the join table).
I suggest the following
remove this
#Column(nullable=false)
private int stId; //The Foreign key column
from your PtCore. I think it is not needed. Also in PtCore, remove the #JoinTable (because what I told above) and add mapping informaiion to #OneToOne annotation, like:
#OneToOne
#JoinColumn(name = "stid")
private StAux staux;
from your PT_CORE.
Then in StAux alter also a bit:
#Id
#Column(name = "stid") // this might not be needed but if there is like "st_id"...
private Integer stId; // so just for sure
#OneToOne(mappedBy = "staux")
private PtCore ptcore;
Because you have existing tables and constraints there might raise errors if hibernate tries to auto-generate those again by JPA instructions.
Check this for example for more information.
UPDATE: just realized also that in your title is #OneToMany but in your code is #OneToOne.
So you might want to elaborate your question and/or title a bit.
In your relation, the owning side is PtCore, the inverse side is StAux.
In bidirectional OneToOne relations, the inverse side has to have the mappedBy attribute. Actually, the mappedBy attribute contains the name of the association-field on the owning side.
So, you must change your inverse side code (StAux Entity). You have to add mappedBy attribute to #OneToOne in StAux class:
#OneToOne(mappedBy="staux")
private PtCore ptcore;

Spring and JPA 2.0 - Single value Primary Key through OneToOne

I have a simple table (ActivityLog) and I want it to have a PK that is also a FK to another table (User).
It seems to be a common thing to have, and I tried to follow this wikibook
Primary Keys through OneToOne and ManyToOne Relationships. The example there involved a composite key. I need just a primitive key, so I ended up with:
#Entity
public class User {
#Id
private Long id;
// other stuff
}
#Entity
public class ActivityLog {
#Id
#OneToOne(optional = false)
#JoinColumn(name="user_id", referencedColumnName="id")
private User user;
// other stuff
}
Unfortunately i am getting:
Caused by: java.lang.IllegalArgumentException: This class [class com.example.ActivityLog] does not define an IdClass
at org.hibernate.metamodel.internal.AbstractIdentifiableType.getIdClassAttributes(AbstractIdentifiableType.java:183)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:253)
I tried to annotate ActivityLog with:
#IdClass(Long.class)
(even though from what I understand it is applicable only for composite keys), yet I am getting the exact same error.
Is my case different than what's on the mentioned wikibook?
Is Spring at fault here? (As suggested in this question? (no accepted answers)).
This should help:
#Entity
public class ActivityLog {
#Id
#Column(name = "user_id")
private Long id;
#OneToOne(optional = false)
#JoinColumn(name="user_id", referencedColumnName="id")
private User user;
// other stuff
}
Btw. I would expect, that you need more logs per user, so you would probably need some additional (generated) id anyway ...

Hibernate: Can we have a relationship between Entities in which both are owning-side?

I am learning Hibernate and came across the terms Owning-side and Non-owing side.
Upon reading, I came to know that Owning-side means the Entity who has FK reference to the other Entity.
Consider the below scenario:
User entity:
#Entity
public class User {
#Id
private int userId;
private String userName;
#OneToOne
#JoinColumn("VEHICLE_ID")
private Vehicle vehicle;
// other code
}
Vehicle Entity:
#Entity
public class Vehicle{
#Id
private int vehicleId;
private String vehicleName;
#OneToOne
#JoinColumn("USER_ID")
private User user;
// other code
}
If I keep the #JoinColumn in both these Entities, then would both these entities User and Vehicle become owning-side entities?
I am not able to understand as to why just placing #JoinColumn annotation makes an entity as the owning-side Entity.
Can anyone help me understand?
If I keep the #JoinColumn in both these Entities, then would both
these entities User and Vehicle become owning-side entities?
We don't start with using the #JoinColumn in an entity and then state that particular entity is an owning entity. In fact the reverse is true.
We first decide which should entity should be owning entity and which one should be non-owning.
And we decide that by looking at which entity has the Foreign Key reference. And this again is more of a design choice. The one having the foreign-key reference is said to be the owning entity.
We then use the #JoinColumn annottation in the owning entity to specify the foreign key.
And in the non-owning side, we use mappedBy attribute inside of the particular association type.
In your example you are specifying a bi-directional relationship. So if we consider Vehicle entity has the foreignKey as userId, then Vehicle entity would be our owning-entity and we annotate this 'userId' with '#JoinColumn'. And in the non-owning entity i.e. User entity, we would use the 'mappedBy' attribute to refer to the field in owning entity on which #JoinColumn has been used :
#Entity
public class User {
#Id
private int userId;
private String userName;
#OneToOne(mappedBy="user")
private Vehicle vehicle;
// other code
}
#Entity
public class Vehicle{
#Id
private int vehicleId;
private String vehicleName;
#OneToOne
#JoinColumn("USER_ID")
private User user;
// other code
}
Here is a detailed documentation that might help you as a reference :
The JPA API reference docs also provide a nice brief description for the different association types : OneToOne , OneTomany , ManyToMany

ManyToMany reflexive relationship with an extra coloumn in JPA

First of all, sorry for my english. This is my first post.
Im developing an app for a friend that uses JPA (EclipseLink) and i can´t figure out how to make a ManyToMany reflexive relationship with an extra column describing the relationship.
I tried the solution of this post (without using a bidirectional relation):
#Entity
#Table(name="relationships")
public class Relationship implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne()
#JoinColumn(name = "associated_id_one")
private Person associatedPersonOne;
#ManyToOne()
#JoinColumn(name = "associated_id_two")
private Person associatedPersonTwo;
#Column(name="description")
private String description;
//Getters, Setters and constructor
...
}
But JPA gives me this error: The entity has no primary key attribute defined.
I know i have to specify a primary key but i don´t now how to make a composite primary key with those two foreign key.

How to properly implement a domain object with composite id in Hibernate?

I have the following domain objects:
public class Department {
private long departmentId;
}
public class Manager {
private long managerId;
}
public class Project {
private ProjectId compositeId;
#ManyToOne
private Department department;
#ManyToOne
private Manager manager;
}
public class ProjectId {
private long departmentId;
private long managerId;
}
Project is identified by a composite key (departmentId,managerId). The question is how should Project.setManager(..) or Project.setDepartment(..) be implemented? Is the implemention listed below the best practice?
public void setManager( Manager manager ) {
this.manager = manager;
this.compositeId.setManagerId( manager.getId() );
}
My understanding is that compositeId needs to be updated whenever an property is set.
A harder and related question is how should Project.setCompositeId(..) be implemented? Project wouldn't be able to update property manager nor department based on a composite id (long). Overwriting the compositeId without updating the properties would leave Project at an incongruous state.
I suggest the following:
#Entity
#IdClass(ProjectId.class)
public class Project {
#Id #Column(name="DEPARTMENT_ID")
private long departmentId;
#Id #Column(name="MANAGER_ID")
private long managerId;
#ManyToOne
#PrimaryKeyJoinColumn(name="DEPARTMENT_ID", referencedColumnName="DPT_ID")
private Department department;
#ManyToOne
#PrimaryKeyJoinColumn(name="MANAGER_ID", referencedColumnName="MGR_ID")
private Manager manager;
...
}
This mapping is very well explained in the JPA Wikibook:
JPA 1.0 requires that all #Id mappings
be Basic mappings, so if your Id comes
from a foreign key column through a
OneToOne or ManyToOne mapping, you
must also define a Basic #Id mapping
for the foreign key column. The reason
for this is in part that the Id must
be a simple object for identity and
caching purposes, and for use in the
IdClass or the EntityManager find()
API.
Because you now have two mappings for
the same foreign key column you must
define which one will be written to
the database (it must be the Basic
one), so the OneToOne or ManyToOne
foreign key must be defined to be
read-only. This is done through
setting the JoinColumn attributes
insertable and updatable to false, or
by using the #PrimaryKeyJoinColumn
instead of the #JoinColumn.
A side effect of having two mappings
for the same column is that you now
have to keep the two in synch. This is
typically done through having the set
method for the OneToOne attribute also
set the Basic attribute value to the
target object's id. This can become
very complicated if the target
object's primary key is a
GeneratedValue, in this case you must
ensure that the target object's id has
been assigned before relating the two objects.
(...)
Example ManyToOne id annotation
...
#Entity
#IdClass(PhonePK.class)
public class Phone {
#Id
#Column(name="OWNER_ID")
private long ownerId;
#Id
private String type;
#ManyToOne
#PrimaryKeyJoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
private Employee owner;
...
public void setOwner(Employee owner) {
this.owner = owner;
this.ownerId = owner.getId();
}
...
}
Reference
JPA Wikibook
Primary Keys through OneToOne and ManyToOne Relationships

Categories