Added @Transactional into a test to avoid org.hibernate.LazyInitializationException no Session error. Why is it needed?

﹥>﹥吖頭↗ 提交于 2021-01-29 12:28:29

问题


I annotated a test method with @Transactional to avoid:


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)

I do not know why it is needed and wonder whether my application configuration is correct.

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

I even added @EnableTransactionManagement but it makes no difference:

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

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig {
}

回答1:


The difference between getOne and findOne is that the first always returns a lazy proxy, even if there is no actual row in the database. The lazy proxy needs an open EntityManager to operate on. However as your test method doesn't run in a single transaction the EntityManager will be closed as soon as the getOnemethod ends.

Without an open EntityManager calls on the object will fail as it cannot retrieve the values from the database anymore.

To solve use findOne instead of getOne OR make your test method transactional. The latter however has some other effects on your test-case (it will return the same object from the findOne call as it will also reuse a single EntityManager).



来源:https://stackoverflow.com/questions/56027994/added-transactional-into-a-test-to-avoid-org-hibernate-lazyinitializationexcept

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!