Hibernate - n+1 select queries with 1-to-1 - java

Organization is mapped to Address as 1-to-1:
Organization:
<one-to-one class="Address" constrained="true" name="address" property-ref="organizationId"/>
Address:
<many-to-one class="Organization" name="organization">
<column name="OrganizationID" not-null="false" unique="true"/>
</many-to-one>
this query generates addtitional select for every Organization + 1:
query = session.createQuery("select o from Organization as o where o.isCool=0").setReadOnly(true);
organizations = query.list();
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html tells to fetch="join" but this doesn't make any difference. How to solve this problem? Any help is appreciated.
EDIT
In debugger i can see that address is actually not lazy loaded, i have no idea why.

Since you are using an HQL to fetch your stuff, it would not help to simply use the annotation or the attribute that you are trying, to avoid the n+1 problem.
The right solution would be to make use of 'FETCH JOIN' clause in your query. You can follow the following link for more details:
http://www.realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects

Related

Using "SELECT FOR UPDATE OF" with Hibernate 4 & Postgres

I'm using Postgres 9.3.5 and recently updated the hibernate from 3.2 to 4.3.11.
As a result I can't run "SELECT... FOR UPDATE OF" queries,
and simply 'select.. for update' is not enough in my case since it returns
could not extract ResultSet. Reason: ERROR: FOR UPDATE cannot be applied to the nullable side of an outer join
The criteria I'm trying to use looks like this:
Criteria criteria = session.createCriteria(objectType).add(Restrictions.eq("name", objectName).ignoreCase());
I'm using the following locking:
in 3.2: criteria.setLockMode(LockMode.UPGRADE);
in 4.3.11: criteria.setLockMode(LockMode.PESSIMISTIC_WRITE);
I have an hierarchy of hibernate (& DB) objects which cause the hibernate perform several joins while constructing the above query.
the 'objectType' is a joined-subclass of the main class
<class name="BaseObject" table="BASE_OBJECTS">
While using hibernate 3.2 the final query (taken from Postgres logs) ended with: "for update of this_2_"
(when this_2_ is the alias given by hibernate to the main table (BaseObject) mapped in hbm.xml file)
After upgrading to 4.3.1.1 the same query returns the above mentioned exception.
which means the final query performed as for update (without the name of the table on which to perform the lock)
After an extensive look of the web I could find only that the "for update of" in hibernate with Postgres is not supported any more?
[https://hibernate.atlassian.net/browse/HHH-5654][2]
It seems very unlikely since it's quite an important sql feature and a big degradation in usage.
Am I missing something here?
02.09.15:
I'll try to clarify myself:
using an example given in the hibernate documentation
at
https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/inheritance.html
class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
If I want to perform something like:
select p from Payment p where id=1
Hibernate will perform an outer join (on the key) on all tables .
Adding a lock (.setLockMode(LockMode.PESSIMISTIC_WRITE)) will lock the lines on the four tables (as 'For update'),
instead of only on table "Payments" ('for update of p') - which did happen in hibernate 3.2
So what We have, is that Something which was supplied earlier by hibernate, is not working any more, using their own mapping examples?
Thanks in advance
Marina
The issue was fixed in Hibernate 5.
Tested in 5.2.8.Final.

Different results with query api vs. hql

I have the following entity in hbm.xml file
<class name="Base" table="base">
<id name="id"/>
<list name="ips" cascade="all-delete-orphan" lazy="false" fetch="join">
<cache usage="read-write" include="all" />
<key column="base_id" />
<list-index column="ip_order"/>
<element column="ip" type="string"/>
</list>
</class>
i have one entity Base with two ips string in the collection.
when i make:
session.createCriteria(base.class).list();
the result is two Base object
when i make:
session.createQuery(" from Base").list();
the result is one entity Base.
can someone tell me why i have this situation?
As per your mapping xml Base is one table and ips(IP) is another table.
One Base having two List(ips) means Base table will have one entry in DB(base table).
IP will have two entries in DB (ip table).
Obvisully Base table will have only one entry.
Check this example
I bet there are 2 records in the table for ips.
As you have declare ips being eager fetched, so it will also join fetch the ips when you are creating the criteria to fetch Base.class, causing the "result set" contains 2 records. However, the two "records" are in fact same instance.
The way to solve is simple though, search for use of DISTINCT_ROOT_ENTITY result transformer.

LONG as primary key in Hibernate mapping to MySQL

I am trying to implement persistence of some Java objects via Hibernate mapping to a MySQL table. When I commit I get a message saying 'Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1'.
My hypothesis is that the problem is caused from having a long-field in my Java POJO that I want to use as my primary key in the MySQL table. Since I was not able to use datatype LONG as my primary key in MySQL table (ERROR 1170: BLOB/TEXT column 'id' used in key specification without a key length) I concluded from some googling and this post that BIGINT would be the suitable mapping for long. However it is not updating.
My test POJO Personis very simple. It has 3 fields: id (long), firstname (String), lastname (String) with setters and getters, etc.
I do the hibernate mapping in xml (person.hbm.xml) that essentially looks like (minus headings):
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="native"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
My actual java code snippet that is supposed to save or update the record is simple:
Transaction tr = session.beginTransaction();
Person person = new Person(1,"John","Doe");
session.saveOrUpdate(person);
tr.commit();
And here's that thing, this all works just fine if I change the type of id to an int (Integer) in the Person object and in the MySQL table. However, I do not have that option for the actual objects that I want to persist so the question is; what am I doing wrong or what should I do to get it to work? Thanks.
ADDING Stacktrace:
Hibernate: update hibernatetest set firstname=?, lastname=? where id=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:57)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3006)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2908)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3237)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:187)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1082)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:317)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.hibernate.test.TestMain.main(TestMain.java:38)
nested transactions not supported
UPDATE:
OK, I have finally worked it out. I changed the hibernate generator class from 'native' to 'assigned' and now it works as expected. So now the hibernate mapping looks like:
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="assigned"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
Must admit I did not know the meaning of that parameter (copied from somewhere) and had no idea it could cause this much headache. Found this explanation which was quite useful.
Apparently I do not have enough credentials to answer my own questions so I guess that it will remain open or if someone provides an empty answer, I will accept it. Thanks.
When you use the saveOrUpdate() method hibernate fires the insert query if the id of the object is null and update if it is any other value. I can see the code,
Person person = new Person(1,"John","Doe"); setting the id to 1 and calling the saveOrUpdate() method. I am assuming there are no entries for the id 1 and hence the error is thrown.
To make it work, you need to make the below changes.
Change the Type of id in person to Long from long(The wrapper class so that it can support null).
Write the constructor new Person("John","Doe"); and save that object.
It is not a good Idea to keep the <generator class="assigned"/> for the transactional data. Instead you should be sticking to the native as you were trying first.
I feel this is a cleaner way to solve your initial problem, even though you have found an alternate solution.

