Multiple relationships with single mapping table w

2020-07-27 14:57发布

问题:

I have two base abstract classes and there are multiple additional classes derived from these two, adding additional attributes etc.

There exist relations between those specific derived types.

A simple illustrating example:

Role and Group classes are abstract, but they are not marked as @MappedSuperclass.

InheritanceType.JOINED strategy is being used, so both tables Role (for abstract class) and AdminRole (for derived class) should exist (they both will have the same RoleID).

  • DiscussionGroup has one AdminRole property, a Set<DiscussantRole>, a Set<ManagerRole>
  • WorkingGroup has Set<WorkerRole>, Set<ManagerRole>
Role 
|-- AdminRole
|-- DiscussantRole
|-- ManagerRole
|-- WorkerRole

Group
|-- DiscussionGroup
|-- WorkingGroup

Because the number of derived classes can grow, and because classes derived from Role can have relations to different classes derived from Group (and vice versa) this would lead to a large amount of different mapping tables (Worker_DiscussionGroup, Worker_WorkingGroup) or multiple foreign key columns (in M:1 relationships - e.g. a ManagerRole would have to have DiscussionGroupID and WorkingGroupId). I want to map all these relations through one common mapping table.

Role_Group (RoleID, GroupId)

We use Hibernate to generate the DDL schema (hbm2ddl.auto=create) during current development (we will use a static schema definition for later production use). Hibernate automatically creates Foreign keys for the relations, and thats quite good for us.

If I instruct it to use the same mapping a table for joins (for the many-to-many, many-to-many and for one-to-one as well), it will try to create foreign keys as well. And it is of course not possible to create a foreign key on RoleID from Role_Group to AdminRole and DiscussantRole at the same time, so I get an error.

Is there any way, how to instruct Hibernate

  1. to generate selected relationships without foreign keys

    or

  2. to define that the relation should be based on the abstract ancestors (i.e. DiscussionGroup and its Set should be mapped as 1:N - Group and Set)?

回答1:

Following works for me as an answer for question number 2:

@ForeignKey( name = "none" )

No foreign key is being generated for the relationship.

@Cascade( value = { CascadeType.ALL } )
@OneToOne( fetch = FetchType.EAGER, orphanRemoval = true )
@JoinTable( name = "Role_Group",
            inverseJoinColumns = { @JoinColumn( referencedColumnName = "rolleId", name = "RolleID" ) },
            joinColumns = { @JoinColumn( referencedColumnName = "groupId", name = "GroupID" ) } )

@ForeignKey( name = "none" )
public AdminRole getAdmin()

Sources:

  • Disabling foreign key generation in hbm2ddl
  • Forcing hbm2ddl to not generate an FK

Based on the sources, it is an undocumented feature which was seen in release notes:

Changes in version 2.1.9 (xx.x.xxxx)
------------------------------------
* TimesTenDialect now supported (look at TimesTenDialect for certain limitations)
* foreign-key="none" can be used to disable generation of a foreign key.

In xml config - you would use it like this

<many-to-one name="Hospital" column="hospitalId" property-ref="hospitalId" update="false" insert="false" foreign-key="none">

as mentioned in Let Hibernate Connect Your World! (see the page sources - xml configuration is not shown on the page)

Note:

This does not solve the whole problem however. Hibernate cannot get the right data through this mapping table for getAdmin and getManagers as well, because it looks to Role_Group, finds RoleIDs for DiscussionGroup GroupID and has no idea if it is for AdminRole or ManagerRole and gives "No row with the given identifier exists" error".

Simimilar mapping works however when I use such table in Group or DiscussionGroup as public Set<Role> getRoles(), Hibernate will successfully load derived classes (AdminRole, ManagerRole) to the Set.



回答2:

I think that you should keep multiple tables. It just maps the relationship that exists in your classes.

If you don't want that then you could define a Set<Role> inside abstract Group and a Set<Group> in abstract Role. You would not have any "subsets" in the subclasses; you just fill the set from the abstract class with the appropriate elements of the right type. The automatic mapping would then give you a single join table like you want.