# Hibernate - Performance



## y0dA (19. November 2007)

Hi!
Ich habe eine Liste von Objekten über welche ich iteriere und für jedes Objekt in der Liste muss ich zumindest 2 Datenbankabfragen absetzen (max 3 Statements). Kann ich hierbei irgendwie performen, denn im Moment dauert das Ganze sehr lange - nach jedem Stmt kommt ein commit etc. Gibt es Prepared Stmts in Hibernate, wie kann ich jene benützen?

Hier mal die Methode die min. 2 mal pro Objekt benutzt wird:

```
public static List<FeatureModel> getNearestNeighbour4Point(final double[] point, final int nnNumRes,
			final String unit, final int featureType, final int withinDistance, final int srid) {	
		StringBuffer point2 = new StringBuffer();
		point2.append(" SDO_GEOMETRY( ");
		point2.append(FeatureDB.POINT_GTYPE);
		point2.append(", ");
		point2.append(srid);
		point2.append(", ");
		point2.append("SDO_POINT_TYPE( ");
		point2.append(point[0]);
		point2.append(", ");
		point2.append(point[1]);
		point2.append(", null), null, null)");		
		
		StringBuffer sb = new StringBuffer();
		sb.append(" SELECT {feature.*} ");
		sb.append(" FROM feature {feature} ");
		sb.append(" WHERE SDO_NN ( ");
		sb.append(" feature.f_geom, ");
		sb.append(point2.toString());
		sb.append(", '");
		sb.append(" SDO_NUM_RES = ");
		sb.append(nnNumRes);
		sb.append(" ");
		sb.append(unit);
		sb.append("', 1) = 'TRUE' AND SDO_WITHIN_DISTANCE( feature.f_geom, ");
		sb.append(point2.toString());
		sb.append(", ' DISTANCE = ");
		sb.append(withinDistance);
		sb.append(" ");
		sb.append(unit);
		sb.append("') = 'TRUE' AND f_ft_id = ");
		sb.append(featureType);

		Session session = HibernateUtil.getCurrentSession();
		Transaction tx = null;
		List<FeatureModel> list = null;
		
		try {
			tx = session.beginTransaction();	
			SQLQuery q = session.createSQLQuery(sb.toString());
			q.addEntity("feature", FeatureModel.class);
			list = q.list();

			/* commit and close session */
			tx.commit();	
		} catch (Exception e) {
			if (tx != null) {
				tx.rollback();
			}
		} finally {
			session = null;
			tx = null;
		}
		return list;
	}
```


----------



## Kulabac (19. November 2007)

Gibt's einen Grund dafür, dass du mit SQL arbeitest und das Hibernate so quasi "umgehst"? Ich denke HQL könnte da um einiges performanter sein...

Ansonsten halt noch die Frage ob der SecondLevelCache schon an ist ... teilweise kann man auch Performance rausholen, indem man Objekte lazy setzt (so dass sie nur bei Bedarf aus der Datenbank nachgeladen werden und nicht immer komplett)

Zum Thema PreparedStatement: Klar gibt's sowas, aber kenn ich auch nur mit HQL bisher... möglich, dass die in SQL-Querys genauso gehen. Beispiel:

```
String queryString = " SELECT device FROM MyDevice device WHERE device.address = :device_address AND device.id = :id";
Query query = hibernateSession.createQuery(queryString);
query.setInteger("device_address", 123);
query.setString("id", "irgendeineID");
MyDevice myDevice = (MyDevice) query.uniqueResult();
```


----------



## y0dA (19. November 2007)

Mein Problem ist dass ich hier nichts aus einer Tabelle laden möchte sondern eine Oracle XE Function benutzen möchte *SDO_NN* um den nearest neighbour zu ermitteln - nun stellt sich für mich die Frage, kann ich das auch mit HQL oder muss ich, wie oben, SQL benutzen?


----------



## Kulabac (19. November 2007)

Ok, so genau hatte ich mir den Code dann doch nicht durchgelesen ... kann dann leider auch nicht weiterhelfen. Funktionen bieten ja die meisten Datenbanken an, von daher würde ich sagen: Klar geht's "irgendwie" mit HQL, man muss nur ein geeignetes Mapping finden, aber wie das geht ... hier gibt's aber einige Hibernate-Profis, die schauen hier bestimmt bald rein, wenn du Glück hast


----------



## Oliver Gierke (19. November 2007)

Warum machst du für jede Anfrage eine Transaktion? das sind doch nur selects, oder? Das readonly flag bei Oracle bringt ungemein etwas, da die sich dann den tablelock spart...

Gruß
Ollie


----------



## y0dA (19. November 2007)

