Hibernate: Two Foreign key as ids - java

I have defined a many-to-one relationship between my three entity classes catalogB, tableA and tableAB. tableAB has a primary key composite of tableA.tableAId and catalogB.catalogBId, and I used the following code:
#Entity
#Table(name = "tableAB", schema = Constantes.SCHEMA_SOLICITUD,uniqueConstraints = {#UniqueConstraint(columnNames = { "tableAId ", "catalogBId" }) })
public class TableAB implements Serializable {
private static final long serialVersionUID = 6360131240770014903L;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "tableAId", referencedColumnName = "tableAId")
private TableAId tableAId;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "catalogBId", referencedColumnName = "catalogBId")
private CatalogBId catalogBId;
And that gives me an error:
Caused by: java.lang.IllegalArgumentException: This class [schema.TableAB ] does not define an IdClass

Refer this question. The accepted answer as well as other answers shed light on what you are trying to achieve.

Related

Invalid Identifier While Joining Tables in JPA

I am trying to join 3 tables in JPA. When i am trying to associate CRL_IC_IMPORT_TRANS table i am getting error as Invalid Identifier as shown below
from
crl_ic investorco0_
left outer join
crl_ic_import_trans icimporttr1_
on investorco0_.icimport_trans_event_id=icimporttr1_.event_id
left outer join
crl_ic_order investorco2_
on investorco0_.current_order_id=investorco2_.id
"INVESTORCO0_"."ICIMPORT_TRANS_EVENT_ID": invalid identifier
Edit
from
crl_ic investorco0_
left outer join
crl_ic_order investorco1_
on investorco0_.current_order_id=investorco1_.id
Error:
Provided id of the wrong type for class ICImportTrans. Expected: class java.lang.Long, got class java.lang.String
Why its going to primary key in ICImportTrans class even after i mapped to a non PK ?
Below are my 3 tables and its keys. What is the mistake i am doing .
#Table(name = "CRL_IC")
public class ICLoanOrder {
#Id
#Column(name="EVENT_ID")
String id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EVENT_ID",referencedColumnName = "eventId" )
ICImportTrans iCImportTrans;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
ICOrder currentOrder;
}
#Table(name = "CRL_IC_ORDER")
public class ICOrder implements Serializable {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Type(type = "uuid-char")
UUID id;
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="EVENT_ID")
ICLoanOrder iCLoanOrder;
}
#Table(name = "CRL_IC_IMPORT_TRANS")
public class ICImportTrans implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE, generator="SEQ1")
#SequenceGenerator(name="SEQ1",sequenceName = "SEQ_IC_IMPORT",allocationSize = 1)
#Column(name="PROCESS_ID")
private Long processId;
private String eventId;
}
The default name of the column should be iCImportTrans_id.
If you want to change it to icimport_trans_event_id you need to specify it in the #JoinColumn
#Table(name = "CRL_IC")
public class ICLoanOrder {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "icimport_trans_event_id", referencedColumnName = "eventId")
ICImportTrans iCImportTrans;
...
}
Provided id of the wrong type for class ICImportTrans. Expected: class java.lang.Long, got class java.lang.String
What kind of mapping are you trying to achieve?
In ICLoanOrder you mapped the id column and the association column to the same name EVENT_ID. I guess you want to use ICImportTrans.eventId as id of ICLoanOrder.
This willwork if you don't care about having an id field:
#Entity(name="LoanOrder")
#Table(name = "CRL_IC")
public static class ICLoanOrder implements Serializable{
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EVENT_ID", referencedColumnName = "eventId")
ICImportTrans iCImportTrans;
}
Normally, you would use #MapsId, but it seems to ignore the referencedColumn attribute and gives the same error.

Mapping a composite foreign key to a composite primary key

I'm having problems mapping composite keys in jpa / hibernate. The parent entity and the child entity both have composite primary keys.
I have been able to use #mapsId when the parent entity has a simple key and the child has a composite key.
In the hibernate documentation they use #JoinCoumns in the mapping to demonstrate mapping two composite keys. But in their example its not clear where those column references are defined.
I have the following:
#Embeddable
public class PriceRequestLegKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
...
}
#Embeddable
public class AllocationKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
#Column(name = "allocation_index")
private int allocationIndex;
...
}
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private List<AllocationModel> allocations;
...
}
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
At runtime when saving it gives the follow exception:
org.springframework.orm.jpa.JpaSystemException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex; nested exception is org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex
Which I assume is spurious as there are getters and setters. I also get the same error if I use mappedBy="leg" on the priceRequestLegModel and #MapsId on the AllocationModel. Could anyone point out what I'm doing wrong here?
You should restore the mappedBy="leg" to the PriceRequestLegModel #OneToMany annotation:
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(mappedBy="leg", cascade = CascadeType.ALL)
private List<AllocationModel> allocations;
...
}
Then you should change AllocationKey to reference PriceRequestLegKey:
#Embeddable
public class AllocationKey implements Serializable {
PriceRequestLegKey legKey; // corresponds to PK type of PriceRequestLegModel
#Column(name = "allocation_index")
private int allocationIndex;
...
}
And then set the value of the Allocation.leg #MapsId annotation appropriately:
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId("legKey")
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
Some examples like this are in the JPA 2.2 spec section 2.4.1.

