Search code examples
spring-mvcjpapersistencespring-orm

Unable to perform LAZY FETCH operation using JPA in one-to-one relationship


I am trying to lazily fetch PetDetails entity using JPA. However, I get LazyInitialization Exception. I read many solutions for this exception and came to find solution using JOIN FETCH in JPQL. Also people recommended using Criteria queries. However, I am trying to look for a solution if there is a way I can fetch the PetDetails entity the way I want without using queries directly or depending on Criteria API or without using EAGER FETCH. I might be missing something. I would appreciate for any help or suggestion. Below are the code samples:

1. Controller class:

    @Controller
    public class PetController {
        private static Logger LOGGER = Logger.getLogger(PetController.class);

        @Autowired
        private PetService petService;

        @RequestMapping(value = "/", method = RequestMethod.GET)
        public void manageAndDisplayPet() {
            PetDetails petDetails = new PetDetails();
            petDetails.setName("DOG");
            Pet pet = new Pet(petDetails);
            // save
            petService.savePet(pet);

            // retrieve
            LOGGER.debug("**********************" + petService.getPet());
            LOGGER.debug("**********************" + pet.getPetDetails());
        }
    }

2. PetService class:

    @Service
    public class PetService {
        @Autowired
        private PetDAO petDAO;

        @Transactional
        public void savePet(Pet pet) {
            petDAO.savePet(pet);
        }

        @Transactional
        public Pet getPet() {
            return petDAO.getPet();
        }
    }

3. PetDAO class

    @Repository
    @EnableTransactionManagement
    public class PetDAO {
        @PersistenceContext(unitName = "petcontext")
        private EntityManager entityManagerFactory;

        public void savePet(Pet pet) {
            entityManagerFactory.persist(pet);
        }

        public Pet getPet() {
            Pet pet = (Pet) entityManagerFactory.find(Pet.class, 1);
            return pet;
        }

    }

4. Pet Entity:

@Entity
@Table(name = "t_pet")
public class Pet {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @JoinColumn(name = "pet_details")
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private PetDetails petDetails;

    public Pet() {
    }

    public Pet(PetDetails petDetails) {
        this.petDetails = petDetails;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public PetDetails getPetDetails() {
        return petDetails;
    }

    public void setPetDetails(PetDetails petDetails) {
        this.petDetails = petDetails;
    }

    @Override
    public String toString() {
        return "Pet [id=" + id + ", petDetails=" + petDetails + "]";
    }

}

5. PetDetails Entity:

@Entity
@Table(name = "pet_details")
public class PetDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "pet_name")

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "PetDetails [id=" + id + ", name=" + name + "]";
    }

}

Thank you for your help.


Solution

  • Easy thing you can do is to call pet.getPetDetails() inside PetService#getPet. This solution is not very clear, but it will force JPA to fetch entity too. This is solution for your question, but not the good way anyways.


    What is the good way?

    The good way may depend on your particular usecase:

    1. If you need this details everytime — you should use EAGER_FETCH
    2. If you need it time to time than good solution it to use JPQL with JOIN FETCH
    3. But the best way is to select not entites, but DTOs, which will contain whole information which your application needs and not more. It may be achieved with SELECT NEW expression as described here