# Constraintsverletzung?



## mavinatic (27. Apr 2012)

Hallo Community,

ich bin nicht der ganz große Experte, wenn es um Datenbanken geht, aber ich habe ein Problem: Momentan arbeite ich mit Hibernate 4.x und habe folgende Tabellen

[User] {userId(PK),userPassword,List<Role>}
[Role] {roleId(PK), List<Rule>}
[Rule] {ruleId(PK), ruleName, ruleValue}

Ich wollte nun mit Hibernate nur eine Role bzw. Rule anlegen...alle Parameter des Objekts ausgefüllt und mit Hibernate.save() archiviert/persistiert...etc.

Da sagt mir Hibernate ich würde Constraints verletzen?! aber wieso?


----------



## Andgalf (27. Apr 2012)

Sorry, aber hier gibt es eine policy, die das nutzen von Glaskugeln untersagt also solltest du mal dein Hibernate mapping und den Stacktrace posten.


----------



## mavinatic (27. Apr 2012)

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: INSERT in Tabelle 'ROLES' hat für Schlüssel (e7e9737d-1aed-428c-b67f-2ea16ac2ec75) die Integritätsbedingung 'FK4B79E9D3192801A' für Fremdschlüssel verletzt. Die Anweisung wurde zurückgesetzt.
	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:156)
	at $Proxy40.addRole(Unknown Source)
	at CoreAdminServiceClient.main(CoreAdminServiceClient.java:31)
Caused by: org.apache.cxf.binding.soap.SoapFault: INSERT in Tabelle 'ROLES' hat für Schlüssel (e7e9737d-1aed-428c-b67f-2ea16ac2ec75) die Integritätsbedingung 'FK4B79E9D3192801A' für Fremdschlüssel verletzt. Die Anweisung wurde zurückgesetzt.
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.unmarshalFault(Soap11FaultInInterceptor.java:75)
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:46)
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:35)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:799)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1494)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1402)
	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:649)
	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:533)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
	at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:88)
	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
	... 2 more


Role:

```
@Entity
@Table(name="Roles")
public class Role implements Serializable {
	@Column
	private String roleName;
	@Id
	@Column
	private String roleId;
	@OneToMany(fetch=FetchType.LAZY, targetEntity=Rule.class, mappedBy="ruleId")
	private List<Rule> ruleList = new ArrayList<Rule>();
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
	public String getRoleId() {
		return roleId;
	}
	public void setRoleId(String roleId) {
		this.roleId = roleId;
	}
	public List<Rule> getRuleList() {
		return ruleList;
	}
	public void setRuleList(List<Rule> ruleList) {
		this.ruleList = ruleList;
	}
	public void addRule(Rule r) {
		this.ruleList.add(r);
	}
}
```

Rule:
	
	
	
	





```
@Entity
@Table(name="rules")
public class Rule implements Serializable {
	@Id
	@Column
	private String ruleId;
	@Column
	private String ruleName;
	@Column
	private String ruleValue;
	
	public String getRuleId() {
		return ruleId;
	}
	public void setRuleId(String ruleId) {
		this.ruleId = ruleId;
	}
	public String getRuleName() {
		return ruleName;
	}
	public void setRuleName(String ruleName) {
		this.ruleName = ruleName;
	}
	public String getRuleValue() {
		return ruleValue;
	}
	public void setRuleValue(String ruleValue) {
		this.ruleValue = ruleValue;
	}
}
```
User:

```
@Entity
@Table(name="users")
public class User implements Serializable 
{
	@Id
	@Column
	private String userId;
	@Column
	private String userPassword;
	@Column
	private Date registrationDate;
	@Column
	private Date lastLogin;
	@Column
	private boolean online;
	@OneToMany(fetch=FetchType.LAZY, targetEntity=Role.class, mappedBy="roleId")
	private List<Role> roles = new ArrayList<Role>();
	
	public void addRole(Role r) {
		this.roles.add(r);
	}
	public List<Role> getRoles() {
		return roles;
	}
	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getUserPassword() {
		return userPassword;
	}
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}
	public Date getRegistrationDate() {
		return registrationDate;
	}
	public void setRegistrationDate(Date registrationDate) {
		this.registrationDate = registrationDate;
	}
	public Date getLastLogin() {
		return lastLogin;
	}
	public void setLastLogin(Date lastLogin) {
		this.lastLogin = lastLogin;
	}
	public boolean isOnline() {
		return online;
	}
	public void setOnline(boolean online) {
		this.online = online;
	}
	
	
}
```

