Jpa - Two #OneToOne relations with same 'mappedBy' - java

I have two java-classes / db-tables: 'message' and 'thirdparty'
#Entity
public class Message {
#OneToOne(mappedBy = "message")
private ThirdParty source = null;
#OneToOne(mappedBy = "message")
private ThirdParty target = null;
....
}
#Entity
public class ThirdParty {
#OneToOne(targetEntity = Message.class)
#JoinColumn(name = "Message", referencedColumnName = "mess_id", nullable = false)
private Message message = null;
#Column(name = "isSource", nullable = false)
private Boolean isSource = null;
}
Message has two references to ThirdParty, which could be differenced by isSource (if they are source or target).
This cannot be resolved by jpa they way it is designed / annotiated. But is there a way to to this by adding some annotiation or some kind of special sql-statement?

This is conceptually wrong. You cannot do this. OneToOne mapping occurs when there are two entities mapped in the following way :
Entity1 : Has a primary key(PK1) and others along with a foreign key(FK)
Entity2 : Has a primary key (PK2).
Now the FK is mapped to PK2 in such a way that for each occurence of PK there must be a one and only one matching occurence of FK.

Related

Hibernate won't set foreign key to value of parent's primary key on insert

I have 3 tables that have a hierarchical relationship:
Page (Grandmother)
public class Page extends BaseDAO {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "page_id", unique = true, nullable = false)
public Integer getPageId() {
return this.pageId;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "page", cascade=CascadeType.ALL, orphanRemoval=true)
#NotFound(action = NotFoundAction.IGNORE)
public Set<PageWell> getPageWells() {
return this.pageWells;
}
}
PageWell (Mother)
public class PageWell extends BaseDAO {
#Id
#Column(name = "page_well_id", unique = true, nullable = false)
public int getPageWellId() {
return this.pageWellId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "page_id", nullable = false)
public Page getPage() {
return this.page;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pageWell", cascade=CascadeType.ALL)
public Set<PageComponentAttribute> getPageComponentAttributes() {
return this.pageComponentAttributes;
}
}
PageComponentAttribute (Daughter)
public class PageComponentAttribute extends BaseDAO {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "page_component_attribute_id", unique = true, nullable = false)
public Integer getPageComponentAttributeId() {
return this.pageComponentAttributeId;
}
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name = "page_well_id", nullable = false)
public PageWell getPageWell() {
return this.pageWell;
}
}
The primary keys for all three tables are AutoIncrement in MySQL. The expected behavior is that when I save the Page, all PageWell objects get saved, and all PageComponentAttribute objects also get saved.
For some reason, it is working correctly for the Grandmonther -> Daughter relationship. But in the case of the Mother -> Daughter relationship, the Daughter's foreign key is set to 0 every time. This was obviously causing a constraint violation. I have temporarily removed the FK constraint on that relationship, and the record makes it into the table, but the FK is still 0.
My save code looks like this:
Page page = getPage(request); //getPage() finds an instance of page, or creates and persists a new instance if none exists.
Set<PageWell> wells = page.getPageWells();
wells.clear(); //delete all related PageWell objects so we can re-create them from scratch
page = pageHome.merge(page);
wells = page.getPageWells();
PageWell pageWell;
// Now create a new PageWell and set up bi-directonal mapping with Page. This part works great.
pageWell = new PageWell();
pageWell.setPage(page);
wells.add(pageWell);
// Now do the exact same thing with the PageComponentAttribute objects
PageComponentAttribute pca = new PageComponentAttribute();
pca.setPageWell(pageWell);
pca.getPageWell().getPageComponentAttributes().add(pca);
// Now save the Page
page = pageHome.merge(page);
When I check the database, the FK in the PageComponentAttribute table is set to 0. Again, I have temporarily removed the FK constraint from MySQL just to allow the record to save without an exception, but besides that, what am I doing wrong?
I would try to do one of the things, or all:
1) Remove the cascade from the #ManyToOne. In general thats not a good idea to have it configured like that. It essentially makes sense only for #OneToMany and #OneToOne.
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "page_well_id", nullable = false)
public PageWell getPageWell() {
return this.pageWell;
}
2) Try using the Hibernate cascade configuration instead of the JPA one:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pageWell")
#Cascade(CascadeType.ALL)
public Set<PageComponentAttribute> getPageComponentAttributes() {
return this.pageComponentAttributes;
}
There may be some small differences, see: article
3) Not sure why you invoke merge twice on the page entity. I would just stick to one at the very end.
4) Last workaround that comes to my mind would be performing an explicit flush here:
pageWell = new PageWell();
pageWell.setPage(page);
wells.add(pageWell);
session.flush();
and then:
PageComponentAttribute pca = new PageComponentAttribute();
pca.setPageWell(pageWell);
pca.getPageWell().getPageComponentAttributes().add(pca);
session.merge(pageWell);
In theory, pageWell should have the primary already generated because of the flush and it should not be 0 anymore.
I wish i had a testing environment right now to test this properly.
In the unlikely chance that someone has made the same bone-headed mistake I've made, the problem was that the PageWell entity's primary key didn't have a Generation strategy. I added that and it fixed my problem.
#GeneratedValue(strategy = IDENTITY)

