# Hibernate - aktualisieren von Objekten



## mmm (14. Aug 2007)

Hallo zusammen,

ich versuche, eine kleine Anwendung mit Hibernate, Spring und Struts zu machen.

Dafür habe ich mir folgendes Konzept überlegt:

- Der DB-Zugriff erfolgt über DAOs, die direkt mit Hibernate arbeiten:

z.B. UserDaoImpl.java enthält eine Methode getUserById(Integer id) mit folgendem Inhalt:


```
return (User) getHibernateTemplate().get(User.class, id);
```


- Zwischen DAO und Präsentationsschicht (Struts2) liegt eine Service-Schicht, die teilweise die DAO-Methoden "durchschleift", aber z.B. auch kompliziertere Dinge macht, bevor sie in die Datenbank (über die entsprechenden DAOs, die von Spring injected werden) geschrieben werden.

Nun habe ich ein - zumindest für mich - seltsames Problem:

In einer Struts-Action möchte ich Formulardaten in das User-Objekt schreiben. Dazu hole ich mir das entsprechende User-Objekt aus der Datenbank (über eine entsprechende Methode der Service-Schicht, welche wiederrum das UserDAO kontaktiert). Anschließend ändere ich die Property des geholten Objekts auf den neuen Wert, der vom Formular kommt. 

Alles beendet sich normal, es gibt keine Exceptions.

Aber: Der Wert wurde nicht in die Datenbank geschrieben. 

Ich habe irgendwo gelesen, dass das Objekt vermutlich detached ist, deswegen war mein zweiter Versuch, eine "attach"-Methode in der UserDAO zur Verfügung zu stellen:


```
public void attachUser(User u) {
   getHibernateTemplate().lock(u, LockMode.NONE);
}
```

Diese Methode habe ich wieder über die Service-Schicht durchgeschleift; wenn ich dann das geänderte User-Objekt übergebe, passiert allerdings (leider) auch ncihts.

Ich weiß echt nicht mehr weiter, es wäre super, wenn mir jemand von euch helfen würde!

Vielen dank und schöne Grüße,

mmm


----------



## SlaterB (14. Aug 2007)

bevor du dich mit noch mehr geschliffenen Schleifen verschleifst:
funktioniert denn überhaupt das Anlegen und Bearbeiten von Usern rein auf der Ebene der DB-Schicht?
öffnest und commitest du eine Transaktion?


----------



## mmm (14. Aug 2007)

Hallo SlaterB,

vielen Dank für deine schnelle Antwort. Ja, alle Elemente auf der DAO-Schicht sind mit JUnit-Tests geprüft (und ich habe manuell später in der DB nachgeschaut ;-))

Ich nutze keine Transaktionen, d.h. ich öffne keine im Code. Ich halte mich ziemlich nah an das Buch "Spring & Hibernate - eine praxisbezogene Einführung" (Hanser-Verlag,2007).

Ich habe in der web.xml noch folgenden Filter definiert:


```
<filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class>
            org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        </filter-class>
</filter>
```

- leider reicht das nicht :-/


----------



## mmm (14. Aug 2007)

SlaterB hat gesagt.:
			
		

> bevor du dich mit noch mehr geschliffenen Schleifen verschleifst:
> funktioniert denn überhaupt das Anlegen und Bearbeiten von Usern rein auf der Ebene der DB-Schicht?
> öffnest und commitest du eine Transaktion?



Anbei noch ein paar Code-Teile aus den betroffenen Klassen:

UserDAO:


```
public User getUserByUsername(String username) {
		List<User> l = getHibernateTemplate().find("FROM User u WHERE u.username = ?", username);
		if(l != null && l.size() >= 1) return l.get(0);
		else return null;
	}

	
	public Integer addUser(User u) {
		Integer r = -1;
		try {
			r = (Integer) getHibernateTemplate().save(u);
		} catch (Exception e) {
			r = -2; // username already exists
			// e.printStackTrace();
		}
		return r;
	
	}
```


In der JUnit-Testklasse hole ich ein Objekt und ändere es dann:

```
public void testChangeProperty() {
		User u = userDao.getUserByUsername("tester");
		assertEquals("Firstname ok?", u.getFirstname(), "Bella");
		// change firstname
		u.setFirstname("BellaHund");
		sessionFactory.getCurrentSession().flush();
		assertEquals("changes made in DB?", 1, jdbcTemplate.queryForInt("SELECT count(1) FROM User u WHERE u.Username = 'tester' AND u.Firstname = 'BellaHund'"));
		assertEquals("old data removed from DB?", 0, jdbcTemplate.queryForInt("SELECT count(1) FROM User u WHERE u.Username = 'tester' AND u.Firstname = 'Bella'"));
	}
```

