I have been working for hours trying to solve this problem, which seems quite simple from a database point of view but hard to figure out with hibernate.
I have got two tables: VehicleCategory, and Vehicle.
A Vehicle has one VehicleCategory, so a Vehicule references a VehiculeCategory using a foreign key.
VehicleCategory:
String attr1
String attr2
Integer attr3
String attr4
PRIMARY KEY (attr1, attr2, attr3, attr4)
Vehicle
String name
String attr1
String attr2
Integer attr3
String attr4
FOREIGN KEY (attr1, attr2, attr3, attr4) REFERENCES VehicleCategory
PRIMARY KEY (name, attr1, attr2, attr3)
Here is my point: I do not want to use attr4 for my primary key, but I want to use the other foreign attributes in my primary key.
This is the mapping I did:
<class name="Vehicle" table="vehicle">
<composite-id>
<key-property name="name" column="name"/>
<key-many-to-one name="vehiclecategory" class="VehicleCategory">
<column name="attr1" not-null="true" />
<column name="attr2" not-null="true" />
<column name="attr3" not-null="true" />
</key-many-to-one>
</composite-id>
<many-to-one name="vehiclecategory" class="VehicleCategory" fetch="select" insert="false" update="false" >
<column name="attr1" not-null="true" />
<column name="attr2" not-null="true" />
<column name="attr3" not-null="true" />
<column name="attr4" not-null="true" />
</many-to-one>
</class>
I get the following exception:
Foreign key (FK_s94md9vv63hrpwhyaw4rfxec5:vehicle [attr1,attr2,attr3])) must have same number of columns as the referenced primary key (vehicle_category [attr4,attr1,attr2,attr3])
I understand what this exception means. But I do not see why I get it because, from my point of view, I did reference the 4-columns primary key in my foreign key, and only used 3 in my Vehicule primary key.
I am using Hibernate 4.3.6
Thank you for your help !
Related
I have 2 tables:
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
address INT NOT NULL,
PRIMARY KEY (id)
);
create table ADDRESS (
id INT NOT NULL auto_increment,
street_name VARCHAR(40) default NULL,
city_name VARCHAR(40) default NULL,
state_name VARCHAR(40) default NULL,
zipcode VARCHAR(10) default NULL,
PRIMARY KEY (id)
);
where EMPLOYEE.address is a foreign key mapped to ADDRESS
My mapping file is defined as follows:
<hibernate-mapping>
<class name="Employee" table="EMPLOYEE">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<many-to-one name="address" column="address" unique="true"
class="Address" not-null="true"/>
</class>
<class name="Address" table="ADDRESS">
<meta attribute="class-description">
This class contains the address detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="street" column="street_name" type="string"/>
<property name="city" column="city_name" type="string"/>
<property name="state" column="state_name" type="string"/>
<property name="zipcode" column="zipcode" type="string"/>
</class>
</hibernate-mapping>
But, when I pull Employee objects all the normal fields are populated normally except the Address field in the Employee plain old object has all the values set to null. What could be causing this?
Unfortunately a bit long for a comment, but hopefully this will give you something to look at:
It is my understanding that NHibernate is actually "lazy by default", this means that the Address object won't be populated until requested, and then (if I remember correctly) only if you are still connected. I've had issues in the past of passing partially loaded objects to a view in my MVC projects and yours sounds similar.
That being said, I've read on several articles that you NHibernate doesn't lazy-load for many-to-one relationships, however I still think this is probably worth looking into. The many-to-one relationship requires the use of a foreign key, and therefore the referencing column must be the primary key. I.e. EMPLOYEE.address = ADDRESS.id.
Once you solve your problem please post what was actually wrong as I am looking to improve my knowledge in NHibernate.
You might find the following articles useful:
Solving Performance Problems with nHibernate (or any ORM)
And these Questions:
NHibernate: many-to-one and lazy
NHibernate 1.x : many-to-one not lazily loaded
Problem Summary
I am trying to map a many-to-one with a composite key. So far I haven't been able to locate another question that helps. I know how to map to a composite key with one-to-one, but it is not allowing me to map many-to-one.
Below you will see bhrvJournalDAO has a store_nbr column, that maps to transAcctgBuTxtDAO acctg_bu_id. How can I hit the composite key and tell it to only use the acctgBuId? I do not have a country code (which is the second part of the composite key).
From Here
<hibernate-mapping>
<class name="com.acctg.BhrvJournalDAO" table="transpo_acctg:bhrv_journal">
<id name="jeId" column="je_id">
<generator class="native"/>
</id>
<property name="bhrvInvoiceId" column="bhrv_invoice_id"/>
<property name="storeNbr" column="store_nbr"/>
<property name="loadId" column="load_id"/>
<many-to-one name="transAcctgBuTxt" class="TransAcctgBuTxtDAO" insert="false" update="false" cascade="all">
<column name="store_nbr"></column>
</many-to-one>
</class>
To Here
<hibernate-mapping>
<class name="TransAcctgBuTxtDAO" table="transpo_acctg:trans_acctg_bu_txt">
<composite-id name="transAcctgBuTxtPKDAO" class="TransAcctgBuTxtPKDAO">
<key-property name="acctgBuId" column="acctg_bu_id"/>
<key-property name="languageCode" column="language_code"/>
</composite-id>
<property name="acctgBuAbbr" type="java.lang.String">
<column name="acctg_bu_abbr" />
</property>
<property name="acctgBuDesc" type="java.lang.String">
<column name="acctg_bu_desc" />
</property>
<many-to-one name="transAcctgBuDAO" class="TransAcctgBuDAO" not-null="false"
insert="false" update="false" not-found="ignore" fetch="select">
<column name="acctg_bu_id" />
</many-to-one>
</class>
Mapping
<many-to-one name="transAcctgBuTxt" class="TransAcctgBuTxtDAO"
insert="false" update="false" cascade="all">
<column name="store_nbr"></column>
</many-to-one>
Composite Key Example
<one-to-one name="transAcctgBuTxt" class="TransAcctgBuTxtDAO" property-
ref="transAcctgBuTxtPKDAO.acctgBuId">
<column name = "store_nbr">
</one-to-one>
Error
Foreign key (FK47A121BB6617227C:transpo_acct:bhrv_journal [store_nbr]))
must have same number of columns as the referenced primary
key (transpo_acct:trans_acctg_bu_txt [acctg_bu_id,language_code])
Thanks in advance
Since TransAcctgBuTxtDAO has the composite id of 2 columns, you NEED to provide both values to uniquely identify one entity, period. Maybe you could reconsider if it is really a many-to-one relation between BhrvJournalDAO and TransAcctgBuTxtDAO. Mabye it is actually many-to-many.
I have two objs; Document and DocumentBatch
Document
public class Document implements Serializable {
....
private String documentId; //PK of Document
private DocumentBatch documentBatch;
....}
DocumentBatch
public class DocumentBatch implements Serializable {
private String batchId;//PK of DocumentBatch
private List<Document> lDocuments = new LinkedList<Document>();
.....
public void insertDocument(Document document) {
lDocuments.add(document); // lDocuments is a list DocumentBatch
document.setDocumentBatch(this);
....}
Hibernate mapping:
Document
<class name="Document" table="DOCUMENTS">
.....
<id name="documentID" column="DOCUMENT_ID" type="string" />
<many-to-one name="documentBatch" class="DocumentBatch" not-null="false"
cascade="save-update" lazy="false" insert="false" update="false">
<column name="BATCH_ID" not-null="true" />
</many-to-one>
......
</class>
DocumentBatch
<class name="DocumentBatch" table="DOCUMENT_BATCHES">
<id name="batchId" column="BATCH_ID" type="string" />
<list name="lDocuments" table="DOCUMENTS" cascade="all"
inverse="false" lazy="false" mutable="true">
<key not-null="true">
<column name="BATCH_ID" />
</key>
<list-index column="LIST_INDEX" base="0" />
<one-to-many class="Document" />
</list>
......
</class>
DocumentBatch has a list of Document
Document has batchId which is PK of DocumentBatch. I have set in Hibernate mapping of the list in DocumentBatch with
inverse="false"
and in Document the many-to-one relation set insert="false" update="false"
but when I try to save a Document obj, its DocumentBatch won't be saved.
How to solve. If someone could help .... hope all have a nice weekend.
Oracle DB :
Of Document
CREATE TABLE DOCUMENTS(
DOCUMENT_ID VARCHAR2(255 CHAR) NOT NULL,
BATCH_ID VARCHAR2(255 CHAR) NOT NULL,
...);
CREATE UNIQUE INDEX PK_DOCUMENT ON DOCUMENTS (DOCUMENT_ID);
ALTER TABLE DOCUMENTS ADD (CONSTRAINT PK_DOCUMENT PRIMARY KEY (DOCUMENT_ID) USING INDEX PK_DOCUMENT);
ALTER TABLE DOCUMENTS ADD (CONSTRAINT FK_DOCUMENT_BATCH_ID FOREIGN KEY (BATCH_ID) REFERENCES DOCUMENT_BATCHES (BATCH_ID) ON DELETE CASCADE);
Of DocumentBatch
CREATE TABLE DOCUMENT_BATCHES(
BATCH_ID VARCHAR2(255 CHAR) NOT NULL
...);
ALTER TABLE DOCUMENT_BATCHES ADD (
PRIMARY KEY (BATCH_ID));
If I do understand you correctly. From the snippets you've shown I would say the behavior which you are experiencing is correct. You are saying that the problem is:
but when I try to save a Document obj, its DocumentBatch won't be
saved.
Because there is explicit setting NOT insert and NOT update:
<many-to-one name="documentBatch" class="DocumentBatch"
not-null="false" cascade="save-update" lazy="false"
insert="false" // Hibernate do not insert
update="false" // Hibernate do not update
>
Then the result is correct.
If you would like to save the relation to the DocumentBatch then just change that mapping:
insert="true" // Hibernate DO insert
update="true" // Hibernate DO update
(or remove them, because default is true), And then if you will assign a DocumentBatch to your Document and save it - all will be persisted correctly. this should solve it.
EDIT: duplicated column exception
I am guessing that your entity Document has another field mapped to batch_id.
<many-to-one name="documentBatch" class="DocumentBatch" not-null="false"
cascade="save-update" lazy="false" insert="false" update="false">
<column name="BATCH_ID" not-null="true" />
</many-to-one>
// this is not in the snippet above, but I guess it is there
<property name="batchId" column="BATCH_ID"/>
and in code:
public class Document implements Serializable {
private String documentId; //PK of Document
private DocumentBatch documentBatch;
// this is not in the snippet above, but I guess it is there
private String batchId;
....}
If this is the case, then two different properties are mapped to same column and that's why we are experiencing the: duplicated column exception
In case my expectations are correct, change the mapping this way:
<many-to-one name="documentBatch" class="DocumentBatch" not-null="false"
cascade="save-update" lazy="false" > // insert and update removed
<column name="BATCH_ID" not-null="true" />
</many-to-one>
<property name="batchId" formula="[BATCH_ID]" insert="false" update="false"/>
So the documentBatch will be used for insert and update, while the batchId will be only for readonly.
I try to write mapping to tables. I decided that id and versionId - composite-id for table test. But in table test_question I have composite-id consists from question_id, test_id and versionId. I can't understand as I can use one composite-id id-versionId as part other composite-id.
I have next peace of script.
Create Table Test
(
id int unsigned not null,
versionId int unsigned not null,
test_text varchar(200) not null,
subject_id int unsigned not null,
primary key (id, versionId),
Foreign key (subject_id) REferences Subject(id)
On delete cascade on update cascade
)
Engine InnoDB CHARACTER SET utf8;
Create Table Question
(
id varchar(36) not null,
question_text varchar(200) not null,
primary key(id)
)
Engine InnoDB CHARACTER SET utf8;
Create table Test_Question
(
test_id int unsigned not null,
question_id varchar(36) not null,
versionId int unsigned not null,
primary key(test_id, question_id, versionId),
Foreign key(test_id,versionId) References Test(id, versionId),
Foreign key(question_id) References Question(id)
)
Engine InnoDB CHARACTER SET utf8;
And my mapping
Test.hbm.xml
<hibernate-mapping>
<class name="by.bsuir.testapp.model.Test" table="TEST">
<composite-id>
<key-property name="testId" type="long" column="TEST_ID" />
<key-property name="versionId" type="long" column="VERSION_ID" />
<generator class="assigned"/>
</composite-id>
<property name="testName" type="string">
<column name="TESTNAME" />
</property>
<many-to-one name="subject" class="by.bsuir.testapp.model.Subject"
fetch="join">
<column name="SUBJECT_ID" />
</many-to-one>
</class>
</hibernate-mapping>
and for TestQuestion.hbm.xml
<hibernate-mapping>
<class name="by.bsuir.testapp.model.TestQuestion" table="TEST_QUESTION">
<composite-id>
<key-property name="test" type="long" column="TEST_ID" />
<key-property name="question" type="long" column="QUESTION_ID" />
<generator class="assigned" />
</composite-id>
</class>
</hibernate-mapping>
What should be id in table TestQuestion? I mean that QUESTION_ID-TEST_ID-VERSION_ID is whole composite id.
I need to map a single class to two tables (both with multiple columns primary key). Let's say TABLE1 has id1, id2, id3 and TABLE2 has id1, id2 as primary keys. Now when writing the mapping file I would do something like the following:
<hibernate-mapping package="beans">
<class name="TABLE1Class" table="TABLE1">
<composite-id name="table1PK" class="TABLE1PKClass">
<key-many-to-one name="id1" class="ID1Class" column="id1"/>
<key-many-to-one name="id2" class="ID2Class" column="id2"/>
<key-many-to-one name="id3" class="ID3Class" column="id3"/>
</composite-id>
<property name="someProperty" type="integer" not-null="true" column="x"/>
<join table="TABLE2">
<key column="id1" />
<!-- <key column="id2"/> The join tag accepts only one key tag!!!
How do I map the second key??? -->
<property name="propertyFromTable2" type="float" not-null="true"/>
</join>
</class>
</hibernate-mapping>
As you can see the join tag accepts only one key tag! How do I map the second Id?
<key> may contain multiple <column> elements:
<key>
<column name = "id1" />
<column name = "id2" />
<column name = "id3" />
</key>