Hibernate Unidirectional OneToMany Not Working with composite id

I've been bashing my head on my keyboard for two days trying to figure this out...
Some background: We have a data model set up with a Perl code base that runs straight native SQL statements to the database via ODBC. For certain reasons, we decided to rewrite the code in Java... I thought it would be a good idea to use Hibernate to define all of the mappings. We don't want to edit the data model.
For simplicity sake, I can express the problem with only part of our data model. We have the entities "Job","JobDatabase" and "JobTable".
Job has a PK of job_name. Database has a PK of job_name,name. Table has a PK of job_name,src_database_name,name. As you may expect, Job has a OneToMany relationship with JobDatabase, and Database has a OneToMany with JobTable.
For purposes of this test, I'm starting with empty tables and trying to create some sample data. I can insert a Job and a JobDatabase, but when I try to insert the JobTable, Hibernate throws an error. Or more accurately, that is where it complains. It doesn't start executing my code because it detects the mapping error. However, if I remove the association between JobDatabase and JobTable, it will insert all Job and JobDatabase records correctly with no errors.
Sample Classes (all fields have getters/setters... there are also many other fields):
#Entity
#Table(name="Job")
public class Job implements Serializable {
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "job_name", referencedColumnName = "job_name")
private Set<JobDatabase> databases;
}
#Entity
#Table(name="JobDatabase")
public class JobDatabase implements Serializable {
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#Id
#Column(name="name",nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "job_name", referencedColumnName = "job_name"),
#JoinColumn(name = "name", referencedColumnName = "src_database_name")
})
private Set<JobTable> tables;
}
#Entity
#Table(name="JobTable")
public class JobTable implements Serializable{
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#Id
#Column(name="src_database_name",nullable = false)
private String srcDatabaseName;
#Id
#Column(name="name",nullable = false)
private String name;
}
The error:
Exception in thread "main" org.hibernate.MappingException: Unable to find column with logical name: src_database_name in JobDatabase
I keep getting this error. I do not understand why it is looking for the referenced column in the entity "owning" the mapping. src_database_name does indeed only exist in JobTable - it is referred to as "name" in JobDatabase. JobTable also has a "name" field, but it refers to the name of the Table.
You need to have src_database_name column in your JobDatabase table. Or you can change src_database_name to other column name.
For composite key reference column must be present in your source table.

Hibernate one to many relationship annotation

I have problem with settings relationship one to many. I want to create relationship between FindingAid and FindingAidVersion. I tried example in Hibernate documentation and many examples in the Internet but I don't know what is wrong.
FindingAid.java
Public class FindingAid implements Serializable {
private String id;
....
#OneToMany
#JoinColumn(name="id")
private Set<FindingAidVersion> listVersion = new HashSet<FindingAidVersion();
...... generate getter and setter .....
}
FindingAidVersion.java
Public class FindingAidVersion implements Serializable {
private String id;
private Date closeDate;
private FindingAid findingAid;
.......
#ManyToOne
#JoinColumn(name = "id", nullable = false)
public FindingAid getFindingAid() {
return findingAid;
}
...... generate getter and setter .....
}
Application code is:
FindingAid data = new FindingAid();
data.setCreateDate(new Date());
data.setName("daniel");
FindingAidVersion verse = new FindingAidVersion();
verse.setCloseDate(new Date());
verse.setIsClose(false);
data.getListVersion().add(verse);
this.getSession().save(data);
this.getTx().commit();
Error is:
Repeated column in mapping for entity: cz.tacr.elza.api.model.FindingAidVersion column: id (should be mapped with insert="false" update="false")
I know that problem is in annotation #JoinColumn but I am lost.
Thanks for your suggestions.
Have you tried to do what the error message suggest ?
#JoinColumn(name = "id", nullable = false, insert=false, update=false)
And in FindindAid for the listVersion attribute try
#OneToMany(mappedBy="findingAid")
Look at your new error:
foreign key constraint "fk_3o68boae9f3oamcm6dfy77tfw" cannot be implemented Detail: Key columns "findingaid" and "id" are of incompatible types: bytea and character varying
On one table (finding_aid_version ) you have "findingAid bytea" and on the other (finding_aid) "id varchar(255)",

