ManyToMany reflexive relationship with an extra coloumn in JPA - java

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.

Related

How to create table with unidirectional relation in Hibernate?

i have the empty database in mysql, and two java entites. One of those have unidirectional relation. When hibernate tryes to create tables, i got the error:
Error executing DDL "alter table entry add constraint FK6ov2k83sx3crs9v3q8nvjuf1j foreign key (category_name) references category (name)" via JDBC Statement
There are my entites:
#Entity
public class Entry {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
#Column
private String myfio;
private String descr;
#OneToOne(cascade = CascadeType.ALL)
private Category category;
}
And the second:
#Entity
#Table(name="category")
public class Category {
#Id
#Column
private String name;
}
How to create tables without errors?
OneToOne relationship shares the same id. So it should be the same type, but the first one is int (actually it should be Integer to allow null value for the transient (not stored) entities) and the second one is String. It seems you simply missed a line. Also, it worths to mention Vlad Mihalchea’s article https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

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;

Foreign key as primary key annotation

There is 2 entity
Product(product_id(PK),product_name..)
Store(product_id(PK,FK),product_quantity,...)
I need #OneToOne, so i do this
public class Product{
#Id
#Column(name="PRODUCT_ID")
#OneToOne(mappedBy="product")
private int product_id;
....
}
public class Store{
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "PRODUCT_ID")
private Product product;
}
Compilier says #Column(s) not allowed on a #OneToOne property: ......shopdb.entity.Product.product_id
What is the problem?
I just moved #OneToOne(mappedBy="product") to new created field in Product
private Store store;
I dont understand how it works correct;
Why i need to use 3 fields instead of 2?
You can't mark a column defined with #JoinColumn as #Id. This is applicable only to columns defined with #Column annotation.
It is also a bit strange what are you trying to do. You are basically making the produc_id primary key for both entites, so then why don't make just one?
From what I can see here, I believe that the Store should have a store id property which will be used as primary key.

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