【问题标题】:Bidirectional @OneToOne Spring Data JPA, Hibernate双向@OneToOne Spring Data JPA,Hibernate
【发布时间】:2021-07-14 11:23:09
【问题描述】:

我正在使用来自Hibernate documentationBidirectional @OneToOne。我为测试创建了一个相同的模型。

我无法通过 PhoneDetails 获取电话。我收到一个错误 - Message Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy [com.example.model.Phone#1] - no Session

我尝试了很多选项,但都不起作用。

请告诉我如何正确获取电话?我整天坐着试图做到这一点。我在网上没有找到任何选项,所以我在这里问。

Phone.java

    @Entity(name = "Phone")
    public class Phone {

        @Id
        @GeneratedValue
        private Long id;

        @Column(name = "`number`")
        private String number;

        @OneToOne(mappedBy = "phone",
                cascade = CascadeType.ALL,
                orphanRemoval = true,
                fetch = FetchType.LAZY)

        private PhoneDetails details;

        public Phone() {
        }

        public Phone(String number) {
            this.number = number;
        }

        // Getters and setters are omitted for brevity

        public void addDetails(PhoneDetails details) {
            details.setPhone( this );
            this.details = details;
        }

        public void removeDetails() {
            if ( details != null ) {
                details.setPhone( null );
                this.details = null;
            }
        }
    }

PhoneDetails.java

    @Entity(name = "PhoneDetails")
    public class PhoneDetails {

        @Id
        @GeneratedValue
        private Long id;

        private String provider;

        private String technology;

        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "phone_id")
        private Phone phone;

        public PhoneDetails() {
        }

        public PhoneDetails(String provider, String technology) {
            this.provider = provider;
            this.technology = technology;
        }


        // Getters and setters are omitted for brevity

    }

LifecycleController.java

    @Controller
    public class LifecycleController {


        @Autowired
        ServiceJpa serviceJpa;


        @GetMapping(value = "/savePhoneAndPhoneDetails")
        public String savePersonAddress () {

            Phone phone = new Phone( "123-456-7890" );
            PhoneDetails details = new PhoneDetails( "T-Mobile", "GSM" );

            phone.addDetails( details );
            serviceJpa.savPhone( phone );


            return "/savePhoneAndPhoneDetails";
        }



        @GetMapping(value = "/getPhone")
        public String addPersonAddress () {


            PhoneDetails address = serviceJpa.findPhoneDetailsById(2L).orElseThrow();
            Phone phone = address.getPhone();

            /* 
               An error appears here -
               could not initialize proxy
               [com.example.model.Phone#1] - no Session
            */
            System.out.println(phone.getNumber());


            return "/getPhone";
        }

    }

ServiceJpa.java

    @Service
    @Transactional
    public class ServiceJpa {



        @Autowired
        PhoneJpa phoneJpa;

        @Autowired
        PhoneDetailsJpa phoneDetailsJpa;


        @Transactional
        public void savPhone(Phone phone) {
            phoneJpa.save(phone);
        }


        @Transactional
        public Optional<PhoneDetails> findPhoneDetailsById(Long id) {
            return phoneDetailsJpa.findById(id);
        }


    }

接口 PhoneJpa.java

    @Repository
    public interface PhoneJpa extends JpaRepository<Phone, Long> {
        
    }

接口 PhoneDetailsJpa.java

    @Repository
    public interface PhoneDetailsJpa extends JpaRepository<PhoneDetails, Long> {

    }

【问题讨论】:

  • 通过添加注释 @Transactional 使您的 ServiceJpa 具有事务性
  • 我做到了。它没有帮助。
  • 请在问题中添加您的 ServiceJpa 代码
  • 我添加了。请看。
  • 您不应访问事务范围之外的实体详细信息。好吧,作为一种快速而肮脏的技巧,您可以将@Transaction 放在控制器级别。正确的解决方案是将实体映射到DTO表示,参考我的文章github.com/slobodator/hibernate-probe#entity-and-dto

标签: spring hibernate spring-data-jpa spring-data hibernate-mapping


【解决方案1】:

我同意 Andriy 的评论,只是稍微添加了“您不应该在事务范围之外访问 [延迟加载] 实体详细信息”。但是,对于初学者来说,您是否有某些理由希望 OneToOne 以 FetchType.LAZY 开头?如果您将其更改为 EAGER,您的“懒惰”问题将得到解决,因为它不再是一个懒惰的引用,而是一个真正的水合对象。

如果这不是您想要采取的确切路线,那么有十几种方法可以总体上急切地获取东西,坦率地说,这里有很多方法,无法在此处提出一个单一的解决方案作为最佳/理想。由于您的代码存在,因为所有取消引用(目前)都发生在您的 Controller 中,因此 Andriy 将 @Transaction 添加到 Controller 的建议可能就足够了,因为它会在您需要时延迟获取。

但是在未来,如果 POJO 中有延迟元素返回到高于控制器的堆栈,例如,在它们被序列化为 JSON 之前,那么即使是 CONTROLLER 的 @Transactional 也不会是“堆栈中足够高”,您最终会遇到相同的惰性初始化问题..

此外,通过将其设为 Lazy 然后在其他地方取消引用,您可以保证两次访问数据库。使用适当的 FETCH/JOIN 急切负载,您可以将其限制为一个,这可能是另一个性能优势。

因此,无论哪种方式,您都回到了手头的真正问题.. 寻找确保您的操作完全发生在事务边界内的方法必须完全水合对象,以免“懒惰” " danglers 在此之外被取消引用.. 即通过使它们急切或通过强制初始化任何潜在的 Lazy 代理/集合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多