6

我在使用春季会话工厂在休眠中获取匿名内部类的会话时遇到问题。这是代码:

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: No Session found for current thread 这通常发生在 DAO 方法不在事务中时。那么如何使它与内部类一起工作呢?

4

2 回答 2

1

我认为@Transactional(readOnly = true)在内部类级别定义,spring 将无法检测和应用事务方面。所以它肯定不会工作。

但我认为如果您编写类似下面的内容可能无法 100% 确定(我怀疑一旦您调用迭代器方法,事务就关闭了)

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

另一个选项可以让调用者负责事务或编写包装器方法iterator()并将getAllDomain()事务应用于该方法。

有效的解决方案(在评论中提到)

可能你可以在 getCurrentSession() 中做一些补丁,比如 sessionFactory 中的 getCurrentSession() 如果不可用,然后使用 openSession(),当然如果新会话打开,你必须手动关闭它。

于 2012-10-18T13:22:12.533 回答
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();
    }
}

道类

包 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。为了防止添加-javaagent参数,您还可以配置 aspectj 编译时编织。

于 2012-10-18T19:17:21.490 回答