How to have the same column as part of composite id and many to one relationship using XML Mappings?

We have a legacy code base and can’t move to annotations. Please suggest a hbm xml mapping based solution.

Example:
We have 3 entities: Manager, Employee, and Company
Manager and Employee has a one-to-many relationship. And both of these entities have a composite id with SEQ and COMPANY_FK. **Now the problem arises in case of Employee entity, where COMPANY_FK is part of the composite id and many-to-one relationship as well.
**

Caused by: org.hibernate.MappingException: Repeated column in mapping for entity:
Employee column: COMPANY_FK
(should be mapped with insert="false" update="false")

But if I map it as insert=false and update=false, then the MANGER_FK column won’t be populated.

Entity: MANAGER
Columns:
MANAGER_SEQ
COMPANY_FK

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="domain" default-cascade="merge" auto-import="false">
	<class name="Manager" entity-name="MANAGER" table="MANAGER">
		<composite-id>
			<key-property name="Id" column="MANAGER_SEQ" type="Integer" access="property"/>
			<key-many-to-one name="Company" column="COMPANY_FK" class="Company" access="property" lazy="false"/>
		</composite-id>

		<set name="EmployeeSet" inverse="true" access="property" batch-size="100">
			<key>
				<column name="MANAGER_FK"/>
				<column name="COMPANY_FK"/>
			</key>
			<one-to-many class="Employee" />
		</set>
	</class>
</hibernate-mapping>

Entity: Employee
Columns:
EMPLOYEE_SEQ
COMPANY_FK
MANAGER_FK

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="domain" default-cascade="merge" auto-import="false">
	<class name="Employee" entity-name="EMPLOYEE" table="EMPLOYEE">
		<composite-id>
			<key-property name="Id" column="EMPLOYEE_SEQ" type="Integer" access="property"/>
			<key-many-to-one name="Company" column="COMPANY_FK" class="Company" access="property" lazy="false"/>
		</composite-id>

		<many-to-one name="Manager" class="Manager" unique="true">
			<column name="MANAGER_FK"/>
			<column name="COMPANY_FK"/>
		</many-to-one>

	</class>
</hibernate-mapping>

You have two options:

  • Mark the many-to-one as insertable = false, updatable = false and change the Java classes by adding a field for the MANAGER_FK column which is synced up with the Manager field.
  • Remove the COMPANY_FK column from the many-to-one association since MANAGER_SEQ is probably unique anyway.

@beikov
First option, if I mark many-to-one as insert=false and update=false, then Hibernate doesn’t insert MANGER_FK value. I don’t quite understand about changing java classes. We already have a field Manager object in Employee class.

Second option, we can’t go ahead with this option as we are using Postgres, and this is a hash partitioned table based on Company, so the Primary Key is a composite key with MANAGER_SEQ, COMPANY_FK. And we need the same for all queries for partition pruning.

You have to add a field in the Manager class for the FK:

@Entity
class Manager {
  @Id
  @Column(name = "EMPLOYEE_SEQ")
  Integer Id;
  @Id
  @ManyToOne
  @JoinColumn(name = "COMPANY_FK")
  Company Company;

  @ManyToOne
  @JoinColumn(name = "MANAGER_FK", insertable = false, updatable = false)
  @JoinColumn(name = "COMPANY_FK", insertable = false, updatable = false)
  Manager Manager;

  @Column(name = "MANAGER_FK")
  Integer ManagerFk;

  public void setManager(Manager Manager) {
     this.Manager = Manager;
     this.ManagerFk = Manager == null ? null : Manager.getId();
  }
}