2012-10-18 13 views
5

Mam problem z uzyskaniem sesji na anonimowej klasie wewnętrznej w stanie hibernacji z fabryką sesji wiosennej. Oto kod:Sesja hibernacji wewnątrz klasy wewnętrznej

public class DomainDaoImpl extends BasicDaoImpl<Domain> implements Iterable<Collection<Domain>> { 

... 

@Override 
public Iterator<Collection<Domain>> iterator() { 
    return (new Iterator<Collection<Domain>>() { 
     private int counter = 0; 
     public static final int LIMIT = 100; 

     ... 

     @Override 
     @Transactional(readOnly = true) 
     public Collection<Domain> next() { 
      final Criteria criteria = getCurrentSession().createCriteria(Domain.class); 
      final LinkedHashSet<Domain> result = new LinkedHashSet<Domain>(); 

      List resultList = null; 
      while (!(resultList = criteria.list()).isEmpty()) { 
       criteria.setFirstResult((counter++ * LIMIT) + 1); 
       criteria.setMaxResults(LIMIT); 
       result.addAll(resultList); 
      } 
      return result; 
     } 

     ... 
    }); 

Problem jest org.hibernate.HibernateException: Nie Sesja znalezionych dla bieżącego wątku to dzieje się zwykle, gdy metoda DAO nie jest w transakcji. Więc jak to działa z wewnętrzną klasą?

Odpowiedz

1

Myślę, że definiowanie @Transactional(readOnly = true) na poziomie klasy wewnętrznej, wiosna nie będzie w stanie wykryć i zastosować aspekt transakcji nad nim. więc to nie zadziała na pewno.

ale myślę, że jeśli napiszesz coś jak poniżej może pracować nie 100% pewny (wątpię raz wywołać transakcję metodą iterator jest zamknięty)

@Override 
@Transactional(readOnly = true) 
public Iterator<Collection<Domain>> iterator() { 
... 
} 

innej opcji można pozwolić rozmówcy jest odpowiedzialny za transakcję lub napisz metoda opakowaniowa ponad iterator(), taka jak getAllDomain() i zastosować transakcję do tej metody.

Rozwiązanie który pracował (wspomniane w komentarzach)

może być możesz zrobić jakieś poprawki w getCurrentSession() jak getCurrentSession() z SessionFactory jeśli nie są dostępne użyj openSession(), oczywiście trzeba zamknij je ręcznie, jeśli otwarta jest nowa sesja .

+0

Dzięki za odpowiedź, wypróbowałem to, zanim poprosiłem bez powodzenia. I nie chcę, aby osoba dzwoniąca była odpowiedzialna za zarządzanie transakcjami, ponieważ nie chcę, aby @Transactional znajdował się w mojej warstwie usług. –

+1

Możliwe, że możesz zrobić łatkę w 'getCurrentSession()' jak getCurrentSession() z sessionFactory, jeśli nie jest dostępna to użyj 'openSession()', oczywiście musisz zamknąć ją ręcznie, jeśli otworzono nową sesję. –

+0

to działało :-) mimo wszystko, zaczekajmy, zobaczmy, czy jest inna propozycja –

1

Można skonfigurować aspekt obciążenia czasu tkania

Oto prosty przykład jak to zrobić

kontekst Wiosna

<?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:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> 

    <context:component-scan base-package="org.foo.bar" /> 
    <context:annotation-config /> 
    <context:load-time-weaver /> 

    <tx:annotation-driven mode="aspectj" proxy-target-class="true"/> 

    <jdbc:embedded-database id="dataSource"> 
     <jdbc:script location="classpath:schema.sql"/> 
    </jdbc:embedded-database> 

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
     <property name="dataSource" ref="dataSource"/> 
     <property name="annotatedClasses"> 
      <list> 
       <value>org.foo.bar.MyEntity</value> 
      </list> 
     </property> 
    </bean> 

    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
     <property name="sessionFactory" ref="sessionFactory"/> 
    </bean> 

</beans> 

klasy Entity

pakiet org.foo.bar;

import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.Table; 

@Entity 
@Table(name = "ENTITY") 
public class MyEntity { 

