I do not understand how to define a composite key, including one foreign key, inside of a set
Here my Objects:
MyObject MySubset
-------- -------------
String myId String subAttribute
String myAttribute String subValue
Set<MySubset> mySubset
I want to have two tables MyObjectTable and MySubsetTable.myId is primary key of MyObjectTable. I would like to define the FK myId and subAttribute as the composite keys of MySubsetTable.
What would the hibernate mapping of in xml look like?
<hibernate-mapping>
<class table="myObjectTable" name="MyObject">
<id name="myId">
<column name="myId"/>
</id>
<property name="myAttribute"> <column name=....> </property>
<set cascade="all, delete-orphan table="MySubsetTable" name"mySubset" ...>
<!-- How should I define my key? -->
<composite-element class="MySubset">
<property name="subAttribute"> <column name="subAttribute"/> </property>
<property name="subValue"> <column name="subValue"/> </property>
</composite-element>
</set>
</class>
</hibernate-mapping>
I solved my problem by not using a set, but a map:
<hibernate-mapping>
<class table="myObjectTable" name="MyObject">
<id name="myId">
<column name="myId"/>
</id>
<property name="myAttribute"> <column name=....> </property>
<map cascade="all, delete-orphan table="MySubsetTable" name"mySubset" ...>
<key not-null="true" foreign-key="FK_MyObj" column="MyObject"/>
<map-key type="string" column="myAttribute"/>
<element type="string" column="myValue"/>
</map>
</class>
</hibernate-mapping>
Maybe this helps someone else...
Related
The error I get is " org.hibernate.MappingException: Repeated column in mapping for entity: cdd.model.Answer column: answer_id (should be mapped with insert="false" update="false") ". However when I put those as attributes I get the error: "Attribute "insert" must be declared for element type "id"."
Any help would be appreciated.
Class:
public class Answer {
UUID answerID;
String content;
//constructors and getters and setters
}
Table:
CREATE TABLE IF NOT EXISTS answer (
answer_id uuid NOT NULL ,
content text NOT NULL,
primary key(answer_id)
);
Hibernate Mapping File:
<!-->==== Answer ====<!-->
<class name="cdd.model.Answer" table="answer" >
<id column="answer_id" name="answerID"
type="org.hibernate.type.PostgresUUIDType"
insert="false" update="false">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<property column="content" name="content" type="org.hibernate.type.TextType"/>
</class>
Note
I have a Question class where a question has a set of answers. This is the mapping I used. I am posting it because the error I'm gettng may be because of how I mapped this one-to-many relationship (I'm not sure).
<!-- ==== Question ==== -->
<class name="cdd.model.Question" table="question">
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
< generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<many-to-one column="submitted_by" name="submittedBy" not-null="true"/>
<many-to-one column="parentCategory" name="parentCategory" not-null="true"/>
<property column="title" name="title" type="org.hibernate.type.TextType"/>
<property column="correct_answer" name="correctAnswer" type="org.hibernate.type.TextType"/>
<property column="date_submitted" name="dateSubmitted" type="org.hibernate.type.TimestampType"/>
<set cascade="all" name="answers" table="answer">
<key column="answer_id" not-null="true"/>
<one-to-many class="cdd.model.Answer"/>
</set>
<join inverse="true" optional="true" table="category_questions">
<key column="question_id"/>
</join>
<join inverse="true" optional="true" table="accepted_questions_by_user">
<key column="question_id"/>
</join>
</class>
Question Entity:
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
Answer Entity:
<id column="answer_id" name="answerID" type="org.hibernate.type.PostgresUUIDType"
insert="false" update="false">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
remove insert="false" update="false" from Answr entity
should look someting like this
<id column="answer_id" name="answerID" type="org.hibernate.type.PostgresUUIDType" >
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
update, insert (optional - defaults to true): specifies that the mapped columns should be included in SQL UPDATE and/or INSERT statements. Setting both to false allows a pure "derived" property whose value is initialized from some other property that maps to the same column(s), or by a trigger or other application.
Please have a look at the below XML code
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Feb 17, 2015 10:01:43 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
<class name="model.main.Family" table="family" catalog="****" optimistic-lock="version">
<id name="idFamily" type="int">
<column name="idFamily" />
<generator class="assigned" />
</id>
<many-to-one name="employee" class="model.main.Employee" fetch="select">
<column name="idEmployee" not-null="true" />
</many-to-one>
<property name="firstName" type="string">
<column name="FirstName" length="45" />
</property>
<property name="middleName" type="string">
<column name="MiddleName" length="45" />
</property>
<property name="lastName" type="string">
<column name="LastName" length="45" />
</property>
<property name="dob" type="date">
<column name="DOB" length="10" />
</property>
<property name="passportNumber" type="string">
<column name="PassportNumber" length="45" not-null="true" />
</property>
<property name="dateLeft" type="date">
<column name="DateLeft" length="10" />
</property>
<property name="lastUpdated" type="timestamp">
<column name="LastUpdated" length="19" not-null="true" />
</property>
<set name="visas" table="visa" inverse="true" lazy="true" fetch="select">
<key>
<column name="idFamily" />
</key>
<one-to-many class="model.main.Visa" />
</set>
</class>
</hibernate-mapping>
It is the Hibernate mapping class of my database table Family. We create the database separately using MySQL Work bench and then generate the mapping classes. We auto generated the mapping files using netbeans as mentioned in "Generating Hibernate Mapping Files and Java Classes" section of netbeans tutorial.
Now we have a problem. That is, we changed the primary key (idFamily) of our table Family to an auto generated field inside MySQL. Now, how can we change the above hibernate code so it identifies the idFamily as an auto generated one?
The other question is, manually editing one mapping class without regenerating all the mappings via a tool can "break" the system? For an example, like messing up with relationships?
In Annotation It work for me as
#GeneratedValue(strategy= GenerationType.IDENTITY)
for you hope it works
<generated-value strategy="IDENTITY" />
You're looking for an identity column. That indicates that the column value is auto-generated as an identity for the row by the RDBMS.
<generator class="identity" />
See the these Hibernate docs for more information. According to it:
Identity
supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. The returned identifier is of type long, short or int.
Just replace your generator class to increment it will treat it as autoincrement
<generator class="increment"/>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tech.spring4.model.User" table="Customer">
<id name="id" type="long">
<column name="USERID" unique="true"/>
<generator class="increment"/>
</id>
<property name="username"><column name="username" length="30" not-null="true"></column></property>
<property name="email"><column name="email" length="100" not-null="true"></column></property>
<property name="address"><column name="address" length="100" not-null="true"></column></property>
</class>
</hibernate-mapping>
Hibernate is giving me the following exception
org.hibernate.MappingException: Repeated column in mapping for entity: Pricelist column: ID_OFFER (should be mapped with insert="false" update="false")
but I really cannot find the duplicate reference to ID_OFFER. Here they are the two mapping files involved.
Offer.hbm.xml
<hibernate-mapping>
<class name="Offer" table="OFFERS">
<id name="idOffer" type="java.lang.Long">
<column name="ID_OFFER" not-null="true" precision="10" scale="0"
sql-type="NUMBER" unique="true"/>
<generator class="native">
<param name="sequence">OFFERS_SEQ</param>
</generator>
</id>
<property generated="never" lazy="false" name="name" type="string">
<column name="name" not-null="true" sql-type="VARCHAR2" unique="true"/>
</property>
<set name="pricelists" sort="unsorted" table="PRICELISTS">
<key not-null="true">
<column name="ID_OFFER" not-null="true" precision="10" scale="0" sql-type="NUMBER"/>
</key>
<one-to-many class="Pricelist"/>
</set>
</class>
</hibernate-mapping>
Pricelist.hbm.xml
<hibernate-mapping>
<class name="Pricelist" table="PRICELISTS">
<id name="idPricelist" type="java.lang.Long">
<column name="ID_PRICELIST" not-null="true" precision="10" scale="0" sql-type="NUMBER"/>
<generator class="native">
<param name="sequence">PRICELISTS_SEQ</param>
</generator>
</id>
<property name="name" type="string">
<column length="255" name="NAME" not-null="true" sql-type="VARCHAR2"/>
</property>
<property name="versionMajor" type="integer">
<column name="VERSION_MAJOR" not-null="true" precision="5" scale="0" sql-type="NUMBER"/>
</property>
<property name="versionMinor" type="integer">
<column name="VERSION_MINOR" not-null="true" precision="5" scale="0" sql-type="NUMBER"/>
</property>
<many-to-one class="Offer" name="offer">
<column name="ID_OFFER" not-null="true" precision="10" scale="0" sql-type="NUMBER"/>
</many-to-one>
<many-to-one class="PricelistStatus" name="status">
<column name="ID_STATUS_PRICELIST" not-null="true" precision="10"
scale="0" sql-type="NUMBER"/>
</many-to-one>
<property name="validFrom" type="calendar">
<column name="INIT_TIMESTAMP" not-null="true" scale="6" sql-type="TIMESTAMP"/>
</property>
<property name="validUntil" type="calendar">
<column name="END_TIMESTAMP" not-null="false" scale="6" sql-type="TIMESTAMP"/>
</property>
</class>
</hibernate-mapping>
I am going crazy. Can anybody see where is it supposed to be duplicated the reference to the column ID_OFFER? Please note: two tables of my schema have a column named like that: OFFERS.ID_OFFER, which is the primary key of the table OFFERS and PRICELIST.ID_OFFER which has a foreign key constraint referencing, obviously, OFFERS.ID_OFFER.
You forgot about map this column as owning side (Offer.hbm.xml file)
<set name="pricelists" sort="unsorted" table="PRICELISTS">
<key not-null="true">
<column name="ID_OFFER" not-null="true" precision="10" scale="0" sql-type="NUMBER"/>
</key>
<one-to-many class="Pricelist"/>
</set>
it should look like (look at inverse="true"):
<set name="pricelists" sort="unsorted" table="PRICELISTS" inverse="true">
<key not-null="true">
<column name="ID_OFFER" not-null="true" precision="10" scale="0" sql-type="NUMBER"/>
</key>
<one-to-many class="Pricelist"/>
</set>
What #zxcf said, actually made the exception disappear, but made another question appear in my mind. Why doesn't the following mapping creates the same problem?
ServiceElement.hbm.xml
<hibernate-mapping>
<class name="ServiceElement" table="SERVICE_ELEMENTS">
<id name="idServiceElement" type="java.lang.Long">
<column name="ID_SERVICE_ELEMENT" not-null="true" precision="10"
scale="0" sql-type="NUMBER" unique="true"/>
<generator class="native">
<param name="sequence">SERVICE_ELEMENTS_SEQ</param>
</generator>
</id>
<set name="prices" table="PRICES">
<key>
<column name="ID_SERVICE_ELEMENT" not-null="true" precision="10"
scale="0" sql-type="NUMBER"/>
</key>
<one-to-many class="Price"/>
</set>
</class>
</hibernate-mapping>
Price.hbm.xml
<hibernate-mapping>
<class name="Price" table="PRICES">
<id name="idPrice" type="java.lang.Long">
<column name="ID_PRICE" not-null="true" precision="10" scale="0"
sql-type="NUMBER" unique="true"/>
<generator class="native">
<param name="sequence">PRICES_SEQ</param>
</generator>
</id>
<many-to-one class="ServiceElement" name="serviceElement">
<column name="ID_SERVICE_ELEMENT" not-null="true" precision="10"
scale="0" sql-type="NUMBER"/>
</many-to-one>
</class>
</hibernate-mapping>
Isn't it the exact same mapping? But it does not throw any exception. Here they go the DDLs:
CREATE TABLE "CE_PRICELIST"."OFFERS"
( "ID_OFFER" NUMBER(10,0),
"NAME" VARCHAR2(255 CHAR));
CREATE UNIQUE INDEX "CE_PRICELIST"."OFFERS_PK" ON "CE_PRICELIST"."OFFERS" ("ID_OFFER");
ALTER TABLE "CE_PRICELIST"."OFFERS" ADD CONSTRAINT "OFFERS_PK" PRIMARY KEY ("ID_OFFER");
CREATE TABLE "CE_PRICELIST"."PRICELISTS"
( "ID_PRICELIST" NUMBER(10,0),
"ID_OFFER" NUMBER(10,0));
CREATE UNIQUE INDEX "CE_PRICELIST"."PRICELISTS_PK" ON "CE_PRICELIST"."PRICELISTS" ("ID_PRICELIST");
ALTER TABLE "CE_PRICELIST"."PRICELISTS" ADD CONSTRAINT "PRICELISTS_PK" PRIMARY KEY ("ID_PRICELIST");
ALTER TABLE "CE_PRICELIST"."PRICELISTS" ADD CONSTRAINT "PRICELISTS_OFFER_ID_FK" FOREIGN KEY ("ID_OFFER") REFERENCES "CE_PRICELIST"."OFFERS" ("ID_OFFER") ENABLE;
CREATE TABLE "CE_PRICELIST"."SERVICE_ELEMENTS"
( "ID_SERVICE_ELEMENT" NUMBER(10,0));
CREATE UNIQUE INDEX "CE_PRICELIST"."SERVICE_ELEMENTS_PK" ON "CE_PRICELIST"."SERVICE_ELEMENTS" ("ID_SERVICE_ELEMENT");
ALTER TABLE "CE_PRICELIST"."SERVICE_ELEMENTS" ADD CONSTRAINT "SERVICE_ELEMENTS_PK" PRIMARY KEY ("ID_SERVICE_ELEMENT");
CREATE TABLE "CE_PRICELIST"."PRICES"
( "ID_PRICE" NUMBER(10,0),
"ID_SERVICE_ELEMENT" NUMBER(10,0));
CREATE UNIQUE INDEX "CE_PRICELIST"."PRICES_PK" ON "CE_PRICELIST"."PRICES" ("ID_PRICE");
ALTER TABLE "CE_PRICELIST"."PRICES" ADD CONSTRAINT "PRICES_SERVICE_ELEMENT_ID_FK" FOREIGN KEY ("ID_SERVICE_ELEMENT")
REFERENCES "CE_PRICELIST"."SERVICE_ELEMENTS" ("ID_SERVICE_ELEMENT") ENABLE;
OFFERS has a PK on ID_OFFER, which is referenced by PRICELISTS.ID_OFFER; Offer has a Set< Pricelist >;
SERVICE_ELEMENTS has a PK on ID_SERVICE_ELEMENT, which is referenced by PRICES.ID_SERVICE_ELEMENT; ServiceElement has a Set< Price >;
So why is the inverse attribute needed in one case and not in another one?
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.
The configuration for User class :
<class name="User" table="users" lazy="false">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="type" column="type"/>
<many-to-one name="parent" column="parent"/>
<property name="loginName" column="login_name" unique="true" not-null="true" index="idx_users_login_name" length="50"/>
<property name="name" column="name" length="50"/>
<property name="password" column="password"/>
<property name="email" column="email" length="50"/>
<property name="locale" column="locale" length="20"/>
<property name="locked" column="locked"/>
<many-to-one name="metadata" column="metadata_id"/>
<set name="userSpaceRoles" cascade="all" inverse="true" lazy="false">
<key column="user_id"/>
<one-to-many class="UserSpaceRole"/>
</set>
</class>
and for the class MeetingItem is:
<class name="MeetingItem" table="meeting_item">
<id name="id" column="meeting_item_id" type="long">
<generator class="native"/>
</id>
<property name="summary" column="summary" type="string"/>
<property name="detail" column="detail" type="string"/>
<many-to-one name="space" column="space_id"/>
<property name="date" column="date" type="date"/>
<list name="users" cascade="all" lazy="false">
<key column="meeting_item_id"/>
<index column="idx"/>
<one-to-many class="User"/>
</list>
</class>
The problem is I am getting the exception:
org.hibernate.MappingException: Association references unmapped class: info.domain.User
at org.hibernate.cfg.HbmBinder.bindCollectionSecondPass(HbmBinder.java:2380)
at org.hibernate.cfg.HbmBinder.bindListSecondPass(HbmBinder.java:2231)
at org.hibernate.cfg.HbmBinder$ListSecondPass.secondPass(HbmBinder.java:2729)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:43)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1130)
at org.hibernate.cfg.Configuration.generateSchemaUpdateScript(Configuration.java:936)
at org.hibernate.tool.hbm2ddl.SchemaUpdate.execute(SchemaUpdate.java:140)
The mapping of the list is creating the problem. What I am doing wrong?
Edit:
This two configuration resides in different file, if these two are placed in same xml then the problem is not occurring.
please add a reference to the mapping file (which maps info.domain.User) into hibernate.cfg.xml.
Please add class level annotations to register the class as a Spring bean, i.e #Entity in this case since you are not using xml configuration.