Oliver Gierke hat gesagt.:


> Warum machst du für jede Anfrage eine Transaktion? das sind doch nur selects, oder? Das readonly flag bei Oracle bringt ungemein etwas, da die sich dann den tablelock spart...
> 
> Gruß
> Ollie



Da bin ich grad dabei, also dass alle stmts ausgeführt werden und dann erst ein einmaliges commit - jedoch was meinst du mit dem "readOnly flag"?


----------



## Oliver Gierke (19. November 2007)

```
Connection.setReadonly(true);
```
 Müsstest du wahrscheinlich in der HibernateUtils Klasse irgendwo möglich machen. Ich halte plain Hibernate mit diesen komischen Helperklassen eh für etwas suboptimal, weil die Trennung von Connection und Transaktionen nicht wirklich gelungen ist, meiner Meinung nach. FlushMode.NEVER bewirkt wohl ähnliches...

Gruß
Ollie


----------



## Thomas Darimont (19. November 2007)

Hallo,

wenn du nur read-only auf die Datenbank zugreifst brauchst du keine Transaktion.

Gruß Tom


----------



## y0dA (20. November 2007)

Im Moment sieht meine Hibernate Helper Klasse so aus, wie sollte ich die nun umgestalten für *readonly*?

```
public class HibernateUtil {

	//------------------------------------------------------------------------------------------------------------------
    // FIELDS
    //------------------------------------------------------------------------------------------------------------------

	/**
	 * hibernate session factory object
	 * singleton
	 */
	private static SessionFactory sf = null;
	
	/** hibernate database configuration file */
	private static final String HIBERNATE_CONFIG_FILE = "hibernate.cfg.xml";
	
	/** logger */
	private static final Logger LOGGER = Logger.getLogger(HibernateUtil.class.getName());
	//------------------------------------------------------------------------------------------------------------------
    // GETTER/SETTER
    //------------------------------------------------------------------------------------------------------------------

	/**
	 * getter method
	 * 
	 * initialize singleton <code>sf</code>
	 * 
	 * @return hibernate session factory
	 */
	private synchronized static SessionFactory getSessionFactory() {
		if (HibernateUtil.sf == null) {
			 /* log info */
			HibernateUtil.LOGGER.info
				("initialize hibernate session factory, class: HibernateUtil method: getSessionFactory");
			HibernateUtil.sf = new Configuration().configure(HibernateUtil.HIBERNATE_CONFIG_FILE).buildSessionFactory();
		}
		return HibernateUtil.sf;
	}
	
	/**
	 * getter method
	 * 
	 * @return hibernate session
	 */
	public static Session getCurrentSession() {
		return HibernateUtil.getSessionFactory().getCurrentSession();
	}
```

Oder soll ich einfach wie folgt arbeiten:

```
Session session = HibernateUtil.getCurrentSession();
                session.connection().setReadOnly(true); //connection ist deprecated
```


----------



## Oliver Gierke (20. November 2007)

```
session.setFlushMode(FlushMode.NEVER);
```
 im Clientcode (also nicht in der HibernateUtils) sollte den Effekt erzielen. Ich hatte die hibernateUtils nur erwähnt, da ich auf den ersten Blick etwas verwirrt war, dass es so Umständlich ist das readOnly Flag zu setzen.

Die vielen Transaktionen die du aufmachst, sind allerdings die größere Bremse glaub ich. Hast du mal versucht, Transaktionen komplett sein zu lassen?

Gruß
Ollie


----------



## y0dA (20. November 2007)

Guten Morgen  !

Ich bin leider betreffend Hibernate noch nicht ganz fit, und mir wurde halt einmal so gezeigt zu arbeiten und nun mach ich das immer so. Brauche ich denn keine *Transaktionen*? Auch nicht wenn ich auf die Db schreibe? Wie soll ich denn ohne Transaktionen arbeiten?

mfg

**EDIT**

```
session.setFlushMode(FlushMode.NEVER);
```
Ist ebenfalls deprecated..

**EDIT**
Ok bin nun ein wenig weiter. Bisher habe ich wohl den Fehler gemacht dass ich in der DAO meine Transaction beginne und nicht in der Businesslogik. Jedoch wenn ich es nun so mache dann klappt zwar das 1. Stmt aber beim 2. Stmt bekomme ich dann eine Exception da meine Transaction geschlossen ist (obwohl ich jene nicht geschlossen habe nach dem 1. Stmt).

Und wenn ich keine Transaktion benutze, bekomme ich folgende Exception:

```
org.hibernate.HibernateException: createSQLQuery is not valid without active transaction
```


----------



## Oliver Gierke (20. November 2007)