Webservice-Methode wird aufgerufen:
Hier wird nur ein Role-Objekt erstellt(und mit Daten gefüllt) und mit Hibernate in der Datenbank versenkt!?

```
String sessionId = "4b98171d-81d6-415c-b823-fa36a9390fa9";
        client.addRole("Admins", sessionId);
```


----------



## nillehammer (27. Apr 2012)

```
@OneToMany(fetch=FetchType.LAZY, targetEntity=Role.class, mappedBy="roleId")
    private List<Role> roles = new ArrayList<Role>();
```
Das mappedBy ist dreifach falsch.
1. Man nutzt es nur, wenn die Beziehung auf Objektseite bidirektional ist, d.h. es muss in User eine Collection von Roles geben (hast Du) und es muss in Rolle ein Feld User geben (hast Du nicht).
2.Man stellt in der Objekwelt die Beziehung über Felder des ensprechenden Typs her und nicht wie in der Datenbankwelt mit den Ids. Hätte Role also ein Feld "user" müsste dort der "user"stehen.
3. Mit mappedBy bezieht man sich auf das Property auf der anderen Seite einer Beziehung, nicht auf die eigene.

Außerdem glaube ich, dass hier @OneToMany konzeptionell falsch ist. Ein Nutzer kann doch mehrere Rollen haben und eine Rolle kann mehreren Benutzern zugewiesen sein. Insofern würde ich hier intuitiv @ManyToMany nehmen.

Abgesehen davon ist das Initialisieren der Listen/Sets/Collections der bei Erzeugen der Instanz ungünstig, weil unnötig. Schreib lieber in dem Getter eine Prüfung auf roles==null und gib in dem Fall eine Collections.emptyList zurück.


----------



## mavinatic (27. Apr 2012)

Zu 1: Wie mache ich das es nur Unidirektional ist? Welchen Parameter muss ich da angeben?

Zu 2: Versteh ich nicht

Zu 3: Versteh ich nicht ganz, wenn es unidirektional ist wie behebe ich es?

Ich habe schon zu @ManyToMany geändert ;-)


----------



## nillehammer (27. Apr 2012)

mavinatic hat gesagt.:
			
		

> Zu 1: Wie mache ich das es nur Unidirektional ist? Welchen Parameter muss ich da angeben?


Die Unidirektionalität entsteht dadurch, dass Du in deinen Klassen nur auf einer Seite der Beziehung die Referenz hast. Beim Mapping musst Du das _mappedBy_ einfach streichen.


			
				mavinatic hat gesagt.:
			
		

> Zu 2: Versteh ich nicht


In der Datenbankwelt sind Daten in Zeilen von Tabellen organisiert. Ids werden über Primärschlüsselspalten, Beziehungen zu anderen Tabellen werden über Fremdschüsselspalten hergestelt. In der Javawelt sind die Daten in Instanzen von Klassen organisiert. Es gibt hier keine Primärschlüssel. Beziehungen zu anderen Instanzen werden über Referenzvariablen hergestellt. Wenn Du also mit _mappedBy_ arbeiten müsstest (was Du hier ja nicht musst), musst Du den Namen des Properties angeben, mit dem Du Dir die andere Instanz holst. Es gibt kein Mapping auf id-Felder. 





			
				mavinatic hat gesagt.:
			
		

> Zu 3: Versteh ich nicht ganz, wenn es unidirektional ist wie behebe ich es?


Wie schon zu 1. bei unidirektionalen Beziehungen brauchst Du überhaupt kein _mappedBy_.


----------



## mavinatic (27. Apr 2012)

Ich habe jetzt alles so korrigiert, dass das "mappedby" weg ist und eine ManyToMany verknüpfung eingebaut. Nun habe ich das Problem, dass ich immernoch die gleiche Exception bekomme, dass eine Constraint verletzt wird.


----------



## nillehammer (27. Apr 2012)

