I am working on a legacy code base with an existing DB schema. The existing code uses SQL and PL/SQL to execute queries on the DB. We have been tasked with making a small part of the project database-engine agnostic (at first, change everything eventually). We have chosen to use Hibernate 3.3.2.GA and "*.hbm.xml" mapping files (as opposed to annotations). Unfortunately, it is not feasible to change the existing schema because we cannot regress any legacy features.
The problem I am encountering is when I am trying to map a uni-directional, one-to-many relationship where the FK is also part of a composite PK. Here are the classes and mapping file...
CompanyEntity.java
public class CompanyEntity { private Integer id; private Set<CompanyNameEntity> names; ... }
CompanyNameEntity.java
public class CompanyNameEntity implements Serializable { private Integer id; private String languageId; private String name; ... }
CompanyNameEntity.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.jboss.org/dtd/hibernate/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.example"> <class name="com.example.CompanyEntity" table="COMPANY"> <id name="id" column="COMPANY_ID"/> <set name="names" table="COMPANY_NAME" cascade="all-delete-orphan" fetch="join" batch-size="1" lazy="false"> <key column="COMPANY_ID"/> <one-to-many entity-name="vendorName"/> </set> </class> <class entity-name="companyName" name="com.example.CompanyNameEntity" table="COMPANY_NAME"> <composite-id> <key-property name="id" column="COMPANY_ID"/> <key-property name="languageId" column="LANGUAGE_ID"/> </composite-id> <property name="name" column="NAME" length="255"/> </class> </hibernate-mapping>
This code works just fine for SELECT and INSERT of a Company with names. I encountered a problem when I tried to update and existing record. I received a BatchUpdateException and after looking through the SQL logs I saw Hibernate was trying to do something stupid...
update COMPANY_NAME set COMPANY_ID=null where COMPANY_ID=?
Hibernate was trying to dis-associate child records before updating them. The problem is that this field is part of the PK and not-nullable. I found the quick solution to make Hibernate not do this is to add "not-null='true'" to the "key" element in the parent mapping. SO now may mapping looks like this...
CompanyNameEntity.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.jboss.org/dtd/hibernate/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.example"> <class name="com.example.CompanyEntity" table="COMPANY"> <id name="id" column="COMPANY_ID"/> <set name="names" table="COMPANY_NAME" cascade="all-delete-orphan" fetch="join" batch-size="1" lazy="false"> <key column="COMPANY_ID" not-null="true"/> <one-to-many entity-name="vendorName"/> </set> </class> <class entity-name="companyName" name="com.example.CompanyNameEntity" table="COMPANY_NAME"> <composite-id> <key-property name="id" column="COMPANY_ID"/> <key-property name="languageId" column="LANGUAGE_ID"/> </composite-id> <property name="name" column="NAME" length="255"/> </class> </hibernate-mapping>
This mapping gives the exception...
org.hibernate.MappingException: Repeated column in mapping for entity: companyName column: COMPANY_ID (should be mapped with insert="false" update="false")
My problem now is that I have tryed to add these attributes to the key-property element but that is not supported by the DTD. I have also tryed changing it to a key-many-to-one element but that didn't work either. So...
How can I map "insert='false' update='false'" on a composite-id key-property which is also used in a one-to-many FK?
We have a new tag <composite-id> tag added which lets us map composite primary key in hibernate. This tag accepts <key-property> mapping, which holds the individual property which has to be marked as primary key. In our example, empId and department will jointly act as composite primary key.
A relational database composite key contains two or more columns which together for the primary key of a given table. In the diagram above, the employee table has a Composite Key, which consists of two columns: Every Employee can also have a Phone, which uses the same composite key to reference its owning Employee.
In order to define a composite primary key, we have used @Id annotation twice ( empId and department ). @GeneratedValue – This annotation is used to specify how the primary key should be generated. Here SEQUENCE Strategy will be used as this the default strategy for Oracle
To see how it works, consider the following persistence logic: INSERT INTO employee (name, company_id, employee_number) We can even map relationships using the information provided within the Composite Key itself.
I think the annotation you are looking for is:
public class CompanyName implements Serializable { //... @JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID", insertable = false, updatable = false) private Company company;
And you should be able to use similar mappings in a hbm.xml as shown here (in 23.4.2):
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/example-mappings.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With