I am using Hibernate. My database is as follows
A Category has many attributes
class category
contains
private Set <Attribute> AllAttributes= new HashSet <Attribute>();
class attribute
How do I retrieve all categories together with their attributes because I am trying 'from category' but it is not working
Category Mapping File:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Dec 16, 2010 8:37:02 AM by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
<class name="com.BiddingSystem.Models.Category" table="CATEGORY">
<id name="CategoryId" type="long">
<column name="CATEGORYID" />
<generator class="native" />
</id>
<property name="CategoryName" type="java.lang.String">
<column name="CATEGORYNAME" />
</property>
<many-to-one name="ParentCategory" class="com.BiddingSystem.Models.Category">
<column name="PARENT_CATEGORY_ID" />
</many-to-one>
<set name="SubCategory" lazy="false" cascade="all-delete-orphan" inverse="true">
<key>
<column name="PARENT_CATEGORY_ID" />
</key>
<one-to-many class="com.BiddingSystem.Models.Category" />
</set>
<set name="AllAttributes" table="ATTRIBUTE" inverse="false" lazy="true" cascade="all">
<key>
<column name="CATEGORYID" />
</key>
<one-to-many class="com.BiddingSystem.Models.Attribute" />
</set>
</class>
</hibernate-mapping>
Attribute Mapping File:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Dec 16, 2010 5:25:09 AM by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
<class name="com.BiddingSystem.Models.Attribute" table="ATTRIBUTE">
<id name="AttributeId" type="long">
<column name="ATTRIBUTEID" />
<generator class="native" />
</id>
<property name="AttributeName" type="java.lang.String">
<column name="ATTRIBUTENAME" />
</property>
<set name="Options" table="ATTRIBUTEOPTION" inverse="false" cascade="all">
<key>
<column name="ATTRIBUTEID" />
</key>
<one-to-many class="com.BiddingSystem.Models.AttributeOption" />
</set>
</class>
</hibernate-mapping>
You have mapped the association with lazy="true". This tells hibernate that by default, a category's attributes should only be loaded from the database when they are actually accessed, in contrast to lazy="false" which would instruct hibernate to load the attributes whenever it loads a category. However, the directive in the mapping file affects all queries.
In case you want it only for a particular query, check out
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/queryhql.html#queryhql-joins :
A "fetch" join allows associations or
collections of values to be
initialized along with their parent
objects using a single select. This is
particularly useful in the case of a
collection. It effectively overrides
the outer join and lazy declarations
of the mapping file for associations
and collections. See Section 20.1,
“Fetching strategies” for more
information.
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
A fetch join does not usually need to
assign an alias, because the
associated objects should not be used
in the where clause (or any other
clause). The associated objects are
also not returned directly in the
query results. Instead, they may be
accessed via the parent object. The
only reason you might need an alias is
if you are recursively join fetching a
further collection:
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens
The fetch construct cannot be used in
queries called using iterate() (though
scroll() can be used). Fetch should be
used together with setMaxResults() or
setFirstResult(), as these operations
are based on the result rows which
usually contain duplicates for eager
collection fetching, hence, the number
of rows is not what you would expect.
Fetch should also not be used together
with impromptu with condition. It is
possible to create a cartesian product
by join fetching more than one
collection in a query, so take care in
this case. Join fetching multiple
collection roles can produce
unexpected results for bag mappings,
so user discretion is advised when
formulating queries in this case.
Finally, note that full join fetch and
right join fetch are not meaningful.
Related
There is something in the inner hibernate's behavior I don't understand.
I have got a normal hibernate configuration of a class having an other one in reference.
My principal class below
<hibernate-mapping package="com.my.package">
<class name="MyClass" table="MY_TABLE">
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">SEQ_MY_TABLE</param>
</generator>
</id>
<!-- reference -->
<many-to-one class="MyReferenceClass" fetch="select" name="myReference">
<column name="ID" not-null="true"/>
</many-to-one>
<!-- some other properties ... -->
.
.
</class>
</hibernate-mapping>
And the reference class below
<hibernate-mapping package="com.my.package">
<class name="MyReferenceClass" table="MY_REFERENCE_CLASS">
<id name="id" type="long">
<column name="ID" precision="22" scale="0" />
<generator class="sequence" />
</id>
</class>
<!-- Some propeties -->
.
.
</hibernate-mapping>
I have in this table of reference some lines:
id , property1, property2
0 val1x val2x
1 val1y val2y
2 val1z val2z
.
.
My problem is that when I try to insert a new instance of MyClass having a reference to MyReferenceClass with id 1,2,..., I have no problem. But when I try to insert one with the id = 0, I have got the exception below:
org.hibernate.PropertyValueException: not-null property references a null or transient value: com.my.package.MyClass.myReference
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:290)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
I resolved the problematic by changing the generator class from sequence to assigned in the MyReferenceClass.hbm.xml :
<hibernate-mapping package="com.my.package">
<class name="MyReferenceClass" table="MY_REFERENCE_CLASS">
<id name="id" type="long">
<column name="ID" precision="22" scale="0" />
<generator class="assigned" />
</id>
</class>
<!-- Some propeties -->
.
.
</hibernate-mapping>
The problem is that I have no clue why it doesn't work only when my id = 0 and not every time.
Which mecanism do I not understand in this part of the Hibernate Framework ?
PS: I know that the sequence in my first file is false as we have no sequence here, that's actually an old legacy code I'm making evolve, but I don't understand why it worked before.
It looks like the unsaved-value attribute of the id mapping for the MyReferenceClass is defaulting to 0, and cascading updates aren't enabled for MyClass.
When you add an object with an id other than the unsaved-value Hibernate assumes you're referencing an existing row. When you add an object with an id matching the unsaved-value then Hibernate assumes the entity is transient and needs to be persisted in order to get a new value from the sequence. And because your collection isn't cascading, Hibernate assumes the references are to existing, non-transient entities.
Changing the generator to "assigned" means you assume full responsibility for populating the id and Hibernate won't have to worry about it.
To make this work with the sequence generator you could save the objects before you add them to the collection, or set up a cascade on the mapping for the collection.
I have three tables. Order, Location, Order_Location where Order_Location is table that holds many-to-many relation.
Order has List<Location>. Location has property called city. Using HQL (Hibernate 3.6 for Java), I want to get all the locations for a particular order, ordered by the city.
In hbm file, List<Location> is mapped using idbag. Though I got the HQL, the generated SQL query is having join to Location and Order_Location table twice which I feel is overhead.
What am I doing wrong here?
SELECT o.locationList FROM Order o
join o.locationList locList
where o.orderId = 1
order by locList.city desc
which translates to something like below
select
order4_.LOC_ID as order1_355_,
order4_.LOC_CODE as order2_355_,
order4_.CITY as order3_355_,
order4_.CITY_LONG_NAME as order4_355_
from
sche.order order0_
inner join
sche.order_location order1_
on order0_.ORDER_ID=order1_.ORDER_ID
inner join
sche.location order2_
on order1_.LOC_ID=order2_.LOC_ID
inner join
sche.order_location order3_
on order0_.ORDER_ID=order3_.ORDER_ID
inner join
sche.location order4_
on order3_.LOC_ID=order4_.LOC_ID
where
order0_.ORDER_ID=1
order by
order2_.city desc
========= EDIT
Order.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"
>
<hibernate-mapping>
<class name="collectionorderby.Order" table="ORDER">
<id name="orderId" type="string">
<column name="ORDER_ID" length="32" />
<generator class="uuid" />
</id>
<idbag name="locationList" lazy="false" table="ORDER_LOCATION" fetch="select">
<collection-id column="ORDER_LOCATION_ID" type="string">
<generator class="uuid" />
</collection-id>
<key>
<column name="ORDER_ID" length="32" not-null="true" />
</key>
<many-to-many column="LOC_ID" class="collectionorderby.Location"
fetch="join" />
</idbag>
</class>
</hibernate-mapping>
Location.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="collectionorderby.Location" table="LOCATION">
<id name="locId" type="string">
<column name="LOC_ID" length="50" />
</id>
<property name="locCode" type="string">
<column name="LOC_CODE" length="50" />
</property>
<property name="city" type="string">
<column name="CITY" length="50" />
</property>
<property name="cityLongName" type="string">
<column name="CITY_LONG_NAME" length="500" />
</property>
</class>
</hibernate-mapping>
==== EDIT
Noticed that when we provide order by, translated query gets the select from first instance of table where as the order by is done using the second instance of the table. This will not happen, I suppose, if we avoid the duplicate instance of these tables.
The following worked as expected. The alias locList was used in select instead of o.locationList
SELECT locList FROM Order o join o.locationList locList where o.orderId = 1 order by locList.city desc
I have a mapping file which sets lazy=false for some related entities. This makes sense for a lot of use cases but there are some exceptions. The problem is that I dont want to fetch the related associations at query time for these cases, which are very expensive time-wise.
Example of the mapping for the entity.
<class name="Category" table="category">
<id name="id" type="string">
<column length="50" name="id"/>
<generator class="uuid"/>
</id>
<property name="name" type="string">
<column length="100" name="name" not-null="true"/>
</property>
<set inverse="true" lazy="false" name="categorySourcesList">
<key>
<column length="50" name="categoryid" not-null="true"/>
</key>
<one-to-many class="CategorySource"/>
</set>
</class>
My question is, is there a way to override the lazy value which is set in the mapping file, either in the sql-query I custom write or enabling lazy load as one of the parameters in the DAO? or through some annotations?
Yes, you can override the annotated or xml mapped association fetching strategy.
Hibernate Documentation
Criteria criteria = session().createCriteria(MyClass.class);
criteria.add(Restrictions.eq("id", "1234"));
criteria.setFetchMode("children",FetchMode.EAGER);
return (MyClass) criteria.uniqueResult();
This will return you an instance of MyClass with its children eagerly fetched.
AFAIK you can't override EAGER loading with LAZY but only the other way round.
Thus, you'd need to define the association the be LAZY and override that in the queries using joins. There might be other ways to do that but I'm afraid that's all I know right now.
I have the following pretty simple many-to-one relationship between Person and their parents/children (which also are instances of Person again).
Person.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 04.05.2011 15:02:31 by Hibernate Tools 3.3.0.GA -->
<hibernate-mapping>
<class name="test.Person" table="PERSONS">
<id name="id" type="long" access="field">
<column name="PERSON_ID" />
<generator class="native" />
</id>
<bag name="children" table="PERSONS" lazy="false" inverse="true" cascade="all">
<key column="PERSON_ID" not-null="false"></key>
<one-to-many class="test.Person" />
</bag>
<many-to-one name="parent" column="PARENT_ID" not-null="false" />
</class>
</hibernate-mapping>
Now, the problem in my case is that I have instances of Person which do not have parents (e.g. orphans).
If I try to persist these objects, I get:
java.sql.SQLException: null, message
from server: "Column
'PARENT_ID' cannot be null"
If I set not-null="true" in the mapping file, I get:
org.hibernate.PropertyValueException:
not-null property references a null or
transient value: test.parent
What's the magic trick here?
Valmar, since you are getting a SQL Exception, probably your COLUMN 'PARENT_ID' have a not null constraint. Check your table DDL. What database are you using?
I have a mapping (only important parts):
<class name="xyz.Role" table="ROLE" lazy="true">
<id name="id" type="java.lang.Integer">
<column name="ROLE_ID"/>
<generator class="increment"/>
</id>
<set name="assignments" lazy="true" table="PERSON_ROLE" cascade="delete"
inverse="true">
<key column="ROLE_ID" />
<many-to-many class="xyz.Person" column="PERSON_ID" />
</set>
</class>
and
<class name="xyz.Person" table="PERSON" lazy="true">
<id name="Id" type="java.lang.Integer">
<column name="TPP_ID"/>
<generator class="increment"/>
</id>
<set name="roles" lazy="true" table="PERSON_ROLE" cascade="save-update">
<key column="PERSON_ID" />
<many-to-many class="xyz.Role" column="ROLE_ID" />
</set>
</class>
With this mapping, when I delete a role, very person with this role is also deleted. What I would like to achieve, is delete the association (row from PERSON_ROLE table) when I delete Role. Is there any way to achieve this?
Cascade works on the level of entities. Since Person_Role is not mapped as entity, cascade can not help you AFAIK.
You might use a database-level "on cascade delete" on the foreign key from Person_Role to Role.
Or you can - as sfussenegger points out - remove the association programatically. Note that since you mapped the association on both entities, every row in Person_Role will appear twice in your object model. In such cases, it is recommended to remove the relevant entries from both collections in order not to corrupt your object model. Hibernate however will only look at the end of the association that is not mapped with inverse="true" when persisting changes. That is, to delete from the association with your current mapping, you must delete from Person.roles, not Role.assignments:
for (Person p : role.assignments) {
person.roles.remove(role)
}
Or you might wish to replace the many-to-many mapping with an association entity, in which case you could simply use cascade. This would allow you to add more information to assignments more easily. For instance, if you had to express "Joe works 30% on QA and 70% as requirements engineer", you could simply add that field to the association.
Why not simply call role.getAssignments().clear() before deleting the role?
for (Person p : role.getAssignments()) {
person.getRoles.remove(role)
}
role.getAssignments.clear();
session.delete(role);
I'm not a big fan of cascade="delete". I get this weird gut feeling any time I think about a snippet of XML being able to delete entire tables of valuable data :)
Don't put a mapping on the Role's side at all. If you eventually need this information, get it with an HQL query. I.e. get rid of this:
<set name="assignments" lazy="true" table="PERSON_ROLE" cascade="delete"
inverse="true">
<key column="ROLE_ID" />
<many-to-many class="xyz.Person" column="PERSON_ID" />
</set>
And whenever you need the persons for a role (which I think should be rare) get it by:
SELECT p FROM Person p JOIN p.roles WHERE role=:role