将@Transactional添加到测试中,以避免org.hibernate.LazyInitializationException no Session错误。为什么需要它?

时间:2019-05-07 17:47:55

标签: java spring-boot jpa

我用@Transactional注释了一种测试方法,以避免:


org.hibernate.LazyInitializationException: could not initialize proxy [com....OrderEntity#6def569a-ebf2-473e-b1b1-8b67e62fd17d] - no Session

    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:169)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309)
    at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
    at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
    at com...orders.OrderEntity$HibernateProxy$wwLGAOuY.getDescription(Unknown Source)

我不知道为什么需要它,并想知道我的应用程序配置是否正确。

import lombok.Getter;
import lombok.Setter;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
import java.util.UUID;

@Entity
@Table(name = "orders")
@Getter
@Setter
public class OrderEntity {

    @Id
    @GeneratedValue
    private UUID uid;
    private Date created;
    private Date updated;
    private String description;

}
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.UUID;

@Repository
public interface OrderRepository extends JpaRepository<OrderEntity, UUID> {

    List<OrderEntity> findByDescription(String description);
}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

@Service
@Transactional
public class OrderService
{

    private OrderRepository repository;

    @Autowired
    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }

    public List<OrderEntity> findAll() {
        return repository.findAll();
    }

    public OrderEntity save(OrderEntity order) {
        return repository.save(order);
    }

    public OrderEntity getOne(UUID uid) {
        return repository.getOne(uid);
    }
}

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import static org.junit.Assert.assertEquals;

@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceTest {

    @Autowired
    private OrderService service;

    @Test
    @Transactional
    public void testSave() {

        OrderEntity order = new OrderEntity();
        order.setDescription("Order description");

        OrderEntity saved = service.save(order);
        System.out.println(saved.getDescription());

        OrderEntity persisted = service.getOne(saved.getUid());
        // throws LazyInitializationException without @Transactional
        System.out.println(persisted.getDescription()); 

        assertEquals(persisted.getDescription(), order.getDescription());
    }
}

我什至添加了@EnableTransactionManagement,但没有区别:

import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig {
}

1 个答案:

答案 0 :(得分:3)

getOnefindOne之间的区别在于,即使数据库中没有实际的行,第一个也总是返回惰性代理。惰性代理需要打开的EntityManager才能进行操作。但是,由于您的测试方法不在单个事务中运行,因此EntityManager方法一结束,getOne将被关闭。

在对象上没有打开EntityManager的调用将失败,因为它不再能够从数据库中检索值。

要解决此问题,请使用findOne而不是getOne或将测试方法变为事务性。然而,后者会对您的测试用例产生其他影响(它会从findOne调用返回相同的对象,因为它还会重用单个EntityManager)。