Reputation: 3858
I have two entities: Vehicle and Weapon classes. They are mapped respectively to Vehicle.hbm.xml and Weapon.hbm.xml. Both have their respective DAOs. All are working fine.
Here's my idea. I want to create a class VehicleWeapon that has all the properties of the Vehicle and Weapon entities. I want to declare a Hibernate mapping file: VehicleWeapon.hbm.xml. I want the properties in the mapping file to match the properties of the Weapon and Vehicle entities.
So that when I add, delete, or save a VehicleWeapon entity, the corresponding changes will reflect on the backing Vehicle and Weapon tables. (I could probably do an HQL query, but I want to know first if the idea I'm asking is doable).
Is this possible? If yes, can you show me an example? (Of course, the Vehicle and Weapon classes are hypothetical classes. I do have an actual scenario where I need this idea to be implemented).
Or what better options are there? Can you provide a direct link or concrete example? Thanks a lot.
Upvotes: 1
Views: 6393
Reputation: 502
use joincolumn and inverseJoincolumn annotations for join two tables (Vehicle and Weapon) and put those in to transaction table (VehicleWeapon). use any mapping(onetoone, onetomany, etc) to map vehicle amd waepon tables with ondeletecascade and fetch.
Upvotes: 0
Reputation: 570575
Hibernate provides support to persist one entity class across multiple tables using the <join>
element (or the @SecondaryTable
annotation when using annotations). From the documentation:
5.1.20. Join
Using the
<join>
element, it is possible to map properties of one class to several tables that have a one-to-one relationship. For example:<join table="tablename" (1) schema="owner" (2) catalog="catalog" (3) fetch="join|select" (4) inverse="true|false" (5) optional="true|false"> (6) <key ... /> <property ... /> ... </join>
table
: the name of the joined table.schema
(optional): overrides the schema name specified by the root element.catalog
(optional): overrides the catalog name specified by the root<hibernate-mapping>
element.fetch
(optional - defaults tojoin
): if set to join, the default, Hibernate will use an inner join to retrieve a<join>
defined by a class or its superclasses. It will use an outer join for a defined by a subclass. If set to select then Hibernate will use a sequential select for a<join>
defined on a subclass. This will be issued only if a row represents an instance of the subclass. Inner joins will still be used to retrieve a<join>
defined by the class and its superclasses.inverse
(optional - defaults tofalse
): if enabled, Hibernate will not insert or update the properties defined by this join.optional
(optional - defaults tofalse
): if enabled, Hibernate will insert a row only if the properties defined by this join are non-null. It will always use an outer join to retrieve the properties.For example, address information for a person can be mapped to a separate table while preserving value type semantics for all properties:
<class name="Person" table="PERSON"> <id name="id" column="PERSON_ID">...</id> <join table="ADDRESS"> <key column="ADDRESS_ID"/> <property name="address"/> <property name="zip"/> <property name="country"/> </join> ...
This feature is often only useful for legacy data models. We recommend fewer tables than classes and a fine-grained domain model. However, it is useful for switching between inheritance mapping strategies in a single hierarchy, as explained later.
Using composition as you did is another option but it will obviously introduce another table (for the "aggregating" entity).
Upvotes: 1
Reputation: 3858
I found a working solution. As I mentioned earlier, I'm not allowed to modify the Vehicle and Weapon entities and the corresponding mapping files. Not even a single annotation is allowed.
I found this answer by Googling Ben's suggestion above which lead me to these articles: http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example/ and http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-one-to-one-1.html
The suggestion from mkyong is good but I didn't like the idea of keeping and assigning primary keys both ways as I have no way of editing the mapping files from the Vehicle and Weapon classes. See the comment "the main difficulty with one-to-one relationship is ensuring both are assigned the same primary key"
The suggestion from vaannila is somehow intriguing. It does a one-to-one relationship but without requiring any edits on other mapping files.
First, I declared my VehicleWeapon.hbm.xml as follows:
<hibernate-mapping package="com.armory">
<id name="id" type="long" >
<column name="id" />
<generator class="native" />
</id>
<many-to-one name="vehicle" class="Vehicle"
column="vehicle_column" cascade="all" unique="true" />
<many-to-one name="weapon" class="Weapon"
column="weapon_column" cascade="all" unique="true" />
Then I declared my standard VehicleWeapon POJO class. And the corresponding DAO implementation.
I run a couple of Junit/Spring tests. I was able to save, delete, retrieve without problems. All actions cascade to the corresponding Weapon and Vehicle tables.
The only downside with this method is Hibernate will create a third table vehicle_weapon that contains the id for the vehicle and weapon tables respectively as a reference.
The good thing is I didn't edit any existing entities or mapping files. I created a new one and compose a new object from two tables.
I still like to be able to map directly to the Weapon and Vehicle's properties instead of mapping directly to the Weapon and Vehicle entities. But for now, I think this is acceptable.
Upvotes: 1
Reputation: 7597
Not sure whether this is the answer, but Hibernate does allow for “compound” relationships, whereby you embed one class within another, and that gets mapped.
For example, here we create the "embeddable" class (Vehicle) and then "embed" it in VehicleWeapon:
import javax.persistence.Embeddable;
@Embeddable
public class Vehicle
{
...
}
Embedded here:
@Entity
public class VehicleWeapon
{
private long id;
private String name;
private Vehicle vehicle;
...
@Embedded
public Vehicle getVehicle()
{
return vehicle;
}
public void setVehicle(Vehicle vehicle)
{
this.vehicle = vehicle;
}
...
}
Would that help in your scenario?
Upvotes: 0