I have an issue with getting session on anonymus inner class in hibernate with spring session factory. Here is the code:
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;
}
...
});
The issue is org.hibernate.HibernateException: No Session found for current thread this happens usually when DAO method is not within transaction. So how to make it work with an inner class?
i think defining @Transactional(readOnly = true)
at inner class level, spring will not be able to detect and apply transaction aspect over it. so it will not work for sure.
but i think if you write something like below might work not 100% sure (i doubt once you invoke iterator method transaction is closed)
@Override
@Transactional(readOnly = true)
public Iterator<Collection<Domain>> iterator() {
...
}
another option can be let caller be responsible for transaction or write wrapper method over iterator()
like getAllDomain()
and apply transaction to that method.
Solution which worked (mentioned in comments)
may be you can do some patch in getCurrentSession() like getCurrentSession() from sessionFactory if not available then use openSession(), ofcourse you have to close it manually if new session opened.
You can configure load-time aspect weaving
Here is the basic example how to do it
Spring context
<?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>
Entity class
package 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 class
package 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')
Unit test
package 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);
}
}
Here is my 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>
After all you have to run JVM with the following argument -javaagent:<PATH-TO>/spring-instrument-{vertion}.jar
. To prevent adding -javaagent
argument you can also configure aspectj compile-time weaving.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With