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.
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:
EAGER_FETCH
JOIN FETCH
SELECT NEW
expression as described here