Hi,
I have three classes, Car, Motorcycle and Engine.
-------------- --------------
| Car | | Motorcycle |
-------------- --------------
/\ /\
\/ \/
| |
engine 0..1 | | engine 0..1
-----------------------
| Engine |
-----------------------
Cars and Motorcycles both have Engines, so each have an EReference ('engine') referring to Engine.
e.g. (in Car)
<eStructuralFeatures xsi:type="ecore:EReference" name="engine" eType="//Engine" containment="true" resolveProxies="false" eOpposite="//Engine/oppCarengine">
<eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
<details key="kind" value="element"/>
<details key="name" value="engine"/>
</eAnnotations>
<eAnnotations source="teneo.jpa">
<details key="value" value="@OneToOne(mappedBy="oppCarengine")"/>
</eAnnotations>
</eStructuralFeatures>
As you can see, it refers to an opposite feaure ('oppCarengine') in Engine, which looks like this:
<eStructuralFeatures xsi:type="ecore:EReference" name="oppCarengine" eType="//Car" transient="true" eOpposite="//Car/engine">
<eAnnotations source="teneo.jpa">
<details key="value" value="@JoinColumn(name="CAR_ENGINE_XXXID")"/>
</eAnnotations>
</eStructuralFeatures>
The relevant excerpts from the generated Hibernate mappings look like this:
<class name="CarImpl" ...
...
<one-to-one name="engine" entity-name="com.example.bdp2.Engine" cascade="all" lazy="false" property-ref="oppCarengine"/>
</class>
<class name="EngineImpl" ...
...
<many-to-one name="oppCarengine" entity-name="com.example.bdp2.Car" cascade="merge,persist,save-update,lock,refresh" not-null="false" lazy="false" insert="true" update="true">
<column not-null="false" unique="false" name="`CAR_ENGINE_XXXID`" index="IDX_1_BDP2_6F6B83114642FBB2F19"/>
</many-to-one>
</class>
(I have a similar 'engine' feature on Motorcycle, which has an opposite ('oppMotorcycleengine') on Engine)
The primary reason for having this opposite is because I want the database to have a child-to-parent foreign key; i.e. a column in Engine referring to Car, rather than the other way around.
This works fine, but the problem is that the SQL to fetch a Car is needlessly traversing the reference from Engine to Motorcycle, which is obviously unnecessary, given that a single engine can only be contained within either a Car OR a Motorcycle, but not both, given that they are containment relationships.
i.e.
SELECT this_.[xxx_id] AS XXX1_904_2_,
this_.[xxx_version] AS XXX3_904_2_,
this_.[id] AS ID4_904_2_,
engine2_.[xxx_id] AS XXX1_905_0_,
engine2_.[capacity] AS CAPACITY3_905_0_,
engine2_.[car_engine_xxxid] AS CAR5_905_0_,
engine2_.[motorcycle_engine_xxxid] AS MOTORCYCLE6_905_0_,
motorcycle3_.[xxx_id] AS XXX1_906_1_,
motorcycle3_.[xxx_version] AS XXX3_906_1_,
motorcycle3_.[id] AS ID4_906_1_
FROM [car] this_
LEFT OUTER JOIN [engine] engine2_
ON this_.[xxx_id] = engine2_.[car_engine_xxxid]
LEFT OUTER JOIN [motorcycle] motorcycle3_
ON engine2_.[motorcycle_engine_xxxid] =
motorcycle3_.[xxx_id]
WHERE this_.[xxx_id] IN ( ? )
In reality, what I've shown is a dramatic simplification of my model. There are several other classes also containing Engines, which in turn are part of a bigger model with other patterns of classes like Engine having multiple containers. This is resulting in a crazy number of joins which is hitting performance.
Is there anything I could do in the Ecore to prevent this? If not, can I modify the Hibernate mapping somehow to prevent the pointless traversal up the containment tree?
Many thanks for any advice!
Regards,
Ben.
[Updated on: Thu, 11 July 2019 17:12]
Report message to a moderator