Wenn du in die DB schreibst brauchst du natürlich eine Transaktion. Allerdings ist von SChreiben in deinem Code nichts zu sehen.


```
FlushMode.NEVER
```
 ist tatsächlich deprecated. Nun gut, ich hab die API auch nicht 100%ig im Kopf. Gut das im JavaDoc aber steht, man soll einfach 
	
	
	



```
FlushMode.MANUAL
```
 benutzen. Darfst dann halt einfach kein flush() rufen.

Weiß allerdings nicht, ob der Effekt dann noch der gleiche ist, da die Connection dann eigentlich nicht mehr readOnly werden kann, weil potentiell ja Schreibzugriffe stattfinden könnten.

Wegen der Exception. Die macht natürlich Sinn, auch wenn das meiner Meinung nach ganz klar ne Schwäche der Session API ist. Wenn die Methode beliebige SQL Statements bekommen kann, können das natürlich potentiell Schreibende SQL Statments sein und dann ist eine Transaktion von nöten. Ein Grund mehr für richtige SQL Querys an Hibernate vorbeizuarbeiten.

Dann schau mal, wie weit du damit kommst, alles in eine Transaktion zu fassen.

Gruß
Ollie


----------



## y0dA (20. November 2007)

Oliver Gierke hat gesagt.:


> Wenn du in die DB schreibst brauchst du natürlich eine Transaktion. Allerdings ist von SChreiben in deinem Code nichts zu sehen.
> 
> 
> ```
> ...



Wenn ich alles in eine Transaktion packe, dann bekomme ich ebenfalls oben genannte Exception (beim 2. Stmt):

```
rg.hibernate.HibernateException: createSQLQuery is not valid without active transaction
```

D.h. bei mir wird die Transaktion geschlossen nachdem ersten Stmt - warum auch immer (ich schließe sie nicht von selbst!). Gibt es hier vllt dem Session Objekt irgendwas mitzuteilen? Kann sowas wie ein AutoClose gesetzt sein?


----------



## Oliver Gierke (20. November 2007)

Hast du in der HibernateConfig autoCommit schon auf false gesetzt?


----------



## y0dA (20. November 2007)

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
	<property name="hibernate.connection.driver_class">
		oracle.jdbc.driver.OracleDriver
	</property>
	<property name="hibernate.connection.password">navteq</property>
	<property name="hibernate.connection.url">
		jdbc:oracle:thin:@192.11.100.207:1521:XE
	</property>
	<property name="hibernate.connection.username">navteq</property>
	<property name="hibernate.dialect">
		org.hibernate.dialect.OracleDialect
	</property>
	<property name="myeclipse.connection.profile">
		ORACLE_SPATIAL_VM
	</property>
	<property name="connection.url">
		jdbc:oracle:thin:@192.11.100.207:1521:XE
	</property>
	<property name="connection.username">navteq</property>
	<property name="connection.password">navteq</property>
	<property name="connection.driver_class">
		oracle.jdbc.driver.OracleDriver
	</property>
	<property name="dialect">
		at.pcd.wam.technologie.persistence.custom.type.OracleSpatialDialect
	</property>

	<!-- Enable Hibernate's automatic session context management -->
	<property name="current_session_context_class">thread</property>

	<!--  -->
	<property name="hibernate.show.sql">true</property>
	<!--<property name="hibernate.hbm2ddl.auto">create</property>-->
	<!-- <property name="hibernate.hbm2dll.auto">update</property>  -->
	<mapping
		resource="at/pcd/wam/technologie/persistence/hbm/GPSToursModel.hbm.xml" />
	<mapping
		resource="at/pcd/wam/technologie/persistence/hbm/FeatureModel.hbm.xml" />

</session-factory>
</hibernate-configuration>
```

wohl nicht, d.h. durch das autocommit wird sowieso nach jedem stmt ein commit abgesetzt?


----------



## Oliver Gierke (20. November 2007)

Richtig. Versuch mal: 


```
<property name="connection.autocommit">true</property>
```


----------



## y0dA (20. November 2007)

Dachte ich soll autocommit auf false stellen  ?


----------



## Oliver Gierke (20. November 2007)

sollst du auch...  *sich an den kopf haut


----------



## y0dA (20. November 2007)

Ich hoffe ich strapaziere deine/eure Geduld nicht, aber folgendes würde mir noch sehr helfen:

Wenn ich nur lesend auf die DB gehe kann ich Transaktionen komplett weglassen (auch wenn ich nicht mit HQL sondern mit SQL arbeite - oder benötigt SQL Transaktionen?

Wie handhabt ihr das mit der Hibernate Session und der Transaktion? Sprich setzt ihr beides schon in der Businesslogik oder erst in der DAO (wenn vorhanden)?


----------



## Oliver Gierke (20. November 2007)

y0dA hat gesagt.:


> Ich hoffe ich strapaziere deine/eure Geduld nicht, aber folgendes würde mir noch sehr helfen:


Nur kein Stress, wenn ich arg viel zu tun hab, antworte ich einfach nicht so schnell 


> Wenn ich nur lesend auf die DB gehe kann ich Transaktionen komplett weglassen (auch wenn ich nicht mit HQL sondern mit SQL arbeite - oder benötigt SQL Transaktionen?


SQL bzw. JDBC benötigen erstmal gar keine Transaktionen, da auch nicht jede DB Transaktionen anbietet. Für rein lesende Operation kann man die also eigentlich weglassen. Wo das Problem mit Hibernates createSqlQuery() liegt, hab ich ja oben schon beschrieben. Scheint so, als "erzwingt" hibernate damit Transaktionen.



> Wie handhabt ihr das mit der Hibernate Session und der Transaktion? Sprich setzt ihr beides schon in der Businesslogik oder erst in der DAO (wenn vorhanden)?


Ich bevorzuge den Weg über Spring. Das bietet ein HibernateTemplate, was das ganze Sessionhandling kapselt und man Transaktionen deklarativ konfigurieren kann. Zum anderen sorgt das Ding dafür, dass man datenbankunabhängige Exceptions bekommt und diese somit besser verarbeiten kann. (Das macht es unter anderem auch sehr leicht möglich für 90% der DB Anfragen Hibernate zu benutzen, StoredProcedures oder ähnliches allerdings mit JDBC zu triggern). Arbeit mit der Session gehört meiner meinung nach wenn dann ins DAO. Allerdings sind die DAOs meist nicht der richtige Platz für die Demarkation von Transaktionen (Methodenschnittstellen zu feingranular, nicht clientorientiert). Daher ist IMHO das Arbeiten mit plain Hibernate nichts, was wirklich Spass macht bzw. elegant wird. Desweiteren würde ich deklarative Transaktionen programmatischen immer vorziehen. Man schreibt einfach zuviel Boilerplatecode sonst.

Gruß
Ollie


----------



## y0dA (21. November 2007)

Also irgendwie klappt das ohne Transaktion nicht wirklich...


```
public static List<FeatureModel> getNearestNeighbour4Point(final double[] point, final int nnNumRes,
			final String unit, final int featureType, final int withinDistance, final int srid) {	

		StringBuffer point2 = new StringBuffer();
		point2.append(" SDO_GEOMETRY( ");
		point2.append(FeatureDB.POINT_GTYPE);
		point2.append(", ");
		point2.append(srid);
		point2.append(", ");
		point2.append("SDO_POINT_TYPE( ");
		point2.append(point[0]);
		point2.append(", ");
		point2.append(point[1]);
		point2.append(", null), null, null)");	
		
		String s = "SELECT feature FROM Feature feature WHERE " + 
			"(SDO_NN (feature.f_geom, :point2, 'SDO_NUM_RES = :nnNumRes :unit') 1) = 'TRUE' AND " + 
			"(SDO_WITHHIN_DISTANCE(feature.f_geom, :point2, ' DISTANCE = :withinDistance :unit') = 'TRUE' AND f_ft_id = :featureType"; 
			
		Session session = HibernateUtil.getCurrentSession();
		List<FeatureModel> list = null;	
		
		try {
			Query query = session.createQuery(s); 
			query.setString("point", point2.toString()); 
			query.setInteger("nnNumRes", nnNumRes); 
			query.setString("unit", unit); 
			query.setInteger("withinDistance", withinDistance); 
			query.setInteger("featureType", featureType); 
			query.list(); 
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session = null;
		}
		return list;
	}
```
Wenn ich das so ausführen möchte, ohne Transaction bekomme ich folgende Exception:

```
org.hibernate.HibernateException: createQuery is not valid without active transaction
	at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
	at $Proxy0.createQuery(Unknown Source)
	at at.pcd.wam.technologie.persistence.db.FeatureDB.getNearestNeighbour4Point(FeatureDB.java:74)
	at at.pcd.wam.technologie.controller.FeatureController.doMatchPoint(FeatureController.java:217)
	at at.pcd.wam.technologie.controller.FeatureController.evaluateTrackSegmentList(FeatureController.java:135)
	at at.pcd.wam.technologie.batch.RunBatch.main(RunBatch.java:123)
```


----------



## y0dA (21. November 2007)

ok, anscheinend braucht man wirklich immer eine Transaction
Punkt 11.2: http://www.hibernate.org/hib_docs/reference/en/html/transactions.html


----------

