I found many times on stackoverflow this issue, but nothing from them gives me clear answer. For simplicity, there are only two tables film and language bound many to one relation. Everything done according Netbeans Hibernate DVD Store tutorial. Now, how to display in first page (index.xhtml) language. It looks like very straightforward. Simply add:
<h:column>
<f:facet name="header">
<h:outputText value="Language"/>
</f:facet>
<h:outputText value="#{item.languageByLanguageId.langName}"/>
</h:column>
(Comumn in table language name was renamed on langName)
But it issues still the same LazyInitializationException. I tried to obtain languageId and in this case I was successful. It means #{item.languageByLanguageId.langName} gives exception but #{item.languageByLanguageId.languageId} not. It is strange. So what happen, when I use explicit fetch according languageId if I can obtain its.
So I added in FilmController.java method for obtaining language:
public String getLanguageById(Integer langId) {
String language = helper.getLangById(langId);
return language;
}
And in FilmHelper.java (final version):
public Film getFilmById(int filmId) {
Film film = null;
try {
session = HibernateUtil.getSessionFactory().getCurrentSession();
org.hibernate.Transaction tx = session.beginTransaction();
Query q = session.createQuery("select count(film.filmId) from Film as film where film.filmId = :filmId");
q.setParameter("filmId", filmId);
Number count = (Number) q.uniqueResult();
if (count.intValue() > 0)
film = (Film) session.load(Film.class, filmId);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
}
return film;
}
And yes, it works, I can obtain language name to modify index.xhtml:
<h:outputText value="{filmController.getLanguageById(item.languageByLanguageId.languageId)}"/>
Than I tried to modify FilmActor.hbm.xml to add lazy="false" and use origin simple solution in index.xhtml ("#{item.languageByLanguageId.langName}"):
<many-to-one name="languageByOriginalLanguageId" class="dvdrental.Language" lazy="false" fetch="select">
<column name="original_language_id" />
</many-to-one>
Again it works properly. Even if I set lazy="proxy" or lazy="no proxy". But still I don't understand, how to use this default attribute lazy="true". If I try to keep whole document in one session (don't do commit, which causes end of session), there is another Exception issue. It looks like, that lazy="true" doesn't meet in any time proper result.
With setting lazy=true attribute you are allowing hibernate to delay association retrieving. So when you disable lazy=false then hibernate will immediate do its fetching method, just after parent instance is retrieved. Yours problem will be solved if you set fetch="join".
Join fetching: Hibernate retrieves the associated instance or
collection in the same SELECT, using an OUTER JOIN.
Your example;
<many-to-one name="languageByOriginalLanguageId" class="dvdrental.Language" fetch="join">
<column name="original_language_id" />
</many-to-one>
You can look at fetch and lazy as how and when, respectively. In your example lazy=false solved your problem but still two queries where done because your fetch method was select.
Hibernate Fetching Strategies
UPDATE
Once when object is lazy initialized, you have only properties of your entity and lazy initialized association (only id you have). Then that object is passed for further(transaction is committed already) use and you want to use one of lazy initialized association(in your case Language) and got exception. This is happening because you are accessing lazy object and your transaction is already commited, so hibernate wants to execute your second query without success (fetch="select"). This can be fixed by moving the code that reads association to just before the transaction is committed.
When your object is detached and your current session is closed then you must do
Hibernate.initialize(entity)
to assign your detached entity to another session.
Thank you for explanation. I tested your advice. It works and I suppose, that join should be quicker than two distinct selects (it depends on indexes, optimizer, etc.), but still when I try combination lazy="true" and fetch="join" it again fails:
<many-to-one name="languageByOriginalLanguageId" class="dvdrental.Language" lazy="true" fetch="join">
<column name="original_language_id" />
</many-to-one>
Even if exception is different, still no succes:
java.lang.ExceptionInInitializerError
at controller.HibernateUtil.<clinit>(HibernateUtil.java:30)
However, there are clearly explained three ways, how to avoid problems with default or explicit lazy="true".
Related
I have a simple scenario in many places in my webapp - when showing a list of objects, I don't want to query ALL the details (ie child objects), but when I show the user a single object for them to edit, I DO want to query the entire object. So I let Hibernate default to lazy-fetching these child objects for getting the list, and want to override that with fetch = JOIN at runtime. I tried 2 methods, both of which should work but dont!
Here is my mapping file :
<hibernate-mapping>
<class name="User" table="User">
<id name="objectId" type="java.lang.Integer">
<column name="Object_ID" />
<generator class="identity" />
</id>
<many-to-one name="address" class="Address" cascade="save-update" >
<column name="Address_ID" not-null="true"/>
</many-to-one>
... other User properties ...
</hibernate-mapping>
First, I tried querying the default lazy User object, and then using Hibernate.initialize() to load the lazy child object :
User user = session.get(User.class, (Serializable) id);
if ( !Hibernate.isInitialized(user.getAddress()) )
Hibernate.initialize(user.getAddress());
}
Hibernate recognized that the child Address object wasnt loaded but initialize() STILL didn't load the Address. Why?
Next I tried a fetch-profile, by adding this to the Hibernate mapping file :
<fetch-profile name="returnEntireUser">
<fetch entity="User" association="address" style="join"/>
</fetch-profile>
And then using the code:
User u1 = session.get(User.class, (Serializable) id);
session.enableFetchProfile("returnEntireUser");
User u2 = session.get(User.class, (Serializable) id);
And both u1 and u2 objects are the same - both without the Address object filled in. I know Hibernate recognizes the fetch profile, but still doesnt do anything.
Can anyone figure out WHY these methods dont work and what I can do to get them to work
OK, I feel a little bit stupid. Both of the above methods DO actually work, however you will NOT see the results in an Eclipse debugger session. In another section of code I used Criteria and the
setFetchMode("field",FetchMode.JOIN);
method and in this case the debugger object DOES contain all the data, so I expected to see the full object in the debugger using the above strategies, but it didn't happen.
Also for ohers to note you cannot run test cases in the code back to back on the same object because of Hibernate caching the object. The debugger might or might not show anything meaningful.
we have a big problem in our development team.
We are using Hibernate and we have some entities which are related in two transitive one-to-many relations. The main object is a Group which has a list of Property instances, and each Property containing a list of Values.
(The mappings are down ahead)
We have two main problems:
A) When making a HQL Query, Criteria Query or SQLQuery it doesn't matter the conditions applied in JOINs or WHERE clauses, Hibernate always retrieves for us all the underlying objects. For example, if I make a Criteria or SQL getting only the Group objects, Hibernate comes and (lazy or not) gets all the Property and Value instances too. We want to control this. We want to do left joins and get only the properties with no values inside (Hibernate removes these properties with no value)
B) When making the Query, for example, a SQL, it shows in the log the SQL code we want. Everything seems perfect. But after that it brings every instance in the list without applying conditions, getting them only by id, and we can assure this because with lazy="true" we see the "load many-to-one" queries in the log.
Is there something we can do in hibernate config, fetching mode/strategy, the mappings configuration or anywhere? I'm thinking on going on Result transformers now.
I would be grateful if someone coud give me a hint or tell me where to find a solution to this problem. We are confused about how to get this, but it must be a way.
Thanks in advance
Query:
Criteria lstCriterios = this.getSession().createCriteria(CardGroup.class, CARD_GROUP)
.add(Restrictions.eq(ID_CATEGORY, idCategory));
lstCriterios.createAlias("listProperty", "listProperty", CriteriaSpecification.LEFT_JOIN);
if (clusterId != null) {
lstCriterios.add(Restrictions.or(
Restrictions.isNull("listPropertyValue" + ".value"),
Restrictions.and(Restrictions.eq("listPropertyValue" + ".clusterId", clusterId),
Restrictions.eq("listPropertValue" + ".companyWarehouseId", idCompanyWarehouse))));
lstCriterios
.createAlias("listProperty" + "." + "listPropertyValue", "listPropertyValue",
CriteriaSpecification.LEFT_JOIN,
Restrictions.eq("listPropertyValue" + ".clusterId", clusterId));
} else {
lstCriterios.createAlias("listProperty" + ".listPropertyValue", "listPropertyValue",
CriteriaSpecification.LEFT_JOIN);
}
lstCriterios.add(Restrictions.eq(ID_CATEGORY, idCategory));
lstCriterios.add(Restrictions.eq("listProperty" + ".groupId", idGroup));
lstCriterios.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
/*
* Sorting
*/
lstCriterios.addOrder(Order.asc("order"));
lstCriterios.addOrder(Order.asc("listProperty" + ".order"));
lstCriterios.addOrder(Order.asc("listPropertyValue"+ ".clusterId")); // Agrupacion, podrĂa ser nulo
lstCriterios.addOrder(Order.asc("listPropertyValue"+ ".propertyId")); // Propiedad
lstCriterios.addOrder(Order.asc("listPropertyValue"+ ".id"));
return lstCriterios.list();
Group mapping:
<list name="listProperty"
table="FICHA_PROPIEDAD" schema="${db2.siglo.schema}"
inverse="false" cascade="all" >
<key column="ID_FICHA_GRUPO" not-null="false" />
<list-index column="ORDEN" base="1"/>
<one-to-many
class="com.company.aslo.appwebsiglo.model.card.property.property.CardProperty" />
</list>
Property mapping:
<bag name="listPropertyValue"
table="FICHA_PROPIEDAD_VALOR" schema="${db2.siglo.schema}"
inverse="false" cascade="all">
<key column="ID_FICHA_PROPIEDAD" not-null="false" />
<one-to-many
class="com.company.aslo.appwebsiglo.model.card.propertyvalue.propertyvalue.CardPropertyValue" />
</bag>
It seems like our model design was bad and we didn't realize that if the DB table FICHA_PROPIEDAD_VALOR has Composite Key we can't map only one of the attributes in the composite key, because it brings us unexpected results.
Because of this and the nested objects, we had also bad implementations of the hashCode() and equals() methods which Hibernate uses.
I had solved this previously with a ResultTransformer getting the rows from a SQLQuery, but we got the Hibernate solution after that refactoring and changing the design of our model.
As per Hibernate docs for one-to-many xml mapping tag there is an attribute called as not-found
http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/collections.html#collections-onetomany
The Doc says:
not-found (optional - defaults to exception): specifies how cached
identifiers that reference missing rows will be handled. ignore will
treat a missing row as a null association.
What is the use of this attribute? I tried to create a mapping between Product and Parts with Product having a set of Parts with below mapping details:
<set name="parts" cascade="all">
<key column="productSerialNumber" not-null="true" />
<one-to-many class="Part" not-found="ignore"/>
</set>
Then I wrote my Java code as:
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Product prod = (Product) session.get(Product.class, 1);
session.getTransaction().commit();
System.out.println(prod);
HibernateUtil.getSessionFactory().close();
}
I was expecting null for my set which has Parts as I configured in my mapping file as not-found="ignore". But I got the regular exception - org.hibernate.LazyInitializationException
Please help me in understanding what is the use of this attribute? What are cached identifiers here?
The not-found has nothing to do with lazy loading. It's used to handle incoherences in your database.
Suppose you know nothing about good database practices, and have an order_line table containing an order_id column, supposed to reference the order it belongs to. And suppose that since you know nothing about good practices, you don't have a foreign key constraint on this column.
Deleting an order will thus be possible, even if the order has order lines referencing it. When loading such an OrderLine with Hibernate, Hibernate will load the Order and fail with an exception because it's supposed to exist, but doesn't.
Using not-found=ignore makes Hibernate ignore the order_id in the OrderLine, and will thus initialize the order field to null.
In a well-designed database, this attribute should never be used.
I have a table called roles. Each role may belong to an organization. Roles that do not belong to an organization have a value of null. I want to find all the roles for a specific organization or where the organization is null within the table.
Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Role.class)
.add(Restrictions.or(
Restrictions.eq("organization",organization.getId()),
Restrictions.isNull("organization")));
The Mapping file has:
<many-to-one class="com.myname.models.Organization" name="organization">
<column name="organization_id" precision="22" scale="0" />
</many-to-one>
When the query runs, I get:
IllegalArgumentException occurred calling getter com.myname.models.Organization.id
I have found that if I modify the criteria to just query for nulls on organization everything works, however once I query for a value, I get the error.
How to I modify the query or mapping file to meet my goals?
Not sure whether you're still looking for the answer, but as I've encountered this thread during my search for a solution of the same problem, I though it might be helpful for future reference.
You'll need to construct your criteria as follows:
final Criterion lhs = Restrictions.eq("organization",organization.getId());
final Criterion rhs = Restrictions.isNull("organization");
Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Role.class).add(Restrictions.or(lhs, rhs));
IllegalArgumentException occurred calling getter com.myname.models.Organiation.id
This seems to suggest that you are using "Organiation" somewhere whhere presumably this should be "Organization".
I have two objects:
public class ParentObject {
// some basic bean info
}
public class ChildObject extends ParentObject {
// more bean info
}
Each of these tables corresponds to a differnet table in a database. I am using Hibernate to query the ChildObject, which will in turn populate the parent objects values.
I have defined my mapping file as so:
<hibernate-mapping>
<class name="ParentObject"
table="PARENT_OBJECT">
<id name="id"
column="parent"id">
<generator class="assigned"/>
</id>
<property name="beaninfo"/>
<!-- more properties -->
<joined-subclass name="ChildObject" table="CHILD_OBJECT">
<key column="CHILD_ID"/>
<!--properties again-->
</joined-subclass>
</class>
</hibernate-mapping>
I can use hibernate to query the two tables without issue.
I use
session.createQuery("from ChildObject as child ");
This is all basic hibernate stuff. However, the part which I am having issues with is that I need to apply locks to the all the tables in the query.
I can set the lock type for the child object by using the query.setLockType("child", LockMode.?). However, I cannot seem to find a way to place a lock on the parent table.
I am new to Hibernate, and am still working around a few mental roadblocks. The question is: how can I place a lock on the parent table?
I was wondering if there was a way around having to do this without undoing the Polymorphic structure that I have set up.
Why do you have to lock both tables? I'm asking because depending on what you're trying to do there may be alternative solutions to achieve what you want.
The way things are, Hibernate normally only locks the root table unless you're using some exotic database / dialect. So, chances are you're already locking your ParentObject table rather than ChildObject.
Update (based on comment):
Since you are using an exotic database :-) which doesn't support FOR UPDATE syntax, Hibernate is locking the "primary" tables as they are specified in query ("primary" in this case being table mapped for the entity listed in FROM clause, not the root of the hierarchy - e.g. ChildObject, not ParentObject). Since you want to lock both tables, I'd suggest you try one of the following:
Call session.lock() on entities after you've obtained them from the query. This should lock the root table of the hierarchy, however I'm not 100% sure on whether it'll work because technically you're trying to "upgrade" the lock that's already being held on a given entity.
Try to cheat by explicitly naming ParentObject table in your query and requesting lock mode for it:
String hql = "select c from ChildObject c, ParentObject p where c.id = p.id";
session.createQuery(hql)
.setLockMode("c", LockMode.READ)
.setLockMode("p", LockMode.READ).list();