I have one entity called ProductTemplate with the following hibernate mapping
<hibernate-mapping default-cascade="none">
<class name="com.stackoverflow.ProductTemplateImpl" table="PRODUCT_TEMPLATE" dynamic-insert="false" dynamic-update="false">
<composite-id name="productTemplatePk" class="com.stackoverflow.product.ProductTemplatePK">
<key-property name="templateType" type="java.lang.String">
<column name="TEMPLATE_ID" sql-type="VARCHAR2(255)" not-null="true"/>
</key-property>
<key-many-to-one name="product" class="com.stackoverflow.ProductImpl" >
<column name="PROD_ID"/>
</key-many-to-one>
</composite-id>
</class>
</hibernate-mapping>
where ProductTemplatePK is a normal java Primary key class.
and another entity called Product with the following hibernate mapping:
<hibernate-mapping default-cascade="none">
<class name="com.stackoverflow.ProductImpl" table="PRODUCT" dynamic-insert="false" dynamic-update="false">
<id name="id" type="java.lang.String" unsaved-value="null">
<column name="PROD_ID" sql-type="VARCHAR2(255)"/>
<generator class="assigned">
</generator>
</id>
<property name="state" type="java.lang.String">
<column name="PROD_STATE" not-null="true" unique="false"/>
</property>
<property name="nameEn" type="java.lang.String">
<column name="PROD_NAME_EN" not-null="true" unique="false"/>
</property>
</class>
</hibernate-mapping>
Now if I tried to retrieve all productTemplates based on the productId I can do it using the following hibernate criteria:
Criteria productTemplateCriteria = this.getSession().createCriteria(ProductTemplate.class);
productTemplateCriteria.add(Restrictions.in("productTemplatePk.product.id", "1"));
but I don't know how to retrieve those templates based on the product.nameEn as the following code:
Criteria productTemplateCriteria = this.getSession().createCriteria(ProductTemplate.class);
productTemplateCriteria.add(Restrictions.in("productTemplatePk.product.nameEn", "Ali"));
generates the following error:
Caused by: org.hibernate.QueryException: could not resolve property: productTemplatePk.product.nameEn of: com.stackoverflow.ProductTemplateImpl.
so how can I query an entity property that is mapped as part of a composite primary key ?
I manged to solve this by simple HQL query as I didn't find any way to do it using hibernate API
this is the HQL query I used:
from com.stackoverflow.ProductTemplateImpl productTemplate
where productTemplate.productTemplatePk.product.state not in (:productStates)
But I'm still interested to know if there is any Hibernate API combination that can do it.
I was not able to get deeper than 2 levels except for ids when specifying property names in criteria. Try using explicit join with alias:
Criteria crit = this.getSession().createCriteria(ProductTemplate.class);
crit.addAlias("productTemplatePk.product","p")
.add(Restrictions.in("p.nameEn", "Ali"));
Related
I'm facing a problem with class mapping in hibernate.
I'm trying to make a many-to-one relationship using a composite-id.
and the mapping is returning
Repeated column in mapping for entity:
br.com.is.isenterprise.cre.model.ClienteRegraIcmsMap column: EMPRESAID
(should be mapped with insert="false" update="false")
but if I set the many-to-one relationship to insert =" false " and update =" false I can not insert the id of regraIcmsSubistituta in the database.
follows the hbm files.
RegraIcms.hbm.xml
<hibernate-mapping package="br.com.is.isenterprise.efi.model">
<class name="RegraIcms" table="REGRAICMS">
<composite-id name="cid" class="br.com.is.isenterprise.efi.model.RegraIcmsId">
<key-property name="empCod" type="integer" column="EMPCOD"/>
<key-property name="idRegraIcms" type="integer" column="IDREGRAICMS"/>
</composite-id>
<property name="descricao" type="string" column="DESCRICAO" access="field"/>
<property name="aplicacao" type="integer" column="APLICACAO" access="field"/>
</class>
</hibernate-mapping>
ClienteRegraIcmsMapId.hbm.xml
<class name="ClienteRegraIcmsMap" table="CLIENTEREGRAICMSMAP">
<composite-id name="cid" class="br.com.is.isenterprise.cre.model.ClienteRegraIcmsMapId">
<key-many-to-one name="cliente" column="CLIENTEID" class="br.com.is.isenterprise.cre.model.Cliente" access="field" lazy="false"/>
<key-many-to-one name="regraIcms" class="br.com.is.isenterprise.efi.model.RegraIcms" access="field" lazy="false">
<column name="EMPRESAID"/>
<column name="REGRAICMSID"/>
</key-many-to-one>
</composite-id>
<version name="versao" type="integer" column="VERSAO"/>
<many-to-one name="regraIcmsSubstituta" class="br.com.is.isenterprise.efi.model.RegraIcms" access="field" lazy="false">
<column name="EMPRESAID"/>
<column name="REGRAICMSSUBSTITUTAID"/>
</many-to-one>
</class>
</hibernate-mapping>
You're trying to use the same column (EMPRESAID) for two different things, the primary key as well as the many-to-one relation.
That's not gonna work (except with insertable = "false", as you mentioned)
If it's possible use a separate column for EMPRESAID in one of the mappings.
I'm new to Hibernate and I'm trying to dynamically join more table. I have built an example with three tables: Employee, Address, Country. I want to retrieve all employees of a certain country. Here my config file:
<class entity-name="Employee">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<many-to-one name="address" column="address" unique="true"
class="Address" not-null="true"/>
</class>
<class entity-name="Address">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="street" column="street_name" type="string"/>
<property name="city" column="city_name" type="string"/>
<property name="state" column="state_name" type="string"/>
<property name="zipcode" column="zipcode" type="string"/>
<many-to-one name="country" column="country" unique="true"
class="Country" not-null="true"/>
</class>
<class entity-name="Country">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
<property name="code" column="code" type="string"/>
</class>
If I do
List employees = session.createCriteria("Employee")
.createCriteria("address")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
I get the right result back but my goal is not to manually specify the whole path between start table and filter table: I want that hibernate makes the job for me. I would like to write something like this:
List employees = session.createCriteria("Employee")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
but I get the error message
org.hibernate.QueryException: could not resolve property: country of: Employee
Hibernate is just a wrapper or abstraction layer to hide away SQL expressions.
In your working example Hibernate builds a SQL select statement creating a double JOIN and returning the desired output. You are feeding Hibernate with the "knowledge" of how to filter the associated table. The same as you would write your own SQL statement.
But in you second example you expect Hibernate to guess a second level association from the first table. Hibernate simply looks up if table Employee has a column/foreign key country and it fails to find it. So the error is: could not resolve property: country of: Employee
You can not expect from Hibernate to do this kind of magic as it would produce or sorts of false queries.
Just to illustrate on your example.
In case your Employee would have two fields of type Address:
<class entity-name="Employee">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<many-to-one name="bussiness_address" column="bussiness_address" unique="true"
class="Address" not-null="true"/>
<many-to-one name="home_address" column="home_address" unique="true"
class="Address" not-null="true"/>
</class>
Both would have a Country linked ... what should in case of this query hibernate do?
List employees = session.createCriteria("Employee")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
Filter country by the home or the business address or both?
Or just imagine a case where you do filtering by an id field present in every table.
So to wrap things up: Hibernate can't do magic it is only here to hide away raw SQL.
If I translate your query to Java language, what you are trying to do is getting value of coutry directly out of employee. In Java you would get a compile error though. Such thing automatically does not work neither in Java, nor in Hibernate or plain SQL. There is no direct link between employee and country, but there is a sequence of links employee -> address -> country.
To fix this in Java way, one could maitain a secondary reference to country directly from employee. Then calling employee.getAddress().getCountry() and employee.getCountry() would be equal.
To achieve this in hibernate, you would need to add new field country to Employee and map it as a non-modifiable many-to-many relationship with entity Country, using entity Address to map the relationship. This would only reduce 2 hibernate joins to one, it could not be applied recurrently to more than 2 joins.
If this solution is OK for you, I can provide you with example syntax.
Trying to insert a new Entity using hibernate and it is throwing me this exception:
a different object with the same identifier value was already associated with the session
I understand that this error is coming because hibernate finds a similar object in the memory. But I am creating a new object every time before inserting. Does it have anything to do with sequence?
hbm
<class name="MyObject" table="My_Object">
<id column="object_id" name="id" type="long">
<generator class="sequence">
<param name="sequence">OBJ_SEQ</param>
</generator>
</id>
<property name="column1" column="column_1" type="string" not-null="true"/>
<property name="column2" column="column_2" type="string" not-null="true"/>
<property name="column3" column="column_3" not-null="true" type="string"/>
</class>
The problem is when you are trying to persist an entity, a entity with the same id is already present in the database. Reason for this can be, you are manually assigning a value to the id, or you have reset the OBJ_SEQ in database.
I have a Node which is associated with NodeDatas. These NodeDatas have revisions, and Node has a column latestRevision which tells it which is the last revision.
I'm trying to figure out the mapping I need to get the set below filtered by the latestRevision in Node.
<class name="Node" table="RatNodes">
<id name="id">
<generator class="native"/>
</id>
<set fetch="join" name="nodeDatas" table="RatNodeData" inverse="true" access="field" cascade="all-delete-orphan" order-by="dataOrder asc">
<key column="nodeID" on-delete="cascade"/>
<one-to-many class="NodeData"/>
<filter name="revisionFilter" condition="revision = latestRevision"/>
</set>
<property name="questionNumber" access="field"/>
<property name="type" access="field"/>
</class>
<class name="NodeData" table="RatNodeData">
<composite-id>
<key-property name="nodeID" access="field"/>
<key-property name="key" access="field" column="dataKey"/>
<key-property name="order" access="field" column="dataOrder"/>
<key-property name="revision" access="field"/>
</composite-id>
<property name="value" access="field" type="EncodedStringUserType" column="dataValue"/>
</class>
The result of this mapping when I enable the filter is an error:
Caused by: com.jnetdirect.jsql.JSQLException: Invalid column name 'latestRevision'.
The query it spits out is:
select nodedatas0_.nodeID as nodeID1_, nodedatas0_.dataKey as dataKey1_, nodedatas0_.dataOrder as dataOrder1_, nodedatas0_.revision as revision1_, nodedatas0_.nodeID as nodeID46_0_, nodedatas0_.dataKey as dataKey46_0_, nodedatas0_.dataOrder as dataOrder46_0_, nodedatas0_.revision as revision46_0_, nodedatas0_.dataValue as dataValue46_0_ from RatNodeData nodedatas0_ where nodedatas0_.revision = nodedatas0_.latestRevision and nodedatas0_.nodeID=? order by nodedatas0_.dataOrder asc
I can tell this is wrong as it's looking for latestRevision on NodeData but I'm not sure how to tell it to look on Node instead. Is this possible in hibernate? Seems like a pretty simple query if I were just using SQL.
Ended up doing this instead of trying to do it in the mapping file:
List datas = session.createCriteria(NodeData.class)
.add(Restrictions.eq("nodeID", this.id))
.add(Restrictions.eq("revision", this.latestRevision))
.list();
I'm not sure if this is the right way to do this or not. Would love to hear about a mapping file solution too.
You have not defined any filter or 'latestRevision' as filter-param. So it will not come to know that what actually the entity means.
Try using following.
For further help have a look at the link.
I'm terribly new to Hibernate. I've googled for two hours but I still can't figure out, how to make JOIN without using HQL, only by criteria. I have tables Clients(cID, name) and Visits(vID, vcID, date). The relation is one to many (one client can visit multiple times). I would also like to do it without setFetchMode. Just Criteria. Do I have to change the mappping xml?
UPDATE:
this is part of my mapping xml:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
</class>
<class name="Visit" table="Visits">
<id name="vID" column="vID"><generator class="native"/></id>
<property name="vcID" length="10" not-null="true"/>
<property name="date" length="25" not-null="true"/>
</class>
Having a class Client with a list-attribute "visits" that's mapping to your Visit-Entity:
Criteria criteria = session.createCriteria(Client.class);
criteria.addCriteria("visits");
This would create an inner join between your client-table and your visits-table.
Update:
Here you'll find some good examples: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html#querycriteria-associations
Mapping Example
I hardly ever use hibernate mapping xml, however it should read similiar to:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
<bag name="visits">
<key column="vcId"/>
<one-to-many class="Visit"/>
</bag>
</class>
Tell Hibernate that there is a property "visits" which represents a one-to-many relationship.
You need to update you mapping:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
<!-- Declare Set<Visit> visits in the Client class-->
<set name="visits" lazy="false" cascade="all">
<key column="vcID"/>
<one-to-many class="your.package.Visit"/>
</set>
</class>
<class name="Visit" table="Visits">
<id name="vID" column="vID"><generator class="native"/></id>
<!-- and add "Client client" property to your Visit class -->
<many-to-one name="client" column="vcID" lazy="false"/>
<property name="date" length="25" not-null="true"/>
</class>
Then:
Criteria criteria = session.createCriteria(Visit.class).addCriteria("client")
.add(Restriction.eq(...));
or
Criteria criteria = session.createCriteria(Client.class).addCriteria("visits")
.add(Restriction.eq(...));
And Hibernate will join them automatically.