当前位置:网站首页>Analysis and solution of lazyinitializationexception

Analysis and solution of lazyinitializationexception

2022-07-04 00:34:00 Teng Qingshan

Introduce

If FetchType.LAZY, Then when transmitting data to the front end, there may be LazyInitializationException abnormal , Because the session is closed in the service , therefore JSON Mapper Object Unable to get data .

Take an example , We will use the following entities : Insert picture description here

When executing the following code :

List<PostComment> comments = null;
 
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
    
    entityManager = entityManagerFactory()
        .createEntityManager();
    transaction = entityManager.getTransaction();
    transaction.begin();
 
    comments = entityManager.createQuery(
        "select pc " +
        "from PostComment pc " +
        "where pc.review = :review", PostComment.class)
    .setParameter("review", review)
    .getResultList();
 
    transaction.commit();
} catch (Throwable e) {
    
    if (transaction != null &&
        transaction.isActive())
        transaction.rollback();
    throw e;
} finally {
    
    if (entityManager != null) {
    
        entityManager.close();
    }
}
 
try {
    
    for(PostComment comment : comments) {
    
        LOGGER.info(
            "The post title is '{}'",
            comment.getPost().getTitle()
        );
    }
} catch (LazyInitializationException expected) {
    
    assertEquals(
        "could not initialize proxy - no Session",
        expected.getMessage()
    );
}

Hibernate Will throw out LazyInitializationException, Because the relationship is marked :EntityManagerPostFetchType.LAZY, In obtaining PostComment Object is not loaded in time Post, When the entity gets later Post The session has been closed .


solve

According to design 、 Performance is different from that of developers , There are two common options to solve this problem :

  1. The simplest one is to use FetchType.EAGER, In this way, the session is still alive at the controller method , But it affects performance .

  2. Another way is to use FetchType.LAZY With a mapper( Such as MapStruct) take Entity Object conversion DTO object , Then return it to controller, Therefore, if the session is closed, there will be no exception .( This method is suitable for you only want to read or hide some properties of the object from the result , If you need to modify database records, you still have to use entities Entity.)

Here is a simple example :

@RestController
@RequestMapping("/api")
public class UserResource {
    

    @GetMapping("/users")
    public Page<UserDTO> getAllUsers(Pageable pageable) {
    
        return userService.getAllUsers(pageable);
    }
}
@Service
@Transactional
public class UserService {
    

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
    
        this.userRepository = userRepository;
    }

    @Transactional(readOnly = true)
    public Page<UserDTO> getAllUsers(Pageable pageable) {
    
        return userRepository.findAll(pageable).map(UserDTO::new);
    }
}
@Repository
public interface UserRepository extends JpaRepository<User, String> {
    

    Page<User> findAll(Pageable pageable);
}
public class UserDTO {
    

    private Long id;

    private String firstName;

    private String lastName;

    private String email;
    
    private Set<String> addresses;

    public UserDTO() {
    
        // Empty constructor needed for Jackson.
    }

    public UserDTO(User user) {
    
        this.id = user.getId();
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
        this.email = user.getEmail();
        this.addresses = user.getAddresses().stream()
            .map(Address::getAddress)
            .collect(Collectors.toSet());
    }

    // Getters, setters, equals, and hashCode
}
@Entity
@Table(name = "user")
public class User implements Serializable {
    

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String firstName;

    @Column
    private String lastName;

    @Column(unique = true)
    private String email;
    
    @OneToMany(mappedBy = "address", fetch = FetchType.LAZY)
    private Set<Address> addresses = new HashSet<>();

    // Getters, setters, equals, and hashCode
}
@Entity
@Table(name = "address")
public class Address implements Serializable {
    

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String address;

    @ManyToOne
    @JsonIgnoreProperties(value = "addesses", allowSetters = true)
    private User user;

    // Getters, setters, equals, and hashCode
}

You can see , When invoking the service layer getAllUsers Method returns UserDTO object , And in the UserDTO Object's constructor method , Called user.getAddresses() Method , So even after session close ,dto Objects already exist address attribute , call getAddress No LazyInitializationException Of .

From :
The best way to handle the LazyInitializationException - Vlad Mihalcea
hibernate - Difference between FetchType LAZY and EAGER in Java Persistence API? - Stack Overflow

原网站

版权声明
本文为[Teng Qingshan]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202142010474651.html