JPA Many To Many Select

I got two entities FoaParamEmploye and FoaParamPosition (tables FOA_PARAM_EMPLOYE and FOA_PARAM_POSITION) with ManyToMany annotation (I didn't put here all attributes) :
#Entity
#Table(name = "FOA_PARAM_EMPLOYE")
#NamedQuery(name = "FoaParamEmploye.findAll", query = "SELECT f FROM FoaParamEmploye f")
public class FoaParamEmploye implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FoaParamEmployePK id;
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") }
)
private List<FoaParamPosition> foaParamPositions;
}
And the second one :
#Entity
#Table(name="FOA_PARAM_POSITION")
#NamedQuery(name="FoaParamPosition.findAll", query="SELECT f FROM FoaParamPosition f")
public class FoaParamPosition implements Serializable {
#EmbeddedId
private FoaParamPositionPK id;
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_POSITION"),
#JoinColumn(name = "COD_ENTREP") }
)
private List<FoaParamEmploye> foaParamEmployes;
}
Association table is FOA_PARAM_EMPLOYE_POSITION with COD_ENTREP, ID_POSITION and ID_EMPLOYE fields.
PK are :
#Embeddable
public class FoaParamEmployePK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="COD_ENTREP")
private String codEntrep;
#Column(name="ID_EMPLOYE")
private long idEmploye;
}
#Embeddable
public class FoaParamPositionPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="COD_ENTREP")
private String codEntrep;
#Column(name="ID_POSITION")
private long idPosition;
}
I try to get all FoaParamPosition for a FoaParamEmploye. I wrote this JPQL query :
Query q = entityMgr.createQuery(
"SELECT position FROM FoaParamPosition position
INNER JOIN position.foaParamEmployes employes
where
employes.id.idEmploye = :idEmploye AND
employes.id.codEntrep =:codEntrep")
.setParameter("idEmploye", pIdEmploye)
.setParameter("codEntrep", "ENT");
I got an exception :
ORA-00904: "FOAPARAMEM1_"."FOAPARAMEMPLOYES_ID_EMPLOYE" : non valid identifier
As you can see, the generate SQL has this attribute but I can't understand why :
select
foaparampo0_.COD_ENTREP as COD1_2337_,
foaparampo0_.ID_POSITION as ID2_2337_,
foaparampo0_.ACTEUR_MAJ_OCCUR as ACTEUR3_2337_,
foaparampo0_.CD_PROFIL_AFFECTATION as CD4_2337_,
foaparampo0_.CD_TYPE_PROFIL_POSITION as CD5_2337_,
foaparampo0_.DATE_HEURE_MAJ_OCCUR as DATE6_2337_,
foaparampo0_.DT_FIN_ACTIVITE_POSITION as DT7_2337_,
foaparampo0_.HIERARCHIE_POSITION as HIERARCHIE8_2337_,
foaparampo0_.ID_DIVISION_AGENCE as ID9_2337_,
foaparampo0_.ID_EMPLOYE_PRINCIPAL as ID10_2337_,
foaparampo0_.ID_REF_EXT_POSITION_PARENTE as ID11_2337_,
foaparampo0_.ID_REF_EXTERNE_POSITION as ID12_2337_,
foaparampo0_.NIVEAU_AGENCE as NIVEAU13_2337_,
foaparampo0_.REF_EXT_POSITION as REF14_2337_,
foaparampo0_.xqcif as xqcif2337_
from
FOA_PARAM_POSITION foaparampo0_,
FOA_PARAM_EMPLOYE_POSITION foaparamem1_,
FOA_PARAM_EMPLOYE foaparamem2_
where
foaparampo0_.COD_ENTREP=foaparamem1_.ID_POSITION
and foaparampo0_.ID_POSITION=foaparamem1_.COD_ENTREP
and foaparamem1_.foaParamEmployes_COD_ENTREP=foaparamem2_.COD_ENTREP
and foaparamem1_.foaParamEmployes_ID_EMPLOYE=foaparamem2_.ID_EMPLOYE
and foaparamem2_.ID_EMPLOYE=?
and foaparamem2_.COD_ENTREP=?
I would recommend you to map your classes with some tool for example Netbeans -> generate JPA entities.
One of your side must be owning side (JoinColumn) and one must be inverse side (mappedBy).
If you don't wanna use any mapping tool, see this:
http://alextretyakov.blogspot.cz/2013/07/jpa-many-to-many-mappings.html
I'm not sure but I think if first you get an FoaParamEmploye object through below JPA query and after that use getFoaParamPosition method of this object to retrieve a list of positions your problem will get solved (when using this method in many-to-many association, the JPA will automatically retrieve the list):
Select distinct f from FoaParamEmployes f
where f.employes.idEmploye = :idEmploye
I don't see a problem in your mapping. The error you're getting complains explicitly about the invalid column name. In Oracle, the quoted names such as "FOAPARAMPO0_"."FOAPARAMPOSITIONS_ID_POSITION" are case sensitive, while the generated columns are foaParamPositions_COD_ENTREP and foaParamPositions_ID_POSITION and the case mismatch is causing your error.
A quick check would be to uppercase your foaParamEmployes and foaParamPositions and if I'm right the mentioned issue should be resolved. If so you can work-out a more elegant way to control the generated names with mappings.
I've noticed your other question in the comments referring to trouble with the inverseJoinColumns. That's why I'm mentioning upper-casing your properties. But in fact, you can handle it with mappings like
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMPOSITION_COD_ENTREP"), #JoinColumn(name = "FOAPARAMPOSITION_ID_POSITION")}
)
private List<FoaParamPosition> foaParamPosition;
and
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_POSITION"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMEMPLOYE_COD_ENTREP"), #JoinColumn(name = "FOAPARAMEMPLOYE_ID_EMPLOYE")}
)
private List<FoaParamEmploye> foaParamEmploye;
which solves the issue in your other question I believe

