Hibernate: Using part of composite FK in another entity PK - java

im relative new to Hibernate Mappings im trying to achieve this functionality between the class Post and Comentario without luck
Relational model
#Embeddable
public class PostPK implements Serializable {
#Column(name="idPost")
private int postID;
#Column(name="idUsuario")
private int userIDFK;
-------------------------------
#Entity
#Table(name="Post")
public class Post {
#EmbeddedId
private PostPK id;
#ManyToOne
#MapsId(value="userIDFK")
#JoinColumn(name="idUsuario")
private Usuario usuario;
#OneToMany(mappedBy="post")
private List<Comentario> comentarios;
#Column(name="titulo")
private String titulo;
-----------------------------------
#Embeddable
public class ComentarioPK implements Serializable{
#Column(name="idComentario")
private int comentarioId;
#Column(name="idPost")
private int postIdFK;
---------------------------
#Entity
#Table(name="Comentario")
public class Comentario {
#EmbeddedId
private ComentarioPK id;
#ManyToOne
#MapsId("postIdFK")
#JoinColumn(name="idPost",referencedColumnName="idPost")
private Post post;
#Column(name="texto")
private String texto;
without mapping comentario and its fields in Post its working fine but when i decide to map it i get this error
Unable to find column reference in the #MapsId mapping: idUsuario
is it not finding the idUsuario column in Comentario table? i dont want to add it , i can achieve joins in mysql but i dont know how to do it in Hibernate

#MapsId annotation is used to map the primary key fields of the parent entity with the child entity(with the same name).
In your case your are having composite primary key in your parent entity but in child entity you want to refer only one field of it.
PostPK has two fields : idPost and idUsuario. But in Comentario class when your are specifying ManyToOne relationship you are mentioning single column in #JoinColumn(which is idPost) and no field for idUsuario is available in your mapping. But as per the behavior of #MapsId annotation both the fields(idPost and idUsuario) are expected in Comentario class.
Thus, in your case #MapsId annotation won't work

Related

Relationship table mapped as entity in JPA

I'm trying to map one specific many to many table on my database as an entity in JPA (cause I have some specific attributes on my relationship table and I wanted to retrieve this as the class attributes two). But having issues while declaring the IDs.
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Entity
#Table(name = "user_plan")
public class UserPlan implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#OneToOne
private User user;
#Id
#OneToOne
private Plan plan;
private Integer billingDay;
#Enumerated(EnumType.STRING)
private BillingType billingType;
#Enumerated(EnumType.STRING)
private PlanStatus planStatus;
}
The application starts successfully but when I try to map some repository to manage this table, Hibernate throws an error:
java.lang.IllegalArgumentException: This class [class com.demo.domain.model.UserPlan] does not define an IdClass
How can I use the JPA entity annotation to manage this relationship table? Is it possible?
I cannot simply declare one property in the user class of Plan model and mark it as #ManyToMany, cause the plan table does not have the property that I need to execute some operations, which are declared on UserPlan class, also I cannot move these properties to Plan class, cause the Plan table is just a template of a plan, and the UserPlan have all the specific data (billingDay, billingType and planStatus).
JPA supports relationship tables as a Java class? Or it can be mapped only as a property?
Thanks
You are using multiple #Id annotations. To do so you need to create PrimaryKey class:
public class PrimaryKey implements Serializable {
private User user;
private Plan plan;
// Getter and Setter
}
And you need to add #IdClass(PrimaryKey.class) annotation to your entity class.
If you have a Repository don't forget to change id type to PrimaryKey:
public interface YourRepository
extends SomeRepositoryInterface<UserPlan, PrimaryKey> {
//...
}
Also check this question

EclipseLink composite PK with FK

I am using eclipselink 2.5.1.
Let's say I have these two class.
JAVA
#Entity
public class Car implements Serializable {
#EmbeddedId
protected CarPK carPK;
private String color;
#ManyToOne(fetch = FetchType.LAZY)
private Manufacturor manufacturor;
//constructors, getters & setters...
}
#Embeddable
public class CarPK implements Serializable {
#NotNull
private int idManufacturor;
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
//constructors, getters & setters...
}
Car has a composite primary key (idManufacturor and date) and idManufacturor is also a foreign key referencing the class Manufacturor.
I'm having issue with the mapping. EclipseLink understand the manufacturor object as a column in my Car table.
Error
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: invalid column nameĀ : 'manufacturor'.
I know the problem will be solved if I add a column manufacturor FK but it would be repeating.
Please feel free to ask for any precision if I'm not clear enough.
Thank you for your help.
Add the JoinColumn Annotation
#JoinColumn(name = "id_manufacturor", referencedColumnName = "id")
Name is the FK column name in your database (not entity).
The referencedColumnName "id" must correspond to the defined id in manufacturer table.

Joining two table in spring data JPA and Querying two table data from repository

I am using Sprind JPA, Spring 3.1.2(in future 3.2.3), Hibernate 4.1 final.
I am new to Sprind Data JPA. I have tow Table Release_date_type and Cache_media which entities are as follows :
ReleaseAirDate.java
#Entity
#Table(name = "Release_date_type")
public class ReleaseDateType {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer release_date_type_id;
#Column
private Integer sort_order;
#Column
private String description;
#Column
private String data_source_type;
#Column(nullable = true)
private Integer media_Id;
#Column
private String source_system; with getters and setters..
and CacheMedia as
#Entity
#Table(name = "Cache_Media")
public class CacheMedia {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer id;
#Column(name="code")
private String code;
#Column(name="POSITION")
private Integer position;
#Column(name="DESCRIPTION")
private String media_Description; with setter and getters.
Now my repository interface is as follows :
public interface ReleaseDateTypeRepository extends CrudRepository<ReleaseDateType, Long>{ }
Now i want to write a method(Query) in ReleaseDateTypeRepository interface which can get all the data from Release_Date_Type table including appropriate media_description from Table 'Cache_Media' based on media_id of Release_date_type table.
So my select (SQL)query looks like
SELECT * from Release_Date_Type r left join Cache_Media c on r.media_id=c.id
I don't know how to map entities.
I tried so many thing but not luck.
Any help is appreciated.
Its not the answer for joining via Hibernate, but alternatively you can create a view with your join and map the view to your objects

Map two entities using a shared foreign key column in hibernate

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).

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