I created a user management service based on springboot.
The user will have a list of attachments, so the relationship of the user and attachment is OneToMany.
I ignored the insert logic here since my question is about the lazyload and when the entitymanager is opened and closed. Below is the entity, controller, service, dao, repository related code.
Entity
@Entity
@Table(name="User")
public class UserInfoEntity {
private long id;
private String mail;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userInfoEntity", cascade = CascadeType.ALL)
private List<UserAttachmentEntity> attachmentList = new ArrayList<>();
}
@Entity
@Table(name = "ATTACHMENT")
public class UserAttachmentEntity {
private long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="userRequestId")
private UserInfoEntity userInfoEntity;
}
Service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void save(UserInfoEntity userInfoEntity) throws RestException {
userDao.save(userInfoEntity);
}
@Override
// I did set @Transactional here
public UserInfoEntity findByMail(String mail) {
UserInfoEntity userInfoEntity = userDao.findByMail(mail);
return userInfoEntity;
}
}
DAO
@Service
public class UserDaoImpl implements UserDao {
@PersistenceContext
private EntityManager entityManager;
@Autowired
private UserInfoEntityRepository userInfoRepository;
@Override
public UserInfoEntity findByMail(String mail) {
return userInfoRepository.findFirstByMailOrderByIdDesc(mail);
}
}
Repository
@Repository
public interface UserInfoEntityRepository extends JpaRepository<UserInfoEntity, Integer> {
UserInfoEntity findFirstByMailOrderByIdDesc(String mail);
}
Controller
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "load", method = RequestMethod.POST)
@ResponseBody
public UserInfoEntity load(UserInfoEntity userInfoEntityInput, HttpServletRequest request) throws Exception {
UserInfoEntity userInfoEntity = userService.findByMail(userInfoEntityInput.getMail());
System.out.println(userInfoEntity.getAttachmentList());
return userInfoEntity;
}
}
After I test the load method in controller, I found that even I set the
@OneToMany(fetch = FetchType.LAZY,...) in UserInfoEntity.
I can still call userInfoEntity.getAttachmentList() in controller. (I can see the select * from attachment query was printed there)
The answer of the post lazyinitializationexception-in-spring-data-jpa said that you need to fetch the lazy data while you are inside of a transaction.
But I did not set the @Transaction in the findByMail method in service.
And I remember that I also met this exception a couple of days ago. But now I can load the lazy data successfully in controller.
I mainly have below questions.
- When the entitymanager is opened and closed? (is it opened in service or dao?)
- Why the lazy data can be loaded in controller?
- Is the entity manager thread-safe? (I googled, but not find useful answer)
- Is the entity manager singleton? ( in above code, I inject the entity manager into dao, although I did not use it, I used spring data, I can inject the entity manager to service, controller, find the hashcode is different)
Thanks in advance. I wrote this question in company, I was not allowed to push any code to github in company. If I can, I think it will be more convenient for you since the spring boot project with h2 database is very easy to setup locally.