Spring JPA could not initialize proxy - no Session

Spring JPA could not initialize proxy - no Session

JPA 通过谓词分析的方式来快速地实现数据库访问相关的操作,优点是提高了开发速度,但是如果遇到问题,排查起来会很麻烦


本次遇到的问题如下图所示:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy [org.xhliu.demo.entity.CustomerInfo#1] - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:322)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at org.xhliu.demo.entity.CustomerInfo$HibernateProxy$zwCU7ed1.toString(Unknown Source)
	at org.xhliu.demo.Application.main(Application.java:29)

这种异常一般发生在使用 JPA 执行一对多的连接查询的情况,这是由于在执行查询时,本次一对多的实体类字段属性可能未完成初始化,如 ListCollection 等,而此时 JPA 的持久化上下文已经被关闭了,因此抛出了 “no session” 这样的异常


解决方案:

  1. 添加全局 JPA 配置 enable_lazy_load_no_trans

    本解决方案极度不推荐

    在全局配置文件中添加如下的配置项:

    spring:
      jpa:
        properties:
          hibernate:
            enable_lazy_load_no_trans: true
    

    如果配置了这个属性,那么 JPA 就会为每一个一对多的关联字段分配一个单独的临时 Session,使得这个字段能够通过这个临时会话拿到对应的属性。

    这是一种 ”反模式“,因为如果这么配置了,当一对多的字段属性变多时,为了维护这些临时 Session 将会给底层的连接池带来很大的压力,严重的话将会明显地影响到性能,如果有别的解决方案尽量不要使用该方案

  2. 定义数据拉取策略

    JPA 定义了如下的获取列数据的策略:

    public enum FetchType {
        // 该策略表示数据可以被延迟获取
        LAZY,
    
        // 该策略表示必须被尽早地获取
        EAGER
    }
    

    对于一对多的集合集合属性字段,使用 EAGER 来确保能够即使获取数据,从而解决这个问题,具体使用如下所示:

    @Entity
    @Table(name = "user_info")
    public class UserInfo {
         @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "user_id", nullable = false)
        private long userId;
        
        @Basic(fetch = FetchType.LAZY)
        @Column(name = "user_phone")
        private String userPhone;
        
        @OneToMany(fetch = FetchType.EAGER)
        private Set<Feature> holdingFeatures = new HashSet<>();
    
        @OneToMany(fetch = FetchType.EAGER)
        private Set<Book> bookSet = new HashSet<>();
    }
    
  3. @NamedEntityGrap@EntityGraph

    具体使用如下所示:

    对于实体 Item

    @Entity
    @NamedEntityGraph(
        name = "Item.characteristics",
        attributeNodes = @NamedAttributeNode("characteristics")
    )
    public class Item {
        @Id
        private Long id;
        private String name;
    
        @OneToMany(mappedBy = "item")
        private List<Characteristic> characteristics = new ArrayList<>();
    }
    

    对于实体 Characteristic

    @Entity
    public class Characteristic {
        @Id
        private Long id;
        private String type;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn
        private Item item;
    }
    

    对于 JPA 接口 ItemRepository

    public interface ItemRepository extends JpaRepository<Item, Long> {
        @EntityGraph(value = "Item.characteristics")
        Item findByName(String name);
    }
    

    对于 JPA 接口 CharacteristicsRepository

    public interface CharacteristicsRepository extends JpaRepository<Characteristic, Long> {
        @EntityGraph(attributePaths = {"item"})
        Characteristic findByType(String type);    
    }
    

    这样也可以解决由于存在的级联关系导致的 “no session” 的问题


参考:

[1] https://stackoverflow.com/questions/36583185/spring-data-jpa-could-not-initialize-proxy-no-session-with-methods-marke

[2] https://www.baeldung.com/spring-data-jpa-named-entity-graphs

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值