【问题标题】:Hibernate session within inner class内部类中的休眠会话
【发布时间】:2012-10-08 22:38:53
【问题描述】:

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

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 方法不在事务中时。 那么如何让它与内部类一起工作呢?

【问题讨论】:

    标签: java spring hibernate annotations inner-classes


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

    • 感谢您的回复,我在询问之前尝试过,但没有成功。而且我不希望调用者负责事务管理,因为我不希望 @Transactional 在我的服务层中。
    • 也许你可以在getCurrentSession() 中做一些补丁,比如 sessionFactory 的 getCurrentSession() 如果不可用,然后使用openSession(),当然如果新会话打开,你必须手动关闭它。
    • 它成功了 :-) 无论如何,让我们拭目以待,看看是否有其他建议
    • 不会有交易。
    • @mijer 如果调用者已启动事务,则 getCurrentSession() 将返回现有事务并加入现有事务,如果调用 `openSession() 则意味着调用者没有启动事务,因此没有意义加入现有交易
    【解决方案2】:

    您可以配置加载时方面编织

    这是一个基本的例子

    弹簧上下文

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

    毕竟您必须使用以下参数-javaagent:&lt;PATH-TO&gt;/spring-instrument-{vertion}.jar 运行JVM。为了防止添加 -javaagent 参数,您还可以配置 aspectj 编译时编织。

    【讨论】:

      猜你喜欢
      • 2012-06-02
      • 2018-09-27
      • 2013-09-14
      • 1970-01-01
      • 2015-03-18
      • 2011-02-01
      • 2017-10-08
      相关资源
      最近更新 更多