6

春のセッションファクトリで休止状態のanonymus内部クラスでセッションを取得する際に問題が発生しました。コードは次のとおりです。

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;
        }

        ...
    });

問題はorg.hibernate.HibernateExceptionです:現在のスレッドのセッションが見つかりません これは通常、DAOメソッドがトランザクション内にない場合に発生します。では、内部クラスで機能させるにはどうすればよいでしょうか。

4

2 に答える 2

1

ロード時のアスペクトウィービングを設定できます

これがそれを行う方法の基本的な例です

春のコンテキスト

<?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>

エンティティクラス

パッケージ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();
    }
}

Daoクラス

パッケージ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')

単体テスト

パッケージ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);
    }

}

これが私の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>

結局のところ、次の引数を指定してJVMを実行する必要があります-javaagent:<PATH-TO>/spring-instrument-{vertion}.jar。引数の追加を防ぐために、-javaagentaspectjコンパイル時ウィービングを構成することもできます。

于 2012-10-18T19:17:21.490 に答える
1

@Transactional(readOnly = true)内部クラスレベルで定義すると、Springはトランザクションの側面を検出して適用することができなくなると思います。だからそれは確かに機能しません。

しかし、以下のようなものを書いた場合、100%確実に機能しない可能性があると思います(イテレータメソッドを呼び出すと、トランザクションが閉じられるとは思えません)

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

別のオプションとして、呼び出し元にトランザクションの責任を負わせるか、ラッパーメソッドをiterator()likeに書き込んgetAllDomain()で、そのメソッドにトランザクションを適用することができます。

うまくいった解決策(コメントで言及)

利用できない場合はsessionFactoryからgetCurrentSession()のようなgetCurrentSession()でパッチを実行でき、openSession()を使用できます。もちろん、新しいセッションが開いた場合は手動で閉じる必要があります。

于 2012-10-18T13:22:12.533 に答える