    @Id 
    private long id; 
    private String name; 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @Override 
    public String toString() { 
     final StringBuilder sb = new StringBuilder(); 
     sb.append("MyEntity"); 
     sb.append("{id=").append(id); 
     sb.append(", name='").append(name).append('\''); 
     sb.append('}'); 
     return sb.toString(); 
    } 
} 

klasy Tao

pakiet org.foo.bar;

import org.hibernate.Criteria; 
import org.hibernate.Session; 
import org.hibernate.SessionFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 
import org.springframework.transaction.annotation.Transactional; 

import java.util.Iterator; 
import java.util.NoSuchElementException; 

@Component 
public class MyEntityDao implements Iterable<MyEntity> { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public Iterator<MyEntity> iterator() { 
     return new Iterator<MyEntity>() { 
      private int num = 0; 
      private MyEntity item; 

      @Override 
      @Transactional(readOnly = true) 
      public boolean hasNext() { 
       item = getEntity(); 
       return item != null; 
      } 

      @Override 
      @Transactional(readOnly = true) 
      public MyEntity next() { 
       try { 
        if(item == null) { 
         item = getEntity(); 
         if(item == null) { 
          throw new NoSuchElementException(); 
         } 
        } 
        return item; 
       } finally { 
        item = null; 
       } 
      } 

      @Override 
      public void remove() { 
       throw new UnsupportedOperationException(); 
      } 

      private MyEntity getEntity() { 
       final Criteria criteria = getCurrentSession().createCriteria(MyEntity.class); 
       criteria.setFirstResult(num++); 
       criteria.setMaxResults(1); 
       return (MyEntity) criteria.uniqueResult(); 
      } 
     }; 
    } 

    public Session getCurrentSession() { 
     return sessionFactory.getCurrentSession(); 
    } 
} 

SQL

drop table ENTITY if exists 

create table ENTITY (id bigint generated by default as identity (start with 1), name varchar(255), primary key (id)) 

insert into ENTITY (name) values ('Entity1') 
insert into ENTITY (name) values ('Entity2') 
insert into ENTITY (name) values ('Entity3') 

testów jednostkowych

pakiet org.foo.bar;

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 

import static org.junit.Assert.assertEquals; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = { 
    "classpath:applicationContext.xml" 
}) 
public class MyEntityDaoTest { 

    @Autowired 
    private MyEntityDao dao; 

    @Test 
    public void testDao() throws Exception { 
     int count = 0; 
     for(MyEntity a : dao) { 
      count++; 
     } 
     assertEquals(3, count); 
    } 

} 

Oto moja pom.xml http://maven.apache.org/xsd/maven-4.0.0.xsd "> 4.0.0

<groupId>org.foo.bar</groupId> 
    <artifactId>spring-aspectj-hibernate</artifactId> 
    <version>1.0</version> 

    <properties> 
     <spring.version>3.1.1.RELEASE</spring.version> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-aspects</artifactId> 
      <version>${spring.version}</version> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-orm</artifactId> 
      <version>${spring.version}</version> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-instrument</artifactId> 
      <version>${spring.version}</version> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-test</artifactId> 
      <version>${spring.version}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.aspectj</groupId> 
      <artifactId>aspectjweaver</artifactId> 
      <version>1.6.12</version> 
     </dependency> 

     <dependency> 
      <groupId>org.hibernate</groupId> 
      <artifactId>hibernate-entitymanager</artifactId> 
      <version>4.1.1.Final</version> 
     </dependency> 

     <dependency> 
      <groupId>org.hsqldb</groupId> 
      <artifactId>hsqldb</artifactId> 
      <version>1.8.0.10</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>junit</groupId> 
      <artifactId>junit</artifactId> 
      <version>4.10</version> 
      <scope>test</scope> 
     </dependency> 

    </dependencies> 

</project> 

Po tym wszystkim musisz uruchomić JVM z następującym argumentem: -javaagent:<PATH-TO>/spring-instrument-{vertion}.jar. Aby zapobiec dodawaniu argumentu, -javaagent może również skonfigurować tkactwo w czasie kompilacji aspektu.

Powiązane problemy