休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话

Posted

技术标签:

【中文标题】休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话【英文标题】:Hibernate Lazy loading not working with Spring Boot => failed to lazily initialize a collection of role could not initialize proxy - no Session 【发布时间】:2021-06-27 01:46:51 【问题描述】:

我正在尝试为我们的 Fund 实体实现 延迟加载,该实体与 FundAlternateId 具有 一对多 关系strong> 实体使用 FetchType.Lazy

当我尝试访问资金端点时,我收到以下错误:

Caused by: org.hibernate.LazyInitializationException: failed to lazily 初始化角色集合: com.example.model.Fund.fundAlternateIds,无法初始化代理 - 没有会话

JsonMappingException: 延迟初始化集合失败 角色:com.example.model.model.Fund.fundAlternateIds,不能 初始化代理 - 无会话(通过引用链: java.util.Collections$UnmodifiableRandomAccessList[0]-> com.example.model.model.Fund["fundAlternateIds"]) ;在 com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:397) ;

这是我的项目文件:

基金实体

@Entity(name="fund")
@Table(name="mv_fund_info")
@Getter
@Setter
public class Fund implements Serializable 

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "fund_port_id")
    private String fundPortId;


    @Column(name = "fund_full_name")
    private String fundFullName;

    @Column(name = "fund_short_name")
    private String fundShortName;

    @OneToMany(
            mappedBy = "associatedFund",
            fetch = FetchType.LAZY
    )
    @JsonManagedReference
    private List<FundAlternateId> fundAlternateIds;

FundAlternateId 类

@Entity(name="fundAlternateId")
@Table(name="mv_fund_alternate_id")
@Getter
@Setter
public class FundAlternateId implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @JsonIgnore
    @Column(name="alternate_id_ins_id")
    private Long alternateIdInsId;

    @Column(name="alternate_id_value")
    @Text
    private String alternateIdValue;

    @Column(name="alternate_id_type")
    @Text
    private String alternateIdType;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="fund_port_id")
    @JsonBackReference
    private Fund associatedFund;

休息控制器

@GET
@Produces(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
public Response getPublicFundAttributes() 
  List<Fund> publicFunds = fundService.getAllPublicFunds(offset,limit, sortBy);
  return Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(publicFunds).build();

基金服务

@Service
@Transactional("myTransactionManager")
public class FundServiceImpl implements FundService 

    @Autowired
    FundDao fundDao;

    @Override
    public List<Fund> getAllPublicFunds(Integer pageNo, Integer pageSize, String sortBy) 
        List<Fund> fundList = fundDao.getAllPublicFunds(pageNo, pageSize, sortBy);
        return fundList;
    

基金道

@Named("fundDao") 公共类 FundDaoImpl 实现 FundDao

@Autowired
@Qualifier("fundRepository")
FundRepository fundRepository;

@Override
public List<Fund> getAllPublicFunds() 
    List<Fund> pagedResult = fundRepository.findAll();
    return pagedResult;

基金库

@Repository("fundRepository")
public interface FundRepository extends PagingAndSortingRepository<Fund, String> 

配置类

@Configuration
@EnableJpaRepositories(
        basePackages = "com.example.repository",
        entityManagerFactoryRef = "myEntityManagerFactory",
        transactionManagerRef = "myTransactionManager"
)
@EnableTransactionManagement
public class ApplicationDatabaseConfiguration 

    @Bean(name = "myEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(@Qualifier("postGresDataSource") HikariDataSource dataSource) 
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan(new String[]  "com.example.model");
        em.setJpaVendorAdapter(hibernateJpaVendorAdapter());
        em.setJpaPropertyMap(buildJpaPropertyMap());

        return em;
    

    @Bean
    public HibernateJpaVendorAdapter hibernateJpaVendorAdapter() 

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL9Dialect");
        adapter.setShowSql(false);
        adapter.setGenerateDdl(false);

        return adapter;
    

    @Bean(name = "postGresDataSource")
    public HikariDataSource getDataSource() 
        HikariDataSource dataSource  = new HikariDataSource();

        try 
            dataSource.setDriverClassName("org.postgresql.Driver");
         catch (Exception e) 
            logger.error("ApplicationDatabaseConfiguration.getDataSource() has issue to get data source:", e);
        

        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUsername(userId);
        dataSource.setPassword(password);
        dataSource.setAutoCommit(true);
        dataSource.setMaximumPoolSize(25);
        dataSource.setMaxLifetime(300000);
        dataSource.setIdleTimeout(30000);

        return dataSource;
    

    @Bean(name = "sleeveTransactionManager")
    public PlatformTransactionManager transactionManager() 
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(getEntityManagerFactory(getDataSource()).getObject());
        tm.setDataSource(getDataSource());

        return tm;
    

【问题讨论】:

一旦您的事务被提交,您在控制器中拥有的List&lt;Fund&gt; 就是具有这种关系的分离Fund 对象的列表。现在当jackson尝试序列化List时,它也需要孩子List&lt;FundAlternateId&gt;,但由于它是一个实体对象,其获取策略设置为惰性,它会尝试获取初始化List&lt;FundAlternateId&gt;,但它会失败这样做,因为对象没有附加到持久性上下文。您只需在事务中构建响应,或者您可以从自定义查询中编写预测 (DTO), 【参考方案1】:

根据 cmets 中@code_mechanic 的建议,有两种方法可以解决这个问题:

    初始化Transaction(您的服务层)中的所有惰性引用 在返回 API 响应之前,将 Controller 中的所有惰性引用设置为 null。

我开发了两个实用方法,您可以使用它们来动态检查惰性对象是否已初始化。您可以在控制器层使用这些方法:

/**
 * Was collection initialized.
 *
 * @param c the c
 * @return true, if successful
 */
public static boolean wasCollectionInitialized(Object c) 
    if (!(c instanceof PersistentCollection)) 
        return true;
    

    PersistentCollection pc = (PersistentCollection) c;
    return pc.wasInitialized();


/**
 * Was object initialized.
 *
 * @param c the c
 * @return true, if successful
 */
public static boolean wasObjectInitialized(Object c) 
    if (!(c instanceof HibernateProxy)) 
        return true;
    

    HibernateProxy pc = (HibernateProxy) c;
    return !pc.getHibernateLazyInitializer().isUninitialized();

【讨论】:

以上是关于休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话的主要内容,如果未能解决你的问题,请参考以下文章

休眠延迟加载不适用于多对一映射

Spring Data:休眠查询缓存不适用于派生查询

从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集

休眠延迟加载的 JSF2 + Ajax EL 问题

Primefaces 动态列不适用于延迟加载

定制的 ObjectMapper 不适用于 spring boot hatoas