Share columns between composite key and #ManyToOne JoinColumns

I'm having a problem that I don't know if it's possible to solve just by using hibernate/jpa annotations. The problem is that I have a composite key that has the same column as one of my foreignkey composite id, and I would like to share this same column on the table. For example:
#Entity
class Id {
#Id
#Column(name = "idPessoa")
public Integer idShared;
}
#Embeddable
class APK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public String nKey;
}
#Entity
class A {
#EmbeddedId
public APK id;
}
#Embeddable
class BPK {
#ManyToOne
#JoinColumn(name = "idShared")
public Id idShared;
public Integer nCode;
}
#Entity
class B {
#EmbeddedId
public BPK id;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "idShared", nullable = false, insertable = false, updatable = false), #JoinColumn(name = "nKey", nullable = false) })
public A a;
}
The question is how I can share the column idShared between A and B and use it in the #ManyToOne for the foreign key?
I already tried to use #JoinColumn inside #JoinColumns with the name idShared but I get an error saying that I need to use insert = false and update = false, I already put insertable = false and updateable = false, but then I get another error saying that I can't mix things.
I found a possible solution saying to use:
#ManyToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula = #JoinFormula(value = "idShared", referencedColumnName = "idShared")),
#JoinColumnOrFormula(column = #JoinColumn(name = "nKey", nullable = false)) })
public A a;
But it gives me the error:
Unable to find column with logical name in table A
It appears that the "name" property of the column it has to find is blank someway.
Need some help please!
Please have a look at Official Java EE 6 Tutorial about composite primary key. You can use #EmbeddedId #Embeddable and/or #IdClass annotations.
For example
// File: APK.java ---------------------
#Embeddable
public class APK implements Serializable {
public Integer idShared;
public String nKey;
}
// File: A.java ---------------------
#Entity
public class A {
#EmbeddedId public APK id;
}

Cascading save Entity objects with Foreign Key as a part of composite Primary Key

I would like to persist object of my QuestionCompletion class with all child elements. One of these childs has a composite primary key. And as a part of this primary key I have also foreign key to another entity. As a result I am getting this error:
Exception caught during request processing: javax.ejb.EJBTransactionRolledbackException:
could not set a field value by reflection setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
javax.ejb.EJBTransactionRolledbackException: could not set a field value by reflection
setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
And the last "caused by" is of course NullPointerException:
Caused by: java.lang.NullPointerException
This is part of my code. The last line causes error.
QuestionCompletion questionCompletion = new QuestionCompletion();
List<QuestionCompletionAnswer> answers = new ArrayList<QuestionCompletionAnswer>();
for (;;) { // loop isn't important; it's loop for answers
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
extendedQuestion.setId(extendedQuestionId); //extendedQuestionId is known to me in that place
for (;;) { // loop isn't important; it's loop for question answers
//questionCompletion and extendedQuestion are popualted here
QuestionCompletionAnswer questionCompletionAnswer = new QuestionCompletionAnswer();
questionCompletionAnswer.setQuestionCompletion(questionCompletion);
questionCompletionAnswer.setExtendedQuestion(extendedQuestion);
answers.add(questionCompletionAnswer);
}
}
questionCompletion.setAnswers(answers);
questionCompletionService.saveOrMerge(questionCompletion);
This is my basic entity class I would like to persist with all its childs elements. I have realized that List<QuestionCompletionAnswer> causes problems. I have used cascade = CascadeType.ALL to allow to persist childs elements also.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "extended_question_id")
protected List<QuestionCompletionAnswer> answers;
}
This is my class - Primary Key for the QuestionCompletionAnswer class.
#Embeddable
public class QuestionCompletionAnswerPK implements Serializable {
#Column(name = "question_completion_id")
protected Long questionCompletionId;
#Column(name = "extended_question_id")
protected Long extendedQuestionId;
}
And this is class which uses my EmbeddedId. Attribues questionCompletionId and questionCompletionId are the foreign key for some another entities so I have placed below also whole objects of these entities with #MapsId annotation.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
Could you tell me if my annotations are correct? Maybe I have mixed up few approaches. Or I can't in that case persist my basic object with all of its child elements.
EDIT
Now my mapping looks like:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
Code of the QuestionCompletionAnswerPK class is the same.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false)
protected ExtendedQuestion extendedQuestion;
}
With that mapping I am still getting the same exception.
EDIT #2
However when I have changed QuestionCompletionAnswer class in this way:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
I am getting that exception:
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated
for:class com.example.model.domain.QuestionCompletionAnswer
Edit 1 and 2 are still not right. You need mapsid specified on the relationship or you must set the field in the embedded id with a value yourself. And when you use mapsid, you shouldn't have the join column marked insertable=false or jpa can't insert a value for you. The last problem I see is that the new question is not persisted so it doesn't get an id assigned that the answer can reference - you need to explicitly persist the new question in the for loop or mark the relationship in the answer to it to cascade persist.
Why are you mixing jpa and hibernate annotations ? jpa annotations alone should be enough.
Try with the following mapping (I removed the class QuestionCompletionAnswerPK) :
QuestionCompletion.java
#Entity
#Table(name = "question_completion")
public class QuestionCompletion {
#Id
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
QuestionCompletionAnswer.java
#Entity
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "question_completion_fk")
private QuestionCompletion questionCompletion;
#Id
#ManyToOne
#JoinColumn(name = "extended_question_fk")
private ExtendedQuestion extendedQuestion;
}
With this mapping, I run the following test case and it worked :
QuestionCompletion questionCompletion = new QuestionCompletion();
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
QuestionCompletionAnswer answer = new QuestionCompletionAnswer();
answer.setQuestionCompletion(questionCompletion);
answer.setExtendedQuestion(extendedQuestion);
questionCompletion.setAnswers(Collections.singletonList(answer));
...
session.save(extendedQuestion);
session.save(questionCompletion);

