Suppose, we have 3 entity
<class name="AEntity" table="A" abstract="true">
<id name="id">
<column name="ID"/>
</id>
<joined-subclass table="Aa" name="AaEntity">
<key column="key"/>
<many-to-one name="b" class="BEntity">
<column name="B_ID"/>
</many-to-one>
</joined-subclass>
<joined-subclass table="Ab" name="AbEntity">
<key column="key"/>
<many-to-one name="b" class="BEntity">
<column name="B_ID"/>
</many-to-one>
</joined-subclass>
</class>
<hibernate-mapping>
<class name="Parent" table="parent">
<id name="id">
<column name="ID"/>
</id>
<many-to-one name="a" class="AEntity">
<column name="a_ID" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="BEntity" table="B">
<id name="id">
<column name="ID"/>
</id>
</class>
</hibernate-mapping>
Then, we make criteria request
Criteria criteria = session.createCriteria(Parent.class);
criteria.createAlias("a","a");
criteria.createAlias("a.b","b");
criteria.list();
And get next sql
select this_.ID as ID1_5_3_,
this_.a_ID as a2_5_3_,
a1_.ID as ID1_2_0_,
a1_.B_ID as B2_2_0_,
a1_1_.B_ID as B2_0_0_,
a1_2_.B_ID as B2_1_0_,
case when a1_1_.key is not null then 1 when a1_2_.key is not null then 2 when a1_.ID is not null then 0 end as clazz_0_,
b2_.ID as ID1_3_1_,
b2_.C_ID as C2_3_1_,
b2_.ID as ID1_3_2_,
b2_.C_ID as C2_3_2_
from parent this_
inner join A a1_ on this_.a_ID=a1_.ID
left outer join Aa a1_1_ on a1_.ID=a1_1_.key
left outer join Ab a1_2_ on a1_.ID=a1_2_.key
inner join B b2_ on a1_1_.B_ID=b2_.ID
inner join B b2_ on a1_2_.B_ID=b2_.ID
We can see that B table joins two times and have same name b2_. Is it bug? And how make two joins but with different name of B table.
Okay?
Try aliases like
Criteria criteria = session.createCriteria(Parent.class, "parent");
criteria.createAlias("parent.a","a");
criteria.createAlias("parent.a.AaEntity.b","aab");
criteria.createAlias("parent.a.AbEntity.b","abb");
criteria.list();
PS: I have not tested this and it may not work as it is, but this will give you an idea on how to force the alias names.
Related
I have 3 tables represented by the following diagram:
I am trying to build a REST API that when /template is accessed by GET method,
the returned JSON will contain a templateDefinition object, that also contains a template type object. To that end I have created the following mapping files:
Template.hbm.xml
<hibernate-mapping package="com.reporttemplateengine.models">
<class name="Template" table="TEMPLATE">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" column="NAME" type="string" />
<one-to-one name="templateDefinition" class="com.reporttemplateengine.models.TemplateDefinition" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
TemplateType.hbm.xml
<hibernate-mapping package="com.reporttemplateengine.models">
<class name="TemplateType" table="TEMPLATETYPE">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" column="NAME" type="string" />
</class>
</hibernate-mapping>
TemplateDefinition.hbm.xml
<hibernate-mapping package="com.reporttemplateengine.models">
<class name="TemplateDefinition" table="TEMPLATEDEFINITION">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="identity" />
</id>
<property name="templateFile" column="TEMPLATE_FILE" type="blob" />
<property name="version" column="VERSION" type="int" />
<property name="active" column="ACTIVE" type="int" />
<many-to-one name="template" class="com.reporttemplateengine.models.Template"
column="template_id" unique="true" not-null="true" cascade="all" />
<many-to-one name="templateType" class="com.reporttemplateengine.models.TemplateType"
column="template_type_id" unique="true" not-null="true" cascade="all" />
</class>
</hibernate-mapping>
When I call the API, Hibernate logs this query:
select
this_.ID as ID1_0_1_,
this_.NAME as NAME2_0_1_,
templatede2_.ID as ID1_1_0_,
templatede2_.TEMPLATE_FILE as TEMPLATE_FILE2_1_0_,
templatede2_.VERSION as VERSION3_1_0_,
templatede2_.ACTIVE as ACTIVE4_1_0_,
templatede2_.TEMPLATE_ID as TEMPLATE_ID5_1_0_,
templatede2_.TEMPLATE_TYPE_ID as TEMPLATE_TYPE_ID6_1_0_
from
SYSTEM.TEMPLATE this_
left outer join
SYSTEM.TEMPLATEDEFINITION templatede2_
on this_.ID=templatede2_.ID
It should actually look like this:
select
this_.ID as ID1_0_1_,
this_.NAME as NAME2_0_1_,
templatede2_.ID as ID1_1_0_,
templatede2_.TEMPLATE_FILE as TEMPLATE_FILE2_1_0_,
templatede2_.VERSION as VERSION3_1_0_,
templatede2_.ACTIVE as ACTIVE4_1_0_,
templatede2_.TEMPLATE_ID as TEMPLATE_ID5_1_0_,
templatede2_.TEMPLATE_TYPE_ID as TEMPLATE_TYPE_ID6_1_0_
from
SYSTEM.TEMPLATE this_
inner join
SYSTEM.TEMPLATEDEFINITION templatede2_
on this_.ID=templatede2_.TEMPLATE_ID
This is also missing the template type object nested in templateDefinition. I do not know how to achieve this.
Let's say I have this named HQL (findRoomQuery):
select r from House h inner join h.roomList r
where h.address = :address and r.roomNo = :roomNo
The mapping of House entity is like this:
<class name="com.example.House" table="house">
<id name="id" column="id" type="long">
<generator class="assigned" />
</id>
<property name="address" column="address" type="string" length="100" not-null="false"/>
<set name="roomList" cascade="none" lazy="false" fetch="join" inverse="true">
<key>
<column name="house_id"/>
</key>
<one-to-many class="com.example.Room"/>
</set>
</class>
While the Room entity is like this:
<class name="com.example.Room" table="room">
<id name="id" column="id" type="long">
<generator class="assigned" />
</id>
<property name="houseId" column="house_id" type="long" not-null="true"/>
<property name="roomNo" column="room_no" type="string" length="4" not-null="false"/>
</class>
The relationship is a House can has one or many Room.
The code to execute the query is like this:
Query query = getSession().createQuery("findRoomQuery")
.setParameter("address", address)
.setParameter("roomNo", roomNo);
return query.list();
You can see the HQL return Room entities from select r (r is alias of h.roomList).
How to do the same thing with Hibernate Criteria Query?
Is it possible?
Try this one
Criteria criteria = session.createCriteria(House.class, "house");
criteria.createAlias("house.roomList", "roomList");
criteria.add(Restrictions.eq("house.address",address));
criteria.add(Restrictions.eq("roomList.roomNo", roomNo));
criteria.setProjection(Projections.property("roomList"));
Room r = (Room) criteria.uniqueResult();
replace
<property name="houseId" column="house_id" type="long" not-null="true"/>
to
<many-to-one name="houseId" class="com.example.House" fetch="select">
<column name="house_id" not-null="true" />
</many-to-one>
I am a new bie to the world of hibernate could you please advise , I was going through one to one relationship in Hibernate,
As per my analysis , one to one hibernate relationships can be established by three ways..
1)Through Join concept
2)Same primary key in both the tables
3)Primary key and foriegn key relationship in both the tables
Please advise , the above three ways to achieve the one to one mapping is correct or I am missing something then please advise, and also please advise that the below hbm mapping files that I am using are correct one.if not then please advise.
1) Through Join concept :-
One way to achieve to one to one relationship is by joining concept , following xml is being used for that
<hibernate-mapping>
<class name="mypack.Person" table="person21">
<id name="personId" type="int">
<generator class="increment">
</generator>
</id>
<property name="name"/>
<join table="personAddress">
<key column="personId"/>
<many-to-one name="address" class="mypack.Address" column="addressId" unique="true" cascade="all"/>
</join>
</class>
<class name="mypack.Address" table="address21">
<id name="id" column="addressId" type="int">
<generator class="increment"/>
</id>
<property name="city"/>
<property name="state"/>
</class>
</hibernate-mapping>
2) Same primary key in both the tables :-
Using same primary key in both the tables , following hbm is being used for that
<class name="mypack.Address" table="address31">
<id name="id" column="addressId" type="int">
<generator class="increment"/>
</id>
<property name="city"/>
<property name="state"/>
</class>
<class name="mypack.Person" table="person31">
<id name="personId" type="int">
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<property name="name"/>
<one-to-one name="address" class="mypack.Address"/>
</class>
</hibernate-mapping>
3)Primary key and foriegn key relationship in both the tables :-
Primary key in one table and foriegn key in another table. below is the hbm for this
<hibernate-mapping>
<class name="mypack.Person">
<id name="personId" type="int">
<generator class="increment"/>
</id>
<property name="name"/>
<many-to-one name="address" class="mypack.Address" column="addressId" unique="true" cascade="all"/>
</class>
<class name="mypack.Address">
<id name="id" column="addressId" type="int">
<generator class="increment"/>
</id>
<property name="city"/>
<property name="state"/>
</class>
</hibernate-mapping>
folks please advise.
I really think that join table is overkill here for a one-to-one mapping. For the other 2 solutions, please take a look at the following Hibernate documentation, everything is explained : http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/mapping.html#mapping-declaration-onetoone.
For your second mapping (primary key association), both entities need to define a one-to-one mapping and one side of the relation also needs to specify the attribute constrained="true".
This is how I would write your second mapping :
<class name="mypack.Person" table="PERSONS">
<id name="id" type="int" column="PERSON_ID">
<generator class="increment"/>
</id>
<property name="name" type="string" column="NAME"/>
<one-to-one name="address" class="mypack.Address" cascade="all"/>
</class>
<class name="mypack.Address" table="ADDRESSES">
<id name="id" type="int" column="ADDRESS_ID">
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<property name="city" type="string" column="CITY"/>
<property name="state" type="string" column="STATE"/>
<one-to-one name="person" class="mypack.Person" constrained="true"/>
</class>
I'm using Java 1.6, Hibernate 3.1, MySQL 5.5, XML mapping. I have two tables, partner and partner_http_account. I'll just show you the relevant fields.
partner:
partnerid dec(22,0) [primary key]
partner_http_account:
partnerhttpacctid dec(22,0) [primary key]
partnerid dec(22,0) [foreign key]
Here's the XML mapping:
<class name="com.rc.model.partner.Partner" table="partner" mutable="true">
<id name="partnerId" type="int">
<column name="partnerid" scale="10" precision="0" not-null="true" unique="true" sql-type="int unsigned"/>
<generator class="com.rc.model.jdbc.sequence.MexpIdentifierGenerator">
<param name="sequence">seq_partnerid</param>
<param name="idDataType">int</param>
</generator>
</id>
...
<one-to-one name="partnerHTTPAccount" class="com.rc.model.partner.PartnerHTTPAccount" lazy="false"
foreign-key="partnerid" cascade="all"/>
</class>
<class name="com.rc.model.partner.PartnerHTTPAccount" table="partner_http_account">
<id name="partnerHttpAcctId" type="int">
<column name="partnerhttpacctid" scale="10" precision="0" not-null="true" unique="true" sql-type="int unsigned"/>
<generator class="com.rc.model.jdbc.sequence.MexpIdentifierGenerator">
<param name="sequence">seq_partnerhttpacctid</param>
<param name="idDataType">int</param>
</generator>
</id>
<many-to-one name="partner" class="com.rc.model.partner.Partner" column="partnerid"
foreign-key="partnerid" cascade="none"/>
</class>
Here is my Java code:
public List<Partner> getPartnersByDomainStartsWith(String domainStartsWith) {
Query hQuery = sessionManager.getSession().createQuery("from Partner p " +
"left outer join fetch p.partnerHTTPAccount " +
"where p.domain like :domainStartsWith||'%'");
hQuery.setString("domainStartsWith", domainStartsWith);
return hQuery.list();
}
And here is what Hibernate is generating:
select partner0_.partnerid as partnerid0_,
partnerhtt1_.partnerhttpacctid as partnerh1_1_,
...
partnerhtt1_.partnerid as partnerid104_1_,
...
from partner partner0_
left outer join partner_http_account partnerhtt1_ on partner0_.partnerid=partnerhtt1_.partnerhttpacctid
where partner0_.domain like concat(?, '%')
My problem is the generated sql query of these two tables is joining on the wrong field. It should join on partner0.partnerid=partnerhtt1.partnerid instead. Would really appreciate some insight. Thanks.
This is really well described in the Hibernate documentation:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>
Notice how property-ref is used, to indicate that the one-to-one is the inverse side of the association mapped by the address property.
I have a table Project with fields
ID
PROJECT_BASELINE_ATTRIBUTES_ID (FK for table PROJECT_BASELINE_ATTR)
This table has the following mapping
<hibernate-mapping package="com.initech.db.model">
<class name="com.initech.db.model.Project" table="PROJECT">
<id name="id" column="id" type="java.lang.Long">
<meta attribute="use-in-tostring">true</meta>
<generator class="sequence">
<param name="sequence">PROJECT_ID_SEQ</param>
</generator>
</id>
<many-to-one name="projectBaselineAttributes" column="PROJECT_BASELINE_ATTRIBUTES_ID" class="com.initech.db.model.ProjectBaselineAttributes" cascade="all" not-null="true">
<meta attribute="use-in-tostring">true</meta>
</many-to-one>^
</class>
</hibernate-mapping>
The respective Project.java class has the fields
private Long id;
private ProjectBaselineAttributes projectBaselineAttributes;
Furthermore, I have a table PROJECT_BASELINE_ATTR, containing the field
ID
The Hibernate mapping:
<hibernate-mapping package="com.initech.db.model">
<class name="com.initech.db.model.ProjectBaselineAttributes" table="PROJECT_BASELINE_ATTR">
<id name="id" column="id" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">PRO_BASE_ATTR_ID_SEQ</param>
</generator>
</id>
<set name="projects" table="PROJECT" inverse="true" lazy="true" fetch="select">
<key>
<column name="PROJECT_BASELINE_ATTRIBUTES_ID" precision="22" scale="0" not-null="true" />
</key>
<one-to-many class="com.initech.db.model.Project" />
</set>
</class>
</hibernate-mapping>
The respective ProjectBaselineAttributes.java class has the fields
private Long id;
private Set projects = new HashSet();
The current mapping is not ideal, as the relationship between the two tables is actually one-to-one, but in the ProjectBaselineAttributes I have a set of Projects, even though there is always one Projet for one ProjectBaseLineattributes. What should the mapping look like for ProjectBaselineAttributes so that I can get the associated Project "singularly", i.e. so that the class ProjectBaselineAttributes.java would look like this:
private Long id;
private Project project;
Here's the section of the Hibernate documentation which details how to map such a bidirectional one-to-one association using a foreign key.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
You can try the below mappings as told in this example.
In the parent bean,
<one-to-one name="one2oneSubA1" class="com.manu.hibernate.mappings.domain.ASub1" cascade="all"
property-ref="parent"/>
In the child bean,
<many-to-one name="parent"
class="com.manu.hibernate.mappings.domain.AMain" column="a_id"
unique="true" cascade="save-update" />