Ok, Mapping müsste dann soweit passen. Kontrollier zur Sicherheit nochmal die entstandenen Tabellen. Du müsstest jetzt folgende Tabellen haben:
- Users
- Roles
- User_Roles (FK nach Users.userId, FK nach Roles.roleId)
- Rules (FK nach Roles.roleId)
Die Namen sind evtl, leicht anders, aber die Struktur müsste so stimmen.

Die Exception, die jetzt fliegt, müsste auch eine andere sein als vorher, weil Roles jetzt keinen Fremdschlüssel auf eine andere Tabelle mehr hat. Bitte poste die neue noch mal.

Und zu guter letzt der Code zum Erzeugen einer Rolle:

```
String sessionId = "4b98171d-81d6-415c-b823-fa36a9390fa9";
client.addRole("Admins", sessionId);
```
Das scheint eine add-Methode zu sein, die auf einer Instanz von User aufgerufen wird. Dort kann ich aber keine Methode mit entsprechender Signatur (zwei String-Parameter) entdecken. Ist das noch Altlast, hast Du für User nicht den aktuellen Code gepostet oder ist _client_ noch etwas völlig anderes?


----------



## mavinatic (27. Apr 2012)

Hey, das hat wunderbar funktioniert!  Ich habe nochmal alle Tabellen gelöscht?! Und dann gings?!

Nun habe ich ein Problem mit dem LazyLoading :-(

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: failed to lazily initialize a collection of role: ....entity.Role.ruleList, no session or session was closed


----------



## nillehammer (27. Apr 2012)

Das ist ein Klassiker. Das hängt damit zusammen, dass Du den User lädst, danach die Sesion schließt und dann versuchst, über eine lazy-Loaded Collection zu iterieren. Dazu gibt es mehrere mögliche Lösungsvarianten:
1.) Mach die Collection eager-loaded (die schnelle Lösung, wenn man nicht verstanden hat, wie es richtig geht )
2.) Lass die Session offen (besser als 1. aber auch noch nicht gut genug, weil Sessions eigentlich als kurzlebige Objekte gedacht sind)
3.) Sorge dafür, dass Du vor dem Iterieren eine neue Session aufmachst, in der Du Dir den User frisch holst. Das zugehörige Pattern heißt Open-Session-in-View-Pattern.

Da würde ich Dich aber jetzt auf google verweisen, weil das doch etwas zu viel ist, um es in einem kleinen Post zu erklären.

[EDIT]
Habe nicht genau hingeschaut. Bei Deinem Fehler "entity.Role.ruleList" müsstest Du Dir natürlich die Role frisch holen und dann über die ruleList iterieren. Aber das Prinzip sollte klar sein.
[/EDIT]


----------



## mavinatic (2. Mai 2012)

Hey nillehammer,

ich habe extra eine Klasse für Hibernate geschrieben (ich poste sie ganz unten) und zwar ist immer eine Session offen, bzw. wenn nicht wird eine Session geholt. Von daher versteh ich nicht, wie die Session geschlossen sein kann!


```
package com.george.crm.core;
....hier ist der ganze Importkram + Konstruktor
		config.configure();
		
		serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();
		sessionFactory = config.buildSessionFactory(serviceRegistry);
	}
	
	public synchronized org.hibernate.Session getSession() {
		session = sessionFactory.getCurrentSession();
		if(session==null)
			session = sessionFactory.openSession();
		
		return session;
	}
	
	public synchronized void saveObject(Object obj) throws CoreServiceException {
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			session.save(obj);
			
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
	
	public synchronized void deleteObject(Object obj) throws CoreServiceException {
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			session.delete(obj);
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
	
	public synchronized void updateObject(Object obj) throws CoreServiceException {
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			session.update(obj);
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
	
	public synchronized List<Object> createQuery(String query) throws CoreServiceException {
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			Query q = session.createQuery(query);
			
			@SuppressWarnings("unchecked")
			List<Object> list = (List<Object>) q.list();
			
			if(list==null)
				return new ArrayList<Object>();
			else
				return list;
			
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
	
	public synchronized Object createQueryWithSingleResult(String query) throws CoreServiceException{
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			Query q = session.createQuery(query);
			
			@SuppressWarnings("unchecked")
			List<Object> list = (List<Object>) q.list();
			
			if(list==null || list.size()==0)
				return null;
			else
				return list.get(0);
			
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
	
	public synchronized void deleteSingleObjectWithQuery(String query) throws CoreServiceException{
		Transaction transaction = null;
		try {
			transaction = getSession().beginTransaction();
			
			Query q = session.createQuery(query);
			
			@SuppressWarnings("unchecked")
			List<Object> list = (List<Object>) q.list();
			
			if(list!=null && list.get(0)!=null) {
				session.delete(list.get(0));
			}
				
		} catch(Exception ex) {
			if(transaction!=null)
				transaction.rollback();
			throw new CoreServiceException(ex.getMessage());
		} finally {
			if(transaction!=null)
				transaction.commit();
		}
	}
}
```