wobei ich in der Methode onSetUpInTransaction() folgende Initialkonfiguration anlegen:


```
protected void onSetUpInTransaction() throws Exception {
		User u = new User();
		u.setUsername("tester");
		u.setFirstname("Bella");
		userDao.addUser(u);
		
		
		sessionFactory.getCurrentSession().flush();
		sessionFactory.getCurrentSession().clear();
		
	}
```

Das funktioniert - der Test wird grün 

Leider löst das nicht mein anderes Problem.... :-(


----------



## mmm (14. Aug 2007)

Nachtrag:

Jetzt habe ich mal versucht, die Änderungen am User-Objekt mal in der Service-Klasse zu machen. D.h. ich erzeuge in der Struts-Action ein neues User-Objekt, befülle es mit den Daten aus dem Formular und mit der Id, die der entsprechende User-Eintrag in der Datenbank hat.

Die Service-Methode sieht wie folgt aus (u.id wurde korrekt gesetzt):


```
public void updateUser(User u) {
		User o = userDao.getUserById(u.getId());
		o.setFirstname(u.getFirstname());
	}
```

Leider führt das auch nicht dazu, dass die Änderung dauerhaft in der DB gespeichert wird (wobei, selbst wenn es so funktionieren würde, fände ich es schöner, wenn die Änderung der Properties gleich eine Änderung der Datenbank zur Folge hätte - ohne dieses zusätzliche Transport-User-Objekt u).

Ist das hier überhaupt noch das richtige Forum oder geht das schon zu weit in Richtung J2EE? 

Danke und schöne Grüße,
mmm


----------



## SlaterB (14. Aug 2007)

cih frage mich nur, ob irgendjemand auch ein Transaktion anlegt,
kann aber nix konkretes weiter dazu beitragen


----------



## mmm (14. Aug 2007)

Hm, ich habe jetzt etwas rumgesucht und bin auf folgendes gestoßen:

1. Dieser OpenSessionInViewFilter hat wohl  als default-Flush-Mode "Never" eingestellt - vermutlich wird dort also gar nicht geflush()t.

2. Ich verstehe den Sinn von Transaktionen in Spring nicht - brauche ich die wirklich? Bis jetzt habe ich für Spring also keine Transaktionsspezifischen Einträge in der applicationContext.xml definiert, sondern fahre ohne. 

Kann es sein, dass es daran liegt?

Ich bin für jeden Hinweis (Links, Hilfestellungen, etc.) unendlich dankbar ;-)

Schöne Grüße,

mmm


----------



## ms (18. Aug 2007)

In deinem JUnitTest, der offensichtlich von org.springframework.test.AbstractTransactionalSpringContextTests ableitet hast du serwohl eine Transaktion. Darum läuft der Test auch ohne Fehler. 
Wenn du in deiner Applikation keine Transaktion hast gibts auch kein commit() und auch keine Daten in der Datenbank.

Der OpenSessionInViewFilter hat mit einer Transaktion selbst nichts zu tun. Er öffnet und schließt nur deine Session.
Man kann zwar die Transaktion an die Hibernatesession koppeln. In deinem Fall, da du den OpenSessionInViewFilter verwendest, macht das nicht viel Sinn, da man üblicherweise die Transaktionsklammern um die Servicemethoden, also der Serviceschicht hat.

Die Transaktionsschicht kannst du im Spring-Context relativ einfach hinzufügen.
Für deinen Fall sollte folgendes passen:


```
<bean id="userServiceTarget">
	<property name="irgendeinDao" ref="irgendeinDao"/>
</bean>

<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
	<property name="transactionManager" ref="transactionManager" />
	<property name="target" ref="userServiceTarget" />
	<property name="transactionAttributes">
		<props>
			<prop key="*">PROPAGATION_REQUIRED</prop>

			<!-- Hier sind weitere Möglichkeiten die Transaktion zu steuern 
				<prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop>
				<prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
			-->
		</props>
	</property>
</bean>

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" lazy-init="true">
	<property name="userTransactionName" value="UserTransaction"/>
</bean>
```

Da meine Applikationen auf einem JBoss laufen kann ich den JTA-Transactionmanager hier angeben, bei einem reinen Tomcat müßtest du den HibernateTransactionmanager hier verwenden.


ms


----------