How do I join tables on non-primary key columns?

I have an issue with join tables on an object in an ORM class hierarchy where the join column is NOT the primary key of the base class due a lagacy database structure.
Here is an example of the table design:
CREATE TABLE "SCH"."FOO"
(
"OWNERID" NUMBER(10,0) NOT NULL ENABLE,
"FOOID" NUMBER(10,0) NOT NULL ENABLE,
CONSTRAINT "FOO_PK" PRIMARY KEY ("OWNERID", "FOOID")
CONSTRAINT "FOO_FK1" FOREIGN KEY ("OWNERID") REFERENCES "SCH"."OWNERS" ("OWNERID") ENABLE
)
CREATE TABLE "SCH"."BAR"
(
"BARID" NUMBER(10,0) NOT NULL ENABLE,
"FOOID" NUMBER(10,0)
CONSTRAINT "BAR_PK" PRIMARY KEY ("BARID")
)
And here are the mappings (unesessary infomation removed)
#Entity
#IdClass(FooId.class)
#Table(name = "FOO")
public class Foo implements java.io.Serializable
{
#Id
#Column(name = "OWNERID")
private BigInteger ownerId;
#Id
#SequenceGenerator(name = "FOO_GENERATOR", sequenceName = "SEQ_FOO")
#GeneratedValue(generator = "FOO_GENERATOR")
#Column(name = "FOOID")
private BigInteger id;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
#Fetch(value = FetchMode.SUBSELECT)
#Cascade(value = {CascadeType.ALL})
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
}
#Entity
#Table(name = "BAR")
public class Bar implements java.io.Serializable
{
#Id
#Column(name = "BARID")
private BigInteger id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
private Foo foo;
}
This fails with an exception:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(FOOID) of com.package.Bar.foo referencing com.package.Foo not mapped to a single property
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:204)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:114)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1580)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1503)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1419)
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1375)
Could you please help with a solution?
You must not map the bidirectional association twice. The One side must be marked as the inverse of the Many side, using the mappedBy attribute:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "foo")
#Fetch(value = FetchMode.SUBSELECT)
#Cascade(value = {CascadeType.ALL})
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID", referencedColumnName = "FOOID")
private Foo foo;
There is no reason to tell Hibernate twice that the association is mapped by the join column FOOID. And doing it is actually an error, because it defines two different unidirectional associations rather than one bidirectional association.
EDIT
The above should work, but doesn't due to the following Hibernate bug: It's a Hibernate bug. See HHH-4284.
To circumvent this problem, since the FOOID is enough to ensure uniqueness, a solution would be to remove the #Id annotation from the owner ID and the #IdClass annotation.
U can do as follows.... it should work -
#Entity
#IdClass(FooId.class)
#Table(name = "FOO")
public class Foo implements java.io.Serializable
{
#Id
#Column(name = "OWNERID")
private BigInteger ownerId;
#Id
#SequenceGenerator(name = "FOO_GENERATOR", sequenceName = "SEQ_FOO")
#GeneratedValue(generator = "FOO_GENERATOR")
#Column(name = "FOOID")
private BigInteger id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "FOOID",nullable=false)
#ForeignKey(name = "fk")
private Set<Bar> bar = new LinkedHashSet<Bar>(0);
}
#Entity
#Table(name = "BAR")
public class Bar implements java.io.Serializable
{
#Id
#Column(name = "BARID")
private BigInteger id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "FOOID", updatable = false, insertable = false, nullable=false)
private Foo foo;
}
You used composite primary key in FOO table. So you should try #EmbeddedId property and you should need two columns "OWNER_ID" and "FOO_ID" in BAR entity that join with FOO entity.

Categories

Resources