Hibernate Reference column in table to indicate how to unmarshall an attribute in a different column in the same table

I have an entity that I want to persist through Hibernate (3.2)
The EntityBean has a column that indicates how another value of the entity bean should be unmarshalled:
<class name="ServiceAttributeValue" table="service_attribute_value">
<cache usage="nonstrict-read-write"/>
<id name="id" column="id" type="int-long">
<generator class="native"/>
</id>
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
</class>
The "service_attribute" column indicates which of the columns for the "value" property to look at when it unmarshalls the value and, more importantly, exactly what Type the value should be, for example the class of the Enum if the enum_value is to be read, or the type of Bean if the the id_value is to be read.
The value property uses a custom CompositeUserType to do the unmarshalling and within this I wish to reference the service_attribute column (although not write to it), however when I try to do this I get the following error:
org.hibernate.MappingException: Repeated column in mapping for entity: com.precurse.apps.rank.model.service.ServiceAttributeValue column: service_attribute (should be mapped with insert="false" update="false")
However within the definition of the composite property these xml attributes are not defined (only within a normal property).
Does anyone know of a way of overcoming this, or if there is a better solution to this propblem.
If you need any more information please let me know,
Cheers
Simon
I had a similar problem and changing the case of one column solved the problem. Could give a try!
e.g., one column could be service_attribute other Service_Attribute.
You can try this. Instead of mapping both values as property on the same table, map one of the property using join to itself and keep the other property as the way it is. This case you will be able to access the same property in both places. Just remember to name the property as different name.
<join table="service_attribute_value">
<key column = "id" />
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
</join>
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
based on your description, it seems like what you want to do is creating different subclasses based on the service_attribute. Instead of trying to achieve repeated column mapping which is not allow in hibernate, you can take a look hibernate inheritance mapping.
I Think I found a solution albeit not a very elegant one.
in the
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
method of the CompositeUserType the "owner" argument passed to the method contains the id of the object who's service_attribute I want to access.
Annoyingly the actual serviceAttribute of the owner is not accessable or has not been set at this stage (I played around with the ordering of the elements in the hbm.xml config, in case this was an ordering thing, but unfortunatly still no joy), so I can't simply access it.
Anyway the id of the owner object is set, so I then used the session argument to run a HQL query based on the id to access the serviceAttribute which I then used to correctly unmarshall the value property.
The drawback of this solution is that it requires a HQL query as an overhead to the unmarshalling process, although its within the same session, its still not optimal.
If anyone has any ideas for a better solution I'd be very grateful.
Cheers