----------



## Andgalf (2. Mai 2012)

Hallo,

kann dein Problem hier leider nicht nachvollziehen, da es i-wie zu wenig Informationen gibt. Du sprichst von Session-Closed also hast Du i-wo eine Exception vermute ich. Aber da Du ja nillehammer direkt ansprichst weiß der vll. ja mehr.

Auf die Gefahr hin mich zu wiederholen Poste doch mal den Stacktrace.

Aufgefallen ist mir allerdings das hier:

```
throw new CoreServiceException(ex.getMessage());
```

da gibst du nur die Message weiter und nicht den ganzen Trace .... vorallem nicht den RootCause. Es könnte also sein, dass das was Du siehst nur ein Symptom bzw. Folgefehler o.ä. ist.


----------



## mavinatic (3. Mai 2012)

Hier der Stacktrace:

```
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: failed to lazily initialize a collection of role: com.crm.core.entity.Role.rules, no session or session was closed
	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:156)
	at $Proxy40.addRuleToRole(Unknown Source)
	at CoreAdminServiceClient.main(CoreAdminServiceClient.java:34)
Caused by: org.apache.cxf.binding.soap.SoapFault: failed to lazily initialize a collection of role: com.george.crm.core.entity.Role.rules, no session or session was closed
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.unmarshalFault(Soap11FaultInInterceptor.java:75)
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:46)
	at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:35)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:799)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1494)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1402)
	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:649)
	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:533)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
	at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:88)
	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
	... 2 more
```


----------



## Andgalf (3. Mai 2012)

Sorry, sieht mir immer noch nicht nach dem kompletten stacktrace auf.

Ich rate mal:

i-wo hast du die Methode addRuleToRole (im Stacktrace durch Proxy weggekapselt)

von dort aus rufst du updateObject in deiner "Hibernate Klasse" auf. 

vorher hast Du allerdings schon Roles geladen ... allerdings in einer anderen Session als die, mit der du das updateObject durchführst.

Beim Nachladen der Collection (Rules) ist die erste Session dann schon wieder geschlossen.

Aber auf dem Informationsstand, den ich habe ist das wie schon erwähnt *geraten*

Alles in allem bleibt es aber der lazy init Fehler und dazu hat nillehammer ja schon einiges geschrieben und auf google verwiesen.


----------



## mavinatic (3. Mai 2012)

Ich habe dieses Pattern verstanden, aber ich möchte diese Logik wegkapseln und zwar, dass über eine Methode saveObject, updateObject, ...in einer Session und einer Transaction abgewickelt wird.

Dafür die Klasse DbFactory!


----------



## mavinatic (4. Mai 2012)

Sorry für den Doppelpost. Ich habe auf der Hibernate-Website eine Beschreibung zum Open-Session in View Pattern gefunden, aber irgendwie kann ich das nicht so einbauen, sodass es "ordentlich" programmiert ist (meiner Meinung nach). Ich könnte jetzt in der Role-Class bei getRules eine Session öffnen und die Datenabholen, wäre aber irgendwie nicht die beste Lösung habt ihr vielleicht vorschläge wie ich das lösen könnte?


----------



## nillehammer (4. Mai 2012)

mavinatic hat gesagt.:
			
		

> Ich könnte jetzt in der Role-Class bei getRules eine Session öffnen und die Datenabholen, wäre aber irgendwie nicht die beste Lösung habt ihr vielleicht vorschläge wie ich das lösen könnte?


Da hast Du vollkommen Recht. Role ist eine Entity und die sollte nun wirklich überhaupt nichts von irgendwelchen Persistenz-Mechanismen wissen.

