How to resolve LazyInitializationException in Spring Data JPA?

后端 未结 6 1557

I have to classes that have a one-to-many-relation. When I try to access the lazily loaded collection I get the LazyInitializationException. I searching the web

相关标签:
6条回答
  • 2020-12-05 11:20

    Consider using JPA 2.1, with Entity graphs:

    Lazy loading was often an issue with JPA 2.0. You had to define at the entity FetchType.LAZY or FetchType.EAGER and make sure the relation gets initialized within the transaction.

    This could be done by:

    • using a specific query that reads the entity
    • or by accessing the relation within business code (additional query for each relation).

    Both approaches are far from perfect, JPA 2.1 entity graphs are a better solution for it:

    • http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/
    • http://www.thoughts-on-java.org/jpa-21-entity-graph-part-2-define/
    0 讨论(0)
  • 2020-12-05 11:22

    You should enable Spring transaction manager by adding @EnableTransactionManagement annotation to your context configuration class.

    Since both services have @Transactional annotation and default value property of it is TxType.Required, current transaction will be shared among the services, provided that transaction manager is on. Thus a session should be available, and you won't be getting LazyInitializationException.

    0 讨论(0)
  • 2020-12-05 11:36

    You have 2 options.

    Option 1 : As mentioned by BetaRide, use the EAGER fetching strategy

    Option 2 : After getting the user from database using hibernate, add the below line in of code to load the collection elements:

    Hibernate.initialize(user.getCreatedJobs())
    

    This tells hibernate to initialize the collection elements

    0 讨论(0)
  • 2020-12-05 11:40

    Change

    @OneToMany(mappedBy = "creator")
    private Set<Job> createdJobs = new HashSet<>();
    

    to

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "creator")
    private Set<Job> createdJobs = new HashSet<>();
    

    Or use Hibernate.initialize inside your service, which has the same effect.

    0 讨论(0)
  • 2020-12-05 11:40

    For those who have not the possibility to use JPA 2.1 but want to keep the possibility to return a entity in their controller (and not a String/JsonNode/byte[]/void with write in response):

    there is still the possibility to build a DTO in the transaction, that will be returned by the controller.

    @RestController
    @RequestMapping(value = FooController.API, produces = MediaType.APPLICATION_JSON_VALUE)
    class FooController{
    
        static final String API = "/api/foo";
    
        private final FooService fooService;
    
        @Autowired
        FooController(FooService fooService) {
            this.fooService= fooService;
        }
    
        @RequestMapping(method = GET)
        @Transactional(readOnly = true)
        public FooResponseDto getFoo() {
            Foo foo = fooService.get();
            return new FooResponseDto(foo);
        }
    }
    
    0 讨论(0)
  • 2020-12-05 11:43

    Basically, you need to fetch the lazy data while you are inside of a transaction. If your service classes are @Transactional, then everything should be ok while you are in them. Once you get out of the service class, if you try to get the lazy collection, you will get that exception, which is in your main() method, line System.out.println(random.getCreatedJobs());.

    Now, it comes down to what your service methods need to return. If userService.getRandomUser() is expected to return a user with jobs initialized so you can manipulate them, then it's that method's responsibility to fetch it. The simplest way to do it with Hibernate is by calling Hibernate.initialize(user.getCreatedJobs()).

    0 讨论(0)
提交回复
热议问题