Hibernate many-to-many collection filtering

I have the following POJO with a Set inside:
class Word {
private Long id;
private String word;
private int type = WordListFactory.TYPE_DEFAULT;
private Set<Word> refs = new HashSet<Word>();
...
}
Here's the mapping XML:
<class name="kw.word.Word" table="word">
<id name="id" column="id" unsaved-value="null">
<generator class="native"/>
</id>
<property name="word"
unique="true"
not-null="true"/>
<property name="type"/>
<set name="refs"
table="word_key"
cascade="save-update">
<key column="word_id"/>
<many-to-many class="kw.word.Word"
column="word_ref_id"
fetch="join">
</many-to-many>
</set>
</class>
There are two tables: word and word_key. The latter links word-parents to word-children.
I'm trying to implement set items filtering when the data is fetched from DB. The resulting object set must contain only items with a specific type.
I tried various things:
Using filtering in mapping like (sorry for lack of brackets)
many-to-many class="kw.word.Word"
column="word_ref_id"
fetch="join">
filter name="word_type" condition="type=:type"
many-to-many
In the code that fetches data I enabled the filter and set the parameter. According to logs hibernate seems to ignore this particular filter as it there's no condition in resulting SQL query.
Using additional condition in Criteria
Word result = null;
session.beginTransaction();
Criteria crit = session.createCriteria(Word.class);
crit.add(Restrictions.like("word", key))
.createAlias("refs", "r")
.add(Restrictions.eq("r.type", getType()));//added alias and restriction for type
List list = crit.list();
if(!list.isEmpty())
result = list.get(0);
session.getTransaction().commit();
now the resulting SQL seems to be OK
select
this_.id as id0_1_,
this_.word as word0_1_,
this_.type as type0_1_,
refs3_.word_id as word1_,
r1_.id as word2_,
r1_.id as id0_0_,
r1_.word as word0_0_,
r1_.type as type0_0_
from
word this_
inner join
word_key refs3_
on this_.id=refs3_.word_id
inner join
word r1_
on refs3_.word_ref_id=r1_.id
where
this_.word like ?
and r1_.type=?
but right after this query there's another one that fetches all the items
select
refs0_.word_id as word1_1_,
refs0_.word_ref_id as word2_1_,
word1_.id as id0_0_,
word1_.word as word0_0_,
word1_.type as type0_0_
from
word_key refs0_
left outer join
word word1_
on refs0_.word_ref_id=word1_.id
where
refs0_.word_id=?
Maybe I'm doing something wrong?
From your given code snippet few points:
In case of many-to-many relationship you require 3 table , two entity tables and one join table. But as you are having same entity -Word , i think the given table structure and mappings seems fine.
Try to use HQL and specify 'LEFT JOIN FETCH' to specify which associations you need to be retrieved in the initial sql SELECT.
See this link related to many-to-many relationship,but they used criteria query.
Querying ManyToMany relationship with Hibernate Criteria

Categories

Resources