Da Du Dich mit Deinem Webservice im Web-Umfeld bewegst, ist der _Request_ für Dich die richtige Klammer. Der Ablauf ist ja immmer so:
- Request kommt rein. Hier könnte man ganz früh die Session aufmachen und an den Request hängen, dann hat man sie während der ganzen Verarbeitung zur Verfügung.
- Das Backend macht irgendwas. U.a. Daten mittels der Session holen.
- Der Response wird versendet (bei normalen Webanwendungen ist das eine aktualisierte GUI, beim Webservice aktualisierte XML-Daten), hier könnte die Session dann geschlossen werden.

Bevor wir jetzt darüber nachdenken, wie man sowas selbst implementiert, würde ich eher zu Frameworks raten, die Dir sowas abnehmen. Im Webservice-Umfeld kenne ich mich da nicht so aus, aber im Umfeld normaler Webanwendungen würde mir als erstes Dependency Injection mit Spring einfallen. Ich selbst habe es bei Webanwendungen mit Tapestry gemacht. Technisch ist das sehr gut gemacht und lässt sich auch gut verwenden, aber seine Verbreitung ist halt eher klein.


----------



## Andgalf (4. Mai 2012)

mavinatic hat gesagt.:


> Ich könnte jetzt in der Role-Class bei getRules eine Session öffnen und die Datenabholen, wäre aber irgendwie nicht die beste Lösung habt ihr vielleicht vorschläge wie ich das lösen könnte?



Keine gute Idee ... innerhalb des hibernate getters mit anderen Sessions rum zumachen. Sagen dir die Begriffe dettached, persistent, transient etwas in Verbindung mit hibernate? Fall nicht:

Chapter*10.*Working with objects

Was spricht denn gegen das openSessionInView Pattern? Das ist doch recht weit verbreitet und somit auch erprobt.

Eine Alternative wäre es noch Eager zu fetchen. Ist aber auch nicht die beste Idee, da Du unter Umständen dann immer die ganze Datenbank lädst. Wenn es allerdings nur um die Rule Collection geht wäre das vertretbar.

[EDIT]
Ok Nillehammer war schneller 

Mal ne Frage nebenbei .... auf welcher Infrastruktur arbeitest Du eigentlich ist das ein JEE Container der die Webservice Requests bearbeitet oder i-was selbstgestricktes?
[/EDIT]


----------



## mavinatic (4. Mai 2012)

Mein Umfeld ist momentan etwas eigengestricktes, da ich dadurch die bestmögliche für meinen Fall rausholen kann. Ein JBoss, Websphere, etc wäre zu übertrieben und vollkommen unnötig. Ich arbeite mit Apache CXF, Hibernate und Jetty. So Slim wie möglich...Mein ganzes Programm wird nur über Webservices angesprochen und brauch, daher "keinen JEE AS".

Wie Nillehammer gesagt hat, wäre es schmiererei im Entity Objekt rumzukritzeln, da die 3-Tier von mir getrennt sein sollen bzw. 2 - Tier.

@Nillehammer:
Ich denke meine Anwendung von Hibernate ist doch kein einzelfall, dass ich LazyLoading betreibe und das es zu so einer Exception kommt. Gibt es keine einfache Alternative, würde ungerne noch nen Stack-Jars da einbauen?!


----------



## Andgalf (4. Mai 2012)

Dann würde ich wahrscheinlich nillehammers Empfehlung folgen und Spring als DI Framework verwenden oder Du implementierst es halt selbst so, dass Du innerhalb eines Requests mit der *gleichen* Session arbeitest und diese entsprechend offen hälst.


----------



## nillehammer (4. Mai 2012)

mavinatic hat gesagt.:
			
		

> Gibt es keine einfache Alternative, würde ungerne noch nen Stack-Jars da einbauen?!


Ok, wir sind hier also in einem Webcontainer unterwegs. Du könntest Dir einen ServletFilter bauen, der die Session für dich öffnet und schließt. Wenn Du nur bei Aufruf bestimmter URLs Daten aus der Datenbank holst, könntest Du das Öffnen auf auf diese URLs einschränken. Hier findest Du ein fast komplett benutzbares Beispiel für solch einen Filter:
https://community.jboss.org/wiki/OpenSessionInView#Using_an_interceptor


