I'm doing a search on one of my tables (legacy database) and recieving a horrible time here. The query is build by criteria api of hibernate, e.g.:
Criteria crit = getSessionFactory().getCurrentSession().createCriteria(P1.class);
crit.add(Restrictions.sqlRestriction("{alias}.compno like ?", "%" + s + "%", new StringType()));
crit.setMaxResults(25);
crit.setFirstResult(0);
crit.addOrder(Order.asc("compno"));
crit.list();
As you can see I'm already doing a paging here to improve the perfomance. This criteria needs ~6 seconds on average.
Well the native query which looks like this
select * from SCHEM.P1 where compno like '%100%' order by compno fetch first 25 rows only
takes only 10 ms which is a huge difference imo. Why does the criteria runs so slow? Need I switch back to the native sql query?
Good point on the comments:
Yes there are some relationships which I didn't had on the scope:
<set name="pI" table="P12" lazy="false">
<key column="awcompno" update="false" />
<one-to-many class="org.gee.hibernate.P12" not-found="ignore"/>
</set>
<one-to-one name="info" class="org.gee.hibernate.P13" />
<set name="ma" table="P03" lazy="true" schema="SCHEMP" mutable="false" >
<key column="macountry" property-ref="land" update="false" />
<one-to-many class="org.gee.hibernate.P03" not-found="ignore" />
</set>
<set name="users" table="P15" lazy="true">
<key column="apcompno" update="false" />
<one-to-many class="org.gee.hibernate.P15" not-found="ignore"/>
</set>
My tip is:
<set name="pI" table="P12" lazy="false">
<key column="awcompno" update="false" />
<one-to-many class="org.gee.hibernate.P12" not-found="ignore"/>
</set>
This collection is not lazy. That may be your bottleneck.
Do you need all informations? You may read fields of your entity with hibernate, if you only want to read the IDs.
IBM pureQuery has some really nice facilities for accelerating Hibernate applications that work with DB2. The other benefit ... it makes it much easier to debug as it allows you to correlate your SQL and your Java code.
Take a look at this article http://www.ibm.com/developerworks/data/library/techarticle/dm-1008hibernateibatispurequery1/index.html
I'd say have a look at the DB logs to check what are the exact SQL instructions that get executed. Hibernate maybe loading more than just the native query you would expect, as it may load eager collections etc.
So - enable Hibernate query logging, or better yet, check the DB logs to see what gets executed.
Related
I am migrating a legacy hibernate project from version 4.3 (with Java 11) to 5.6 (with Java 16). The HBM files below map an object graph of Jurisdiction -> Unit -> UnitAux. Units are lazy-loaded, and UnitAux is one-to-one with Unit. Under version 4.3, when initiazing Units, it would take about 100ms to load. Under version 5.6, it now takes 600-800ms.
These are the abbreviated HBM files for the 3 entities:
Jurisdiction.hbm.xml
<hibernate-mapping>
<class name="com.edc.c2c.core.model.impl.Jurisdiction" table="Jurisdiction" schema="domain" dynamic-update="true">
<set name="units"
inverse="true" cascade="all" lazy="true" fetch="select"
optimistic-lock="false" batch-size="1000" where="recordStatus = 'A'">
<key>
<column name="jurisdictionId"/>
</key>
<one-to-many class="com.edc.c2c.core.model.impl.Unit"/>
</set>
</class>
</hibernate-mapping>
Unit.hbm.xml
<hibernate-mapping>
<class name="com.edc.c2c.core.model.impl.Unit" table="Unit" schema="domain" dynamic-update="false">
<composite-id>
<key-property name="id" column="id" type="long"/>
<key-property name="owningJurisdictionId" column="jurisdictionId" type="long"/>
</composite-id>
<one-to-one name="unitAux" class="com.edc.c2c.core.model.impl.UnitAux" cascade="all" fetch="join" property-ref="unit"/>
</class>
</hibernate-mapping>
UnitAux.hbm.xml
<hibernate-mapping>
<class name="com.edc.c2c.core.model.impl.UnitAux" table="UnitAux" schema="domain" dynamic-update="true">
<composite-id>
<key-property name="id" column="id" type="long"/>
<key-property name="jurisdictionId" column="jurisdictionId" type="long"/>
</composite-id>
<many-to-one name="unit" class="com.edc.c2c.core.model.impl.Unit" unique="true" not-null="true"
cascade="all" insert="false" update="false">
<column name="id"/>
<column name="jurisdictionId"/>
</many-to-one>
</class>
</hibernate-mapping>
If I comment out the one-to-one in Unit.hbm.xml, the unit(s) Set loads fast, as expected.
In the UnitAux.hbm.xml, I replaced the many-to-one with a bag containing a one-to-many, something like this:
<bag name="unitGroup" inverse="true" cascade="all" lazy="true" fetch="select">
<key>
<column name="id"/>
<column name="jurisdictionId"/>
</key>
<one-to-many class="com.edc.c2c.core.model.impl.unit"/>
</bag>
With this, the UnitAux class had a List property called unitGroup. With the bag, the unit(s) load time dropped to 300ms.
I'm at a loss as to how to get the hibernate 5.6 to perform at the same load times as 4.3.
Any ideas or suggestions would be greatly appreciated.
Update: I forgot to mention, both versions effectively produce the same SQL. Something about how the objects themselves are initialized must be causing the slow down.
Update 2: The session statistics between 4.3 and 5.6 were very similar; not enough to explain the performance difference. My investigation has shown that the delays appear to be centered around initializing the entities. Specifically, the call to
Loader.initializeEntitiesAndCollections( final List hydratedObjects, final Object resultSetId, final SharedSessionContractImplementor session, final boolean readOnly, List<AfterLoadAction> afterLoadActions)
The time spent here is where the latency lies. Every property in each entity is tested for bytecode enhancement. In my test, I'm loading 600+ Units, along with the 600+ UnitAux entities. Is than an alternate loader that doesn't do this?
Update 3: Changing the association for Unit -> UnitAux to unidirectional reduced the latency by roughly half. Now it's only 3x slower.
Update 4: This is very strange. After experimenting with a variety of things, I made the following discovery. If I enable logging at the INFO (or ERROR) level for hibernate (see below config), everything runs fast, at the expected timing:
<logger name="org.hibernate" additivity="false">
<level value="info"/>
<appender-ref ref="STDOUT"/>
</logger>
If logging isn't declared, it runs slow (meaning nothing is specifically configured for hibernate). Is this something peculiar with jboss logging? I'm using jboss-logging-3.4.2.Final.jar. Does it run slower if nothing is explicitly declared in the log4j.xml? It's like the classic problem of having debug statements that never get used, but Java has to build all of the string values, which leads to extreme latency.
Update 5: I just did a spot check of the source code for Hibernate Core 5.6.0-Final. 141 classes use log.trace, and there are 249 class that use log.debug. Most of the log.trace calls don't pre-check to see if TRACE is enabled. The log.debug calls are checked more frequently, but there are stil a ton that don't pre-check to see if DEBUG is enabled.
In the end, I found my own answer to this problem.
First, there was no problem at all with Hibernate 5.x and one-to-one bidirectional associations. The problem had everything to do with debug/trace logging. With Hibernate 5.x and bytecode enhancement, a lot of new logging was added. Not only is logging done for each row-wise entity, but also for each column/property (testing for column lazy loading?).
Second, my entity classes have special formatting for toString(). Invocations of these toString()s can take a lot of time to execute, and then multiply if over hundreds of entities.
Third, there must be some bug between jboss-logging and log4j1-12.x. Internally, jboss-logging#doLogf() checks for whether or not the logging level is enabled, however, it is still reaching the String formatter. Here is a snapshot from VisualVM with the application running in Tomcat; you can see that 700ms was added for debug formatting, even though it would never be printed out to the log file:
If the Hibernate code had debug/trace level checks before the logging calls, this would not happen.
In the end, with Tomcat, I removed all log4j and slf4j-log4j jar files. That solved the problem.
I'm working with Hibernate 3.6 version, with xml mapping files. In my case I have three mapped entities, which are Detector, Antenna and Location. Basically, having Detector->Set<Antenna> and Location->Set<Antenna> relations, I would like to have also Detector->Set<Location> available.
Each Detector has a Set of Antenna entities, mapped like that:
<set name="_Antennas" table="tantenna" inverse="true" cascade="all">
<key>
<column name="id_detector" not-null="true" />
</key>
<one-to-many class="Antenna" />
</set>
Also each Antenna belongs to a specific Location and to a specific Detector. That's the many-to-one mapping to refer that:
<many-to-one name="_Detector" class="com.tadic.model.detector.Detector"
column="id_detector" />
<many-to-one name="_Location" class="com.tadic.model.location.Location"
column="id_location" />
In the same way, Location has a Set of its Antennas:
<set name="_Antennas" table="tantenna">
<key>
<column name="id_location" />
</key>
<one-to-many class="com.tadic.model.detector.Antenna" />
</set>
So Detector knows about its Antennas, Antennas know about their Detector and Location. Location entity has a set of its Antennas, but tlocation table has no foreign-keys.
However, I'm interested in knowing all the Locations of a Detector in a specific point. I know I can do it writing an HQL, but I would like to know if this is possible when Detector loads, just mapping it as a Set of Location entities.
Remember tlocation table has no iddetector column to link it with, also I think there's no need for it.
If I got it right from a database point
tdetector [1]--[id_detector]-->[n] tantenna
tlocation [1]--[id_location]-->[m] tantenna
Meaning tantenna has a column tuple of (id_detector, id_location) and is essentially a link table between tdetector and tlocation. This could be used to facilitate a many-to-many mapping between Detectors and Locations.
And here is the mapping fragment for the Detector hibernate mapping.
<set name="locations" table="tantenna">
<key column="id_detector" />
<many-to-many class="com.tadic.model.location.Location" column="id_location" />
</set>
One more thing. In my experience, having such a complex relations scheme mapped on the ORM does not come without cost. Even if hibernate finds your mapping files to be fine during the session factory initialization, I urge you to test thoroughly and, if necessary, specify some relations to be read-only (i.e. only useful when reading data) with insert="false" update="false".
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.
I have to create complex hibernate mapping. The following simplified example explains my problem.
I have two entities:
public class Work {
private WorkType type;
private Set<Workers>;
...
}
public class Worker {
private Map<WorkType,Work>;
...
}
I have 3 tables:
t_works columns: id, type,...
t_workers columns: id,...
t_work_worker columns: worker_id, work_id.
I want to map the map with hibernate, without copying the type values to t_work_worker.
The problem here is that the map key (WorkType) is a part of the map value (Work).
My hibernate hbm:
<typedef class="org.hibernate.type.EnumType" name="workType">
<param name="enumClass">myPackage.WorkType</param>
<param name="type">12</param>
</typedef>
<class name="work" table="T_WORKS">
<property name="type" type="workType" column="type"/>
<set name="workers" table="T_WORK_WORKER" inverse="true" lazy="false" cascade="none">
<key column="WORK_ID" />
<many-to-many column="WORKER_ID"class="myPackage.Worker"/>
</set>
</class>
<class name="Worker" table="T_WORKERS">
<map name="channels" table="T_WORK_WORKER" lazy="false" cascade="all">
<key column="WORKER_ID" />
<map-key formula="(select w.TYPE from t_works w where w.ID=WORK_ID)"type="workType"/>
<many-to-many column="WORK_ID" class="myPackage.Work"/>
</map>
</class>
This mapping works but requires additional select statement (see formula atribute).
I wonder whether there's a way to map the work type as a key, without using "formula" and without adding the type column to the relations table.
As you said, part of your problem is that you have a circular reference. This is usually not a very good idea, so you might want to think of an alternative structure. Do you really need the circular reference? Why does the worker need to have a map of worktype and work? I would probably make the connection between worker and work in another class, so you won't need the circular reference.
I have a many-to-many association defined like:
Parent.hbm.xml:
<set name="children" table="child_parent_map" lazy="true">
<cache usage="nonstrict-read-write" />
<key column="parent_id" />
<many-to-many class="Child">
<column name="child_id" not-null="true" index="child_parent_by_child"/>
</many-to-many>
</set>
Child.hbm.xml:
<set name="parents" table="child_parent_map" lazy="true">
<cache usage="nonstrict-read-write" />
<key column="child_id" />
<many-to-many column="parent_id" class="Parent" lazy="false"/>
</set>
I am quite sure I am initializing Parent.children by walking the collection. Something like:
for(Child child : parent.getChildren()) {
Hibernate.initialize(child.getAnotherProperty());
}
Parent has six children. However, in one session parent appears to have only five, and in another (2 seconds later, nothing changed in DB or in another session) - all six. Actually, I discovered it after detaching these entities from session with a custom cloner.
I thought that lazy collections are either completely initialized (i.e. all elements are), or not. Is it possible that somehow only a part of the collection was initialized? Can it be an issue with caching?
EDIT: This session handles a fairly large data set (a few thousands of entities). Is it possible that this is because some already-loaded entities got evicted from the session?
Start by checking your hashCode() and equals() methods, incorrect implementation of these methods often cause this kind of behavior.