tutorials.de Buch-Aktion 05/2012
Seite 1 von 2 12 LetzteLetzte
ERLEDIGT
NEIN
ANTWORTEN
16
ZUGRIFFE
12717
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Konstantin Denerz Tutorials.de Gastzugang
    Hallo,

    ich bekomme folgende Exception beim Laden eines Objektes mit Hibernate(Spring):

    Code java:
    1
    2
    3
    4
    5
    6
    7
    
    Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.tutorials.training.springLazyLoading.domain.Person.contacts, no session or session was closed
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
        at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
        at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
        at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
        at de.tutorials.training.springLazyLoading.domain.Main.main(Main.java:28)

    Ich benutze Spring und Hibernate.
    Ich habe ein Objekt person, das ein Attribut contacts vom Typ Set<Contact> hat.
    Dieses person Objekt will ich nun speichern und dann wieder laden. Speichern funktioniert problemlos. Wenn ich dann aber die Kontakte der Person laden will(Lazy Loading) krieg ich die Exception.

    Das person-Objekt:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    package de.tutorials.training.springLazyLoading.domain;
     
    import java.util.Set;
     
    public class Person {
        private long id;
     
        private String firstName;
     
        private String lastName;
     
        private Set<Contact> contacts;
     
        public Set<Contact> getContacts() {
            return contacts;
        }
     
        public void setContacts(Set<Contact> contacts) {
            this.contacts = contacts;
        }
     
        public String getFirstName() {
            return firstName;
        }
     
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
     
        public long getId() {
            return id;
        }
     
        private void setId(long id) {
            this.id = id;
        }
     
        public String getLastName() {
            return lastName;
        }
     
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
     
    }
    Mapping zu Person:
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="de.tutorials.training.springLazyLoading.domain">
      <class name="Person">
      <id name="id" unsaved-value="0"><generator class="native"></generator> </id>
      <property name="firstName" type="string"></property>
      <property name="lastName" type="string"></property>
      <set name="contacts" cascade="all" lazy="true">
          <key column="person_id"></key>
          <one-to-many class="Contact"/>
      </set>
      </class>
    </hibernate-mapping>
    Das contact Objekt:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    public class Contact {
        
        public Contact(String type, String value) {
            super();
            this.type = type;
            this.value = value;
        }
     
        public Contact() {
            super();
            // TODO Auto-generated constructor stub
        }
     
        private long id;
     
        private String type;
     
        private String value;
     
        public long getId() {
            return id;
        }
     
        private void setId(long id) {
            this.id = id;
        }
     
        public String getType() {
            return type;
        }
     
        public void setType(String type) {
            this.type = type;
        }
     
        public String getValue() {
            return value;
        }
     
        public void setValue(String value) {
            this.value = value;
        }
     
    }

    Mapping des Contacts:
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="de.tutorials.training.springLazyLoading.domain">
      <class name="Contact">
      <id name="id" unsaved-value="0">
      <generator class="native"></generator>
      </id>
      <property name="type" type="string"></property>
      <property name="value" type="string"></property>
      </class>
    </hibernate-mapping>

    Hier die Serviceklassen:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    
    //PersonAdministrationService
    package de.tutorials.training.springLazyLoading.service;
     
    import de.tutorials.training.springLazyLoading.domain.Person;
    import de.tutorials.training.springLazyLoading.service.dao.IPersonDao;
     
    public class PersonAdministrationService {
        private IPersonDao personDao;
     
        public IPersonDao getPersonDao() {
            return personDao;
        }
     
        public void setPersonDao(IPersonDao personDao) {
            this.personDao = personDao;
        }
        public void savePerson(Person person){
            this.getPersonDao().makePersistent(person);
        }
        public Person getPersonById(long id){
            return this.getPersonDao().getPersonById(id);
        }
    }
     
    // IPersonDao
    package de.tutorials.training.springLazyLoading.service.dao;
     
    import de.tutorials.training.springLazyLoading.domain.Person;
     
    public interface IPersonDao {
        void makePersistent(Person person);
        Person getPersonById(long id);
    }
     
    //PersonDaoHibernateImpl
    package de.tutorials.training.springLazyLoading.service.dao.internal;
     
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
     
    import de.tutorials.training.springLazyLoading.domain.Person;
    import de.tutorials.training.springLazyLoading.service.dao.IPersonDao;
     
    public class PersonDaoHibernateImpl extends HibernateDaoSupport implements IPersonDao {
     
        public Person getPersonById(long id) {
            
            return (Person) this.getHibernateTemplate().get(Person.class, id);
        }
     
        public void makePersistent(Person person) {
            
            this.getHibernateTemplate().save(person);
        }
     
    }

    Und die Spring Konfiguration:
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
               [url]http://www.springframework.org/schema/aop[/url] [url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]
               [url]http://www.springframework.org/schema/tx[/url] http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
        
     
        <bean name="propertyConfigurer"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="conf/jdbcOracle.properties"/>
        </bean>
        
        
        <bean name="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
        
        
         <bean name="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="myDataSource"></property>
            <!-- O/R-Mappings -->
            <property name="mappingResources">
                <list>
                    <value>de/tutorials/training/springLazyLoading/domain/Contact.hbm.xml</value>
                    <value>de/tutorials/training/springLazyLoading/domain/Person.hbm.xml</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.generate_statistics">false</prop>
                    <prop key="hibernate.hbm2ddl.auto" >create-drop</prop>
                </props>    
            </property>
        </bean>
        
        
        <bean name="personDao" class="de.tutorials.training.springLazyLoading.service.dao.internal.PersonDaoHibernateImpl">
            <property name="sessionFactory" ref="mySessionFactory"/>
        </bean>
        
         <bean name="myPersonAdministrationServiceTarget" class="de.tutorials.training.springLazyLoading.service.PersonAdministrationService">
            <property name="personDao" ref="personDao"></property>
         </bean>
         
          <bean name="myTransactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="mySessionFactory"/>
          </bean>   
          
         
           
           <bean id="myPersonAdministrationService"
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
     
            <property name="transactionManager" ref="myTransactionManager"/>
            <property name="target" ref="myPersonAdministrationServiceTarget" />
            <property name="transactionAttributes">
                <props>
                    <prop key="save*">PROPAGATION_REQUIRED</prop>
                    <prop key="get*">PROPAGATION_REQUIRED</prop>
                </props>
            </property>
     
        </bean>
          
    </beans>

    Die Main-Methode:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    package de.tutorials.training.springLazyLoading.domain;
     
    import java.util.HashSet;
    import java.util.Set;
     
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
     
    import de.tutorials.training.springLazyLoading.service.PersonAdministrationService;
     
    public class Main {
            public static void main(String[] args){
                ApplicationContext applicationContext = new FileSystemXmlApplicationContext("conf/applicationContext.xml");
                PersonAdministrationService personAdministrationService= (PersonAdministrationService) applicationContext.getBean("myPersonAdministrationService");
                
                Person person =new Person();
                person.setFirstName("Konstantin");
                person.setLastName("Denerz");
                
                Set<Contact> contacs = new HashSet<Contact>();
                contacs.add(new Contact("email_privat","kde@tutorials.de"));
                person.setContacts(contacs);
                
                personAdministrationService.savePerson(person);
                
                System.out.println(personAdministrationService.getPersonById(1).getFirstName());
            
                System.out.println(personAdministrationService.getPersonById(1).getContacts().iterator().next().getValue());
                
            
            }
    }
    Hat jemand eine Idee warum das Lazy Loading nicht funktioniert?

    Gruß Konstantin
     

  2. #2
    Konstantin Denerz Tutorials.de Gastzugang
    Warum die Exception geworfen wird, das weiß ich. Diese Session ist zu, aber wie löse ich es?
     

  3. #3
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.886
    Blog-Einträge
    29
    Hallo,

    änder doch mal spaßeshalber das Mapping der Contacts in Person in:
    Code xml:
    1
    
    <set fetch="join" name="contacts" cascade="all" lazy="false">
    Das ändert das Ladeverhalten so, dass zum einen beim Laden einer Person auch die Kontakte sofort mitgeladen werden und zum anderen das keine zusätzlichen Selects sondern nur ein "großer" Join gemacht werden.

    Ansonsten gibts eben die Möglichkeit die Collection (in einer entsprechenden ServiceMethode die noch in der Transaktion ist) mit Hibernate.initialize(person.getContacts()); explizit zu initialisieren.

    Weiterhin wäre es denkbar eine (transaktionale) Service Methode zu haben die entsprechend alle Daten vorlädt.

    Außerdem gäbs noch die Möglichkeit mit "langlaufenden" Transaktionen...

    Gruß Tom
     
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

  4. #4
    frank123 frank123 ist offline Grünschnabel
    Registriert seit
    May 2007
    Beiträge
    1
    Hallo zusammen,

    ich habe das gleiche Problem. In anderen Foren hab ich gelesen, dass durch die Aufnahme der OpenSessionInViewFilter in der web.xml sichergestellt werden soll, dass für einen Request immer eine komplette Session verwendet wird. Das Funktioniert bei mir leider nicht. Anscheinend wird bei mir Spring zweimal initialisiert (?) und es existieren somit min. zwei Sessionfactorys, HibernateTemplates, etc. im laufenden System. Die beiden Initialisierungen werden durch zwei deployte Servlets (org.springframework.web.context.ContextLoaderServlet, org.apache.struts.action.ActionServlet) ausgelöst.

    Leider bin ich relativ neu im Spring-Umfeld tätig und habe ein bestehendes System übernommen. Hier die wichtigsten Daten:

    Spring 1.2.9, Hibernate 2.1

    Auszug Web.xml
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    <servlet>
      <servlet-name>SpringContextServlet</servlet-name>
      <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
      <load-on-startup>2</load-on-startup>
     </servlet>
    <servlet>
      <servlet-name>Struts Servlet</servlet-name>
      <!--servlet-class>org.apache.struts.action.ActionServlet; FrontController ist eine subclass von ActionServlet</servlet-class-->
      <servlet-class>biz.myera.era.web.servlet.FrontController</servlet-class>
      <init-param>
       <param-name>application</param-name>
       <param-value>ApplicationResources</param-value>
      </init-param>
      <init-param>
       <param-name>config</param-name>
       <param-value>/WEB-INF/struts-config.xml</param-value>
      </init-param>
      <init-param>
       <param-name>debug</param-name>
       <param-value>6</param-value>
      </init-param>
      <init-param>
       <param-name>detail</param-name>
       <param-value>6</param-value>
      </init-param>
      <load-on-startup>3</load-on-startup>
     </servlet>

    Auszug application-context.xml
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
        <!-- Hibernate SessionFactory Definition -->
        <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
            <property name="mappingResources">
                <list>              
                    <!-- Business Object beans -->
    ...                     
                </list>
            </property>     
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
                    <prop key="hibernate.cache.provider_class">net.sf.hibernate.cache.HashtableCacheProvider</prop>
                </props>
            </property> 
            
            <property name="dataSource">
                <ref bean="dataSource"/>
            </property>
        </bean>
        
        <!-- Spring Data Access Exception Translator Defintion -->
        <bean id="jdbcExceptionTranslator" class="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator"> 
            <property name="dataSource"><ref bean="dataSource"/></property> 
        </bean> 
     
        <!-- Hibernate Template Defintion -->
        <bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate"> 
            <property name="sessionFactory"><ref bean="sessionFactory"/></property> 
            <property name="jdbcExceptionTranslator"><ref bean="jdbcExceptionTranslator"/></property> 
        </bean> 
     
        <!-- Hibernate Transaction Manager Definition -->
        <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
            <property name="sessionFactory"><ref local="sessionFactory"/></property>
        </bean>

    Schon mal vielen Dank für jegliche Unterstützung.

    Best
    Frank
     

  5. #5
    mquasten mquasten ist offline Rookie
    Registriert seit
    Dec 2007
    Beiträge
    6
    Ich hatte das Problem ebenfalls innerhalb von JUnitTests mehrfach. Ich verwende JPA , Hibernate Entitymanager un Spring. Lösen konnte ich das Problem nur, weil ich zusätzlich AspectJ und LoadtimeWeaving vewende. damit habe ich die Möglichkeit auch ausserhalb von Springbeans in normalen Pojos Einfluss auf Transaktionen zu nehmen. Ich habe schicht und einfach über meine JUnitTests:
    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public void testEBankingAccountSaveGuard() throws AccountException {
    ...
    } geschrieben und in meine aop.xml

    <include within="*..itest.*" />

    eingetragen.



    Was ich als Propagation angebe ist relativ egal, Hauptsache Transactional steht da. Der commit detached die Entities und schließt offensichtlich die session. Für Webanwendungen gibt es ein Filter SessionInRequestfilter, das irgendwie in der web.xml eingetragen werden muss. Als Lösung ausserhalb von Webanwendungen fällt mir nur ein, auf Lazy-Loading zu verzichten, oder eben AspectJ und Transactional versuchen. Dadurch kommt man sowieso erst wieder zurück zur Objektorientierung ...

    Spring (vom ursprünglichen Ansatz her) scheint mir da ein paar recht üble Schwächen vom Konzept her gehabt zu haben. Es gibt Dinge, die sich nicht mit Sigeltons und Factories alleine lösen lassen
     

  6. #6
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Sorry, wenn ich so direkt bin, aber was du da schreibst, ist zum großteil Blödsinn. Wenn die Transaktionen richtig konfiguriert sind und das Hibernate Mapping korrekt ist, fliegen keine LazyLoadingExceptions.

    Auch dein Arbeiten mit den JUnit Tests, zeugt davon, dass deine Annahmen Spring gegenüber auf Unwissenheit beruhen. Für Integrationstests gibt es AbstractTransactionalSpringContextTest, die dafür sorgt, dass jede Testmethode automatisch transaktional wird und am Ende ein Rollback ausgeführt wird.

    Wie tut denn dein Code, wenn er produktiv ausgeführt wird? Wo ist denn dann dein Transaktionshandling, wenn der Unittest fehlt?

    Ich hab einfach das Gefühl da werkelt jemand mit DIngen, die er nicht wirklich verstanden hat, macht dadurch Fehler und stellt dann die These auf "man häte vom Ansatz her ein paar recht üble Schwächen gehabt". Ich bitte dich, wenn Spring bei solch primitiven Dingen Schwächen hätte, wäre es wohl kaum so weit verbreitet wie es ist.

    Vielleicht kannst du ja nochmal etwas Code und Konfiguration posten, die bei dir zu den LLEs führen. Vielleicht finden wir noch die ein oder andere Stelle, an der ein Fehlerchen zu finden ist.

    Gruß und einen guten Rutsch...

    REINHAUN!
     

  7. #7
    mquasten mquasten ist offline Rookie
    Registriert seit
    Dec 2007
    Beiträge
    6
    Da will ich mal antworten,

    AbstractDependencyInjectionSpringContextTests ist sicher eine schöne Sache, weil er mir das manuelle besorgen des ApplicationContests und das getBean abnimmt, was Transactionen angeht, spielt es keine Rolle, ob ich JUnitTest oder AbstractDependencyInjectionSpringContextTests oder was sonst nehme (Ich nutze aus Faulheit AbstractDependencyInjectionSpringContextTests)

    Alle Mechanismen von Spring funktionieren nur mit managedBeans, d.h. dem was in der BeanKonfiguration konfiguriert ist. Alles was mit new oder sonst wie erzeugt wird, ist assen vor.

    Ich rede auch von Schwächen, nicht von Fehlern. Martin Fowler beschreibt in seinem Antipattern "The anemic Domainmodell" die "Vorzüge" von J2EE. All das läßt sich auch mit Springerreiche. Wohl auch deshalb gibt es seit Spring 2.x Configurable. Geanu darum, weil es das gibt, bin ich heute überzeugt von Spring und genau aus dem Grund verwende ich es, wann immer möglich.

    Es ist doch recht Klar, was passiert. JUnitTests (auch AbstractDependencyInjectionSpringContextTests ) sind keine managedBeans. Die Session bleibt genau so lange offen, wie die Methode auf dem BusinessBean (bei mir die Methode auf dem Pojo) ausgeführt wird. Greift man danach mit einem getter auf ein noch nicht aus der Datenbank gelesenes Object zu, ist die Session schon zu und das war es dann. Mit Transactional (Propagation.NOT_SUPPORTED) halte ich lediglich die Session solange offen, bis die Methode des JUnitTests zu ende ist (auf Transaktionsverhalten nehme ich in der BusinessMethode einfluss.

    Ob ich Persitent Anemic Object oder Persistent Domain Object verwende ist meine Designentscheidung (ich nehme wenn immer möglich Persistent Domain Object . Noch bis Java EE 1.4 gab es laut Spezifikation von J2EE nur Persitent Anemic Object. Das das heute auch da anders ist, ist Verdienst von Spring und Co. Ebenfalls Verdienst von Spring und CO ist JPA und EJB3.0. Das was vorher war ist (nur meine persönliche Meinung) höchst suboptimal. HIer hat Spring einiges erreicht. Trotzdem. Zaubern kann auch Spring nicht und an nicht managedBeans kommt man nur mit AspectJ und vergleichbarem heran.

    Wie ich es in Produktion löse:

    1. LazyLoading wäre bei mir in der Tat entbehrlich

    2. ich würde das SessionInRequestFilter ausprobieren

    3. Ich würde die JSF-FormBean weaven und mit Transactional versehen
     

  8. #8
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Na da haben wir doch schon etwas mehr fundiertes Futter um ins Gespräch zu kommen ...

    Zitat Zitat von mquasten Beitrag anzeigen
    Da will ich mal antworten,

    AbstractDependencyInjectionSpringContextTests ist sicher eine schöne Sache, weil er mir das manuelle besorgen des ApplicationContests und das getBean abnimmt, was Transactionen angeht, spielt es keine Rolle, ob ich JUnitTest oder AbstractDependencyInjectionSpringContextTests oder was sonst nehme (Ich nutze aus Faulheit AbstractDependencyInjectionSpringContextTests)
    Ich habe nie von AbstractDependencyInjectionSpringContextTest (im folgenden ADISCT) geschrieben, sondern von AbstractTRANSACTIONALSpringContextTest (im folgenden ATSCT). Dieser ist für Integrationstests gedacht und umschließt jede Testmethode mit einer Transaktion, die im Normalfall nach der Ausführung gerollbackt wird.

    Ich versteh halt den Sinn deines Unittests nicht wirklich. Entweder willst du deine Transaktionskonfiguration testen, dann reicht ADISCT indem du zusätzlich das Konfigurationsfile mit der Transaktionskonfiguration lädst und nach der Ausführung den Transaktionsmanager fragst, was er denn ausgeführt hat. Oder du machst nen Integrationstest auf DAOs z.B. an denen selbst keine Transaktionskonfiguration hängt und nutzt dann aber ATSCT.

    Alle Mechanismen von Spring funktionieren nur mit managedBeans, d.h. dem was in der BeanKonfiguration konfiguriert ist. Alles was mit new oder sonst wie erzeugt wird, ist assen vor.
    Das ist nicht wirklich korrekt. Es gibt seit Spring 2.0 @Configurable, wie du ja auch selbst unten schreibst. Zum anderen stellt sich mir die Frage, warum das Thema non-managedBeans so ein großes für dich ist. Ich vermute wegen der Unittests, aber dazu hab ich ja grad schon was geschrieben. Und Unittest heißt Unittest, weil da die Klasse als Unit getestet wird, dependencies gemockt usw. D.h. da spielt Transaktionalität keine Rolle. Und für Integrationstests, gibts wie gesagt ATSCT.

    Ich rede auch von Schwächen, nicht von Fehlern. Martin Fowler beschreibt in seinem Antipattern "The anemic Domainmodell" die "Vorzüge" von J2EE. All das läßt sich auch mit Springerreiche. Wohl auch deshalb gibt es seit Spring 2.x Configurable. Geanu darum, weil es das gibt, bin ich heute überzeugt von Spring und genau aus dem Grund verwende ich es, wann immer möglich.
    Hm...okay, ich versteh den Punkt, aber das hat doch mit Spring an sich nichts zu tun. Welches andere Programmiermodell unterstützt denn Objektorientierung besser? Überhaupt, OO hat doch keinen Wert an sich. Ich bin auch ein Freund von DDD ala Evans, aber das weit verbreitete Layering (und damit eher prozeduraler Code) hat sich schon bewährt und ist sicher nicht grundfalsch.

    Es ist doch recht Klar, was passiert. JUnitTests (auch AbstractDependencyInjectionSpringContextTests ) sind keine managedBeans. Die Session bleibt genau so lange offen, wie die Methode auf dem BusinessBean (bei mir die Methode auf dem Pojo) ausgeführt wird. Greift man danach mit einem getter auf ein noch nicht aus der Datenbank gelesenes Object zu, ist die Session schon zu und das war es dann. Mit Transactional (Propagation.NOT_SUPPORTED) halte ich lediglich die Session solange offen, bis die Methode des JUnitTests zu ende ist (auf Transaktionsverhalten nehme ich in der BusinessMethode einfluss.
    Siehe oben... genau das tut ATSCT.

    Ob ich Persitent Anemic Object oder Persistent Domain Object verwende ist meine Designentscheidung (ich nehme wenn immer möglich Persistent Domain Object . Noch bis Java EE 1.4 gab es laut Spezifikation von J2EE nur Persitent Anemic Object. Das das heute auch da anders ist, ist Verdienst von Spring und Co. Ebenfalls Verdienst von Spring und CO ist JPA und EJB3.0. Das was vorher war ist (nur meine persönliche Meinung) höchst suboptimal. HIer hat Spring einiges erreicht. Trotzdem. Zaubern kann auch Spring nicht und an nicht managedBeans kommt man nur mit AspectJ und vergleichbarem heran.
    Ich glaub nicht, dass die J2EE Spech dir vorgibt oder vorgegeben hat, welches Pattern du für deine Domäne implentieren sollst. Wie du ja selber sagst, geht J2EE mit Spring locke ohne EJB und damit also auch DDD. Oder zwingt dich jemand EntityBeans bzw. JPA zu benutzen, ausser dein Chef vielleicht?

    Zum Thema managed Beans hab ich ja schon was geschrieben. Und AspectJ ist ja auch nicht grad der Vorort der Hölle, alles halb so schlimm.

    Wie ich es in Produktion löse:

    1. LazyLoading wäre bei mir in der Tat entbehrlich

    2. ich würde das SessionInRequestFilter ausprobieren

    3. Ich würde die JSF-FormBean weaven und mit Transactional versehen
    Ich meine eher, wie deine Transaktionskonfiguration bezgl. deines zu testenden Services gemacht ist. Mit ATSCT sollte es wie gesagt reichen, einfach für diese Testklasse die Transaktionskonfiguration nicht zu laden und das Standardverhalten von Spring zu nutzen.

    Gruß und ein frohes Neues...

    REINHAUN
     

  9. #9
    mquasten mquasten ist offline Rookie
    Registriert seit
    Dec 2007
    Beiträge
    6
    Es ist schon richtig, das Objektorientierung an sich nur ein technisches Paradigma ist und an sich noch keinen Wert darstellt. Und falsch ist auch, das sich nicht alles auch mit Sigeltons und Factories lösen läßt: Es muss sich lösen lassen, weil sich alles auch in einer prozeduralen Sprache wie C lösen ließe aber mit zum Teil auch üblen Folgen. Niemand zwingt mich derzeit zu irgendetwas. Man muss nur gewisse Dinge "ertragen", weil Tatsachen vorhanden sind (geschaffen wurden) mit denen man (oder jemand anderer) sich dann arangieren muss ...

    Der JUnitTest ist doch gar nicht das Problem: Irgendwo stand etwas von Sping 1.2.x und Transactionales Verhalten wird in Spring doch wohl über Aspekte dazukonfiguriert? Wie sonst auch? Da wollte ich eigentlich lediglich anmerken, dass sich durchaus Zenarien ergeben können das jemand zugreift, wenn der Interceptor die Session geschlossen hat. Spring 1.x hat nichts von den Möglichkeiten über die wir jetzt diskutieren wollen.


    Mein Transaktionsverhalten will ich ausschließlich über Transactional-Annotation, weil mir xml-Konfigfiles von Hause aus zu wieder sind

    ....

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="oracle"></property>
    </bean>

    <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf" >
    <property name="transactionManager" ref="jpaTransactionManager" />
    </bean>

    Die BusinessMethode definiert das Transactionsverhalten, der DAO schon soetwas wie ein Defaultverhalten, das die Business-Methode aber in jedem Fall überschreibt. Der JUnitTest soll zeigen, ob meine Werke arbeiten und mir helfen sie zu erstellen (besonders das letzte). Transaktionsverhalten hat darin rein gar nichts 8nach der THerorie) zu suchen

    Die Diskussionen sind aber jetzt schon am eigentlichen Thema vorbei fürchte ich und für den Rest der Welt vermutlich nicht allzu spannend.

    Auch gebe ich zu, das für mich die direkte Verwendung von Hibernate derzeit Geschichte ist, ich also nicht sehr tief mit deren Konfiguration mehr vertraut bin Zugegeben rein spekulativ, weil ich glaube, das sich in Zukunft vielleicht noch etwas anderes neben Hibernate entwickelt, das einmal eine Alternative darstellen könnte und ebenfalls die JPA Schnittstelle bedient. Und irgendwann wird sich auch Spring erledigen, glaube ich. Derzeit benutzen wir es doch eigentlich nur, um Mittel zur Verfügung zu haben, die in Java fehlen und die wir dann durch xml Konfiguration (und Runtime-Exceptions) erkaufen. Auch dabei ist nur wichtig, das wir Strategie gegenüber Vererbung bevorzugen (in gewissem Rahmen), derzeit tun wir das, bis wir etwas besseres oder anderes haben. Die Frameworks sind mir egal. Die sind vergänglich und austauschbar, wichtig sind einzig und alleine die Prinzipien. Spring ist derzeit auch mein Favorit, aber das wird sich vermutlich ändern. J2EE ist Darwinismus pur. Spannend ist, was am Ende wirklich das beste ist. Und um das zu beurteilen, muss ich auch die Schwächen kritisch sehen.

    Ich glaube, das am Ende Dependency Injection und AOP in eine Sprache x eingehen werden. Ich glaube auch, das sich der ganze xml-Konfigurationsmist erledigen wird und ebenfalls integraler Bestandteil in Form von Annotations (oder vergleichbarem) werden wird. Und zurück zum Ausgang: Objektorientierung ist kein Wert an sich: Aber Evolution als solche ist eines der höchsten Werte: Alternative wäre es an Stelle dieser Diskussionen vor Säbelzahntigern auf Bäume zu fliehen... Auch dieser Vergleich hat eine gewisse Arroganz, indem er unterstellt, das die Evolution schon ihre Vollendung gefunden hätte, was relatistisch betrachtet wohl naiv wäre

    Spring ist also vermutlich nur ein Zwischenschritt hin zu einer neuen Sprache oder hin zu einer Veränderung bzw. Anpassung der Pattern und Idiome und wird am Ende wegoptimiert werden.

    Demnächst wird es ein Spring geben, das ganz ohne Konfigfiles auskommt und rein auf Basis von Annotions arbeitet.

    Das beste OO-Modell ist das von Java, C++ etc ohne alle mehr oder weniger selbstgemachten "Antipattern" , die nur versuchen wieder C daraus zu machen !
     

  10. #10
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Ich machs kurz: Transaktionales verhalten von JUnit Testmethoden ist mit ATSCT möglich und die Klasse gibt es laut Api Doc seit 1.1.1. Das programmierst du von Hand nach... Deswegen war ich halt arg verwirrt...

    Gruß
    Ollie
     

  11. #11
    mquasten mquasten ist offline Rookie
    Registriert seit
    Dec 2007
    Beiträge
    6
    Ich programmiere Transaktionen nicht von Hand, zumindest nicht in den Umgebungen und nicht freiwillig.

    Wollen wir es mal etwas umformulieren: Wir haben eine BusinessMethode auf einem SpringBean, die als Wert ein Hibernate oder JPA-Pojo zurückgibt und die wir dann in einem sagen wir mal Servlet aufrufen, d.h. nicht in einer SpringBean. Dieses Pojo hat eine Collection die mit LazyLoad konfiguriert ist und auf die ich mittels getter (nachher zugreife)


    Pojo pojo = myBean.myMethod(...);
    System.println(pojo.getLazyLoadedCollection() );

    Meinetwegen holen wir uns das Bean mit WebApplicationContextUtil oder wie dieses Ding auch immer heißt, oder über den ApplikationContext direkt

    Der Aufruf auf des Getter auf diese Collection ist das interessante. Was passiert? Vermutlich LazyLoadingException ?!
     

  12. #12
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Ich versteh schon was du vorhast. Das ist ja auch alles richtig, nur fliegt im Testcase keine LLE, wenn du von der richtigen Klasse erbst.

    Ich weiß nicht, ob du bewusst nicht verstehen willst, was ich schreibe oder was sonst das Problem ist. Dein Ausgangsproblem schien es zu sein, dass in einer unmanaged Bean (deine m Unittest) die Transaktion - wenn du sie um die Businessmethode gelegt hast - logischerweise zu Ende ist. Dies umgehst du, in dem du mit @Transactional auch die Unittestmethode transaktional machst, was einfach den Transaktionsraum "aufweitet". Und jetzt schreib ich es gern noch zum dritten mal, genau dies tut ATSCT out of the Box für JEDE Testmethode. Schau doch einfach mal in die ApiDoc:

    http://static.springframework.org/sp...textTests.html

    Gruß
    Ollie
     

  13. #13
    mquasten mquasten ist offline Rookie
    Registriert seit
    Dec 2007
    Beiträge
    6
    Auch wenn ich alt und stur und manchmal unbelehrbar bin: Ich habe es verstanden. Nur war der JUnitTest kein Problem, für das ich eine Lösung gesucht habe, sondern ein Phänomen, dessen Ursachen ich für sehr weitreichend halte:

    Bei LazyLoading und meinem so geliebten Persistent DomainModel muss man höllisch aufpassen. Unsere Diskussion hat sogar noch etwas konstruktives, weil sie meinen Verdacht, den ich hatte ja letztendlich bestätigt (leider).

    Jetzt beziehe ich mich auch jetzt nicht auf den JUnitTest, sondern auf das was vorher schon zu dem Thema stand: Mögliche Lösungsansätze sind:

    1) Das schon erwähnte Filter, was das Problem aber nicht in allen Umgebungen löst und wo ich auch bezweifle, das es für nicht mangedBeans funktioniert. Aber mir fehlt damit die Erfahrung.

    2) LazyLoading abhacken

    3) Einen Weg finden über Konfigurationsmöglichkeiten der sessionfactory, wo ich mich nicht besonders auskenne. Was allenfalls irgendwelche Timeouts sein könnten, die die Session erst zeitversetzt schließen, was sicher keine wirkliche Lösung zu sein scheint.

    4) AspectJ und Loadtime Waeving um den Preis erheblicher zusätzlicher Komplexität

    5) nicht von aussen auf die Collections zugreifen, d.h. ein rein serviceorientierter Ansatz (positiv formuliert, ich nenne es: einen rein prozeduralen Ansatz!)

    Ich werde in Zukunft sehr viel vorsichtiger sein, und nach Möglichkeit noch stärker Prototyp basiert arbeiten, um nicht in diese Fallen zu laufen Vielleicht ist es auch an der Zeit sich wieder mal etwas neuem zuzuwenden und Java und Spring lebewohl zusagen?
     

  14. #14
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Okay... wie gesagt, dein erster Post machte halt den Eindruck, als wäre dein Hauptproblem der Unittest.

    Grundsätzlich führt die Problematik zu dem Thema, ob Services überhaupt Domänenobjekte an Clients zurückgeben sollten. Dies hat nämlich zur Folge, dass der Client recht eng an den Server gekoppelt ist. Ausserdem zieht das die hier nun ausführlich diskutierte LL Problematik nach sich.

    Lösen könnte man das ganze, in dem der Service DTOs zurückgibt die eine clientspezifische Sicht auf das Domänenmodell repräsentieren. Wenn du dazu noch ein Rich Domain Model verwendest, hätte der Servicelayer auch wieder die Aufgabe, die er eigentlich hat: Demarkation von Transaktionsgrenzen und Aufbereitung der Daten für den Client. Drawback bei der ganzen Geschichte ist dann natürlich, dass die DTOs zu weiten Teilen identisch zu den Domänenklassen sind, nur eben mehr "anemic".

    Bisher habe ich folgende Erfahrungen gemacht: Laufen Client und Server in der gleichen VM (klassische Webanwendung), lohnt es meiner Meinung nach nicht, einen Extra DTO Laxer einzuführen. Hier machen OpenSessionInView und ähnliche Paradigmen Sinn.

    Bei physisch verteilten Anwendungen (RichClient + Server Backend) macht es Sinn mit DTOs zu arbeiten und im Service die Daten vorzubereiten, auch des Netzwerktraffics wegen.

    Den Aufwand, DTOs zu schreiben und zu pflegen umgeht man meist durch modellgetriebene Ansätze in solchen Projekten (aka. Codegenerierung).
     

  15. #15
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.886
    Blog-Einträge
    29
    Hallo,


    Grundsätzlich führt die Problematik zu dem Thema, ob Services überhaupt Domänenobjekte an Clients zurückgeben sollten. Dies hat nämlich zur Folge, dass der Client recht eng an den Server gekoppelt ist. Ausserdem zieht das die hier nun ausführlich diskutierte LL Problematik nach sich.
    Eine Möglichkeit den Client vom bestimmten Serverseitigen Implementierungen von Domain Objects zu entkoppeln ist
    der, entsprechende Interfaces zu definieren und dafür Proxies auf Clientseite zu erzeugen.
    Siehe: http://www.tutorials.de/forum/java/2...-erzeugen.html
    Bei diesen Proxies wird dann zwischen einfachen get/set und "Business Logic" Calls zu unterscheiden.
    Die get/set Calls werden auf dem Client direkt behandelt. Aufrufe von "Business Methods" werden dann entweder direkt Remote abgesetzt
    oder je nach Konfiguration verzögert, asynchron.

    Über diesen Ansatz kann man über entsprechende InvocationHandler / Interceptoren sehr flexible weitere Funktionalität in die Modelle
    stecken (e.g. LazyLoading....) ohne das der Anwender was davon mitbekommt. D.h. der Anwendungsentwickler arbeitet nur mit den Interfaces und weis wie er
    die Modelle Insatziieren kann und kann ganz einfach mit arbeiten, ohne sich über irgendwelche interna Gedanken machen zu müssen.

    ...
    Drawback bei der ganzen Geschichte ist dann natürlich, dass die DTOs zu weiten Teilen identisch zu den Domänenklassen sind,
    nur eben mehr "anemic".
    Wenn man diesem Interface-Ansatz folgt hat man im Endeffekt nur eine Implementierung... und ein Interface, anstatt zwei "Implementierungen".

    Den Aufwand, DTOs zu schreiben und zu pflegen umgeht man meist durch modellgetriebene Ansätze in solchen Projekten (aka. Codegenerierung).
    Bei der Interface-basierten Variante erledigt die IDE, einen Großteil der Arbeit


    Gruß Tom
     
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

Ähnliche Themen

  1. Spring und Hibernate
    Von y0dA im Forum Enterprise Java (JEE, J2EE, Spring & Co.)
    Antworten: 0
    Letzter Beitrag: 21.10.09, 14:30
  2. Antworten: 10
    Letzter Beitrag: 27.03.09, 13:24
  3. Spring und Hibernate - Testen
    Von y0dA im Forum Enterprise Java (JEE, J2EE, Spring & Co.)
    Antworten: 4
    Letzter Beitrag: 28.05.08, 19:10
  4. JSF, Spring, Hibernate --> Struktur!
    Von crossib im Forum Enterprise Java (JEE, J2EE, Spring & Co.)
    Antworten: 9
    Letzter Beitrag: 03.03.08, 07:15
  5. DTO, Spring und Hibernate
    Von DerGrinsemann im Forum Java
    Antworten: 2
    Letzter Beitrag: 08.06.07, 11:41