----------



## Andgalf (4. Mai 2012)

nillehammer hat gesagt.:


> Hier findest Du ein fast komplett benutzbares Beispiel für solch einen Filter:
> https://community.jboss.org/wiki/OpenSessionInView#Using_an_interceptor



Das ist doch genau das open session in View Pattern über das wir vor drei Posts schon gesprochen haben.


----------



## nillehammer (4. Mai 2012)

Hab ich den Link übersehen?!? Auf jeden Fall kann man damit arbeiten.
Request kommt an, läuft durch den Filter, dann zur Resource, Filterchain fertig, Session wird geschlossen.


----------



## mavinatic (4. Mai 2012)

Was haltet ihr von der Idee, dass ich das in die Interceptors von CXF einbaue?

Planänderung: Ich mache Sie EAGER, weil...es werden ja nur die RuleList abgerufen und das sind garnicht so viele.


----------



## Andgalf (4. Mai 2012)

Ich habe zwar keine Erfahrung mit CXF scheint mir aber die falsche Stelle zu sein. Wenn ich das richtig sehe ist CXF doch das Webservice Framework. Und das würde bedeuten, dass Du ein Problem, was du mit deinem Persistenz Framework hast mit den Mitteln des Webservice Frameworks fixed oder umgehst. Fühlt sich i-wie smelly an.


Was spricht den gegen die Lösung mit dem Servlet Filter? Mach das doch einfach so. Wie ich schon gesagt habe, das ist ein verbreitetes Pattern und somit entsprechend erprobt.


----------



## mavinatic (4. Mai 2012)

Wie ihr merkt, bin ich sehr unerfahren in großen Anwendungen in dem Bereich, aber...ich dachte, bevor der Benutzer auf einen WS zugreift, wird dieser Interceptor aufgerufen, welcher die Session öffnet und wenn der "fertig" ist, wird der Outgoing-Interceptor aufgerufen und dann wird die Session geschlossen. Soweit meine Idee.

Schaut bitte nochmal meien DbFactory-Klasse an, theoretisch kann ich ja da dann meine "Session" rausnehmen, zumindest die Methode "getSession()" in folgenden Methoden: "saveObjec..,update,delete..." und in den Query Methoden?!


----------



## mavinatic (4. Mai 2012)

Hier ist er! Der SessionFilter: 

```
public class SessionFilter implements Filter {
	DbFactory dbFactory = DbFactory.getInstance();
	
	@Override
	public void destroy() {
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		dbFactory.openSession();
		dbFactory.openTransaction();
		
		chain.doFilter(request, response);
		
		dbFactory.closeTransaction();
		dbFactory.closeSession();
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		
	}

}
```
Schön eingebunden:

```
/*
		 * CXFNonSpringServlet & Server 
		 */
		Server httpServer = new Server(8080);
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        httpServer.setHandler(contexts);
        
        ServletContextHandler root = new ServletContextHandler(contexts, "/", ServletContextHandler.SESSIONS);
        Bus bus = BusFactory.getDefaultBus(true);
        CXFNonSpringServlet cxf = new CXFNonSpringServlet();
        cxf.setBus(bus);
        
        ServletHolder servlet = new ServletHolder(cxf);
        servlet.setName("services");
        servlet.setForcedPath("services");
        root.addServlet(servlet, "/services/*");
        
        root.addFilter(SessionFilter.class, "/*", FilterMapping.ALL);
```

Jedoch wirft er nun, wenn ich addRuleToRole ausführe, folgende Exception:

```
org.hibernate.TransactionException: Transaction not successfully started
	at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:200)
	at com.george.crm.core.DbFactory.closeTransaction(DbFactory.java:227)
	at com.george.crm.main.SessionFilter.doFilter(SessionFilter.java:30)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1330)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:941)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:409)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:186)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:875)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
	at org.eclipse.jetty.server.Server.handle(Server.java:349)
	at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:441)
	at org.eclipse.jetty.server.HttpConnection$RequestHandler.content(HttpConnection.java:936)
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:801)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:224)
	at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
	at java.lang.Thread.run(Thread.java:619)
```

Die Klasse dbFactory ist eine Singleton! Bevor ich die Chain weitergebe, öffne ich explizit eine Transaction!


----------