org.hibernate.QueryException: could not resolve property: composite key in query

I am trying to fetch the list of records from a view which has a composite primary key with three columns.
I tried to embed the composite key in the entity class. But I am getting the below mentioned errors. The columns of the views (VW_ALERTS) are C_ID, MAT_ID, P_MONTH, CO_TYPE, CO_SUBTYPE.
Here the composite keys are C_ID, MAT_ID, P_MONTH. I am making the property of them in the embeddable class.
Please help to resolve the issue
org.hibernate.QueryException: could not resolve property: coreId of: com.sp.cpem.dto.VwAlerts [FROM com.ct.cpem.dto.VwAlerts d ORDER BY d.cId ASC]
This following code is used to execute the hql.
Session session = sessionFactory.openSession();
String hql = "FROM VwAlerts d ORDER BY d.coId ASC";
Query query = session.createQuery(hql);
return query.list();
The entity class :
#SuppressWarnings("unchecked")
#Entity
#Table(schema = "TIGER", name = "VW_ALERTS")
public class VwAlerts {
#Embedded
private VwAlertsPK vwAlertsPK;
#Basic
#Column(name = "CO_TYPE", nullable = true)
private String coType;
#Basic
#Column(name = "CO_SUBTYPE", nullable = true)
private String coSubType;
Class used to get the composite key
#Embeddable
public class VwAlertsPK implements Serializable {
#Basic
#Column(name = "C_ID", nullable = false)
private BigDecimal cId;
#Basic
#Column(name = "MAT_ID", nullable = true)
private BigDecimal matId;
#Basic
#Column(name = "P_MONTH", nullable = true)
private BigDecimal pMonth;
I am expecting to get all the records from the view.
I tried with the #Id column in the entity class, it failed by returning only the duplicate records of the first row from the view.
Your entity VwAlerts has only 3 properties --> vwAlertsPK, coType, coSubType
but in your HQL you are trying to access a property coreId which does not exist in your entity.
FROM com.ct.cpem.dto.VwAlerts d ORDER BY d.coreId ASC
So add the property coreId to your entity or else just update the ORDER BY clause so you are pointing to correct properties of your entity.

Categories