Getting LazyInitializationException within in a #Transactional method - spring

I’m getting this exception when I access this method from my controller:
{"status":"failure","exception":"LazyInitializationException","exceptionMessage":"failed
to lazily initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session","errorMessage":"failed to lazily
initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session"}
Controller:
#Autowired
private ThirdPartyService m_thirdPartySvc;
…
#RequestMapping(value = "/launch", method = RequestMethod.GET)
#Transactional
public String launchLti(final #RequestParam String assignmentId,
final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException
{
final subcoAuthenticationUser auth = (subcoAuthenticationUser) ((Authentication) principal).getPrincipal();
String nextPage = null;
final User user = m_userSvc.findById(auth.getId());
// Provision the assignment in ThirdParty if not already done so
final Assignment assmt = m_lessonPlanDao.getAssignment(assignmentId);
if (!assmt.isSentToThirdParty())
{
m_thirdPartySvc.sendAssignment(assignmentId);
} // if
Is the #Transactional annotation unnecessary? Especially since I already have it on my #Service class…
#Service
#Transactional
public class ThirdPartyServiceImpl implements ThirdPartyService
{
#Override
public void sendAssignment(final String assignmentId)
{
final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
if (isThirdPartyAssignment(assignment))
{
final String ThirdPartyPromptId = assignment.getTocItem().getThirdPartyPromptId();
// Gather the teacher id
final LessonPlan lessonPlan = m_lessonPlanDao.getLessonPlan(assignment.getLessonPlan().getId());
final String teacherId = lessonPlan.getOwnerId();
// Gather the students who have been assigned this assignment
final List<Classroom> classes = lessonPlan.getClassrooms();
// Send one request for each class assignment
for (final Classroom classroom : classes)
{
The error occurs on the for (final Classroom classroom : classes) line. I have #Transactional everywhere, yet I’m getting this LazyInitializationException. Why? And how do I create a transaction so that I can run my method?
I’m using Spring 3.2.11.RELEASE, Hibernate 4.3.6.Final, and JPA 2.1 on JBoss 7.1.3.Final. If upgrading any of these would solve my problem, let me know.

The #Transactional boundary is being respected by application during runtime. You can find this out by calling: TransactionSynchronizationManager#isActualTransactionActive()
Add some code to print out the value of above method. If it's false, then maybe you need to make sure the component-scan is set up right.
Example: <context:component-scan base-package="com.application.dao" />
This would totally miss the classes in the com.application.service package.

Related

SOLVED. Mock service.insert() works with ArgumentMatchers.any() only, triggers exception otherwise

Im creating a test class using Mockito and everything is runnig OK, except by one mock that calls a service layer method and it only works if the input parameter is any() and it throws the following exception if the input parameter is native from the method.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.devsuperior.dscatalog.dto.ProductDTO.getId()" because "productDTO" is null at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
Lets go through the code:
The service layer method, productDTO is a DTO class from a ordinary Product entity.
#Transactional
public ProductDTO insert(ProductDTO productDTO) {
Product entity = new Product();
copyDtoToEntity(productDTO, entity);
entity = repository.save(entity); // reposity.save() returns a reference to object saved in DB
return new ProductDTO(entity);
}
the controller layer method:
#PostMapping
public ResponseEntity<ProductDTO> insert(#RequestBody ProductDTO productDTO){
productDTO = service.insert(productDTO); //[1]
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(productDTO.getId()).toUri();
return ResponseEntity.created(uri).body(productDTO);
}
Before I continue I ran in debug mode this code and the ProdutDTO was correctly instanciated until the mock captured the service.insert(productDTO) call and after this line [1] productDTO = null
my test class:
#WebMvcTest(ProductResource.class)
public class ProductResourceTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService service;
#Autowired
private ObjectMapper objectMapper;
private PageImpl<ProductDTO> page;
private ProductDTO productDTO;
private long existingId;
private long nonExistingId;
private long dependentId;
private long productBadCategoryId;
private String jsonBody;
#BeforeEach
void setUp() throws Exception {
productDTO = Factory.createProductDTO();
existingId = Factory.getExistingProductId();
nonExistingId = 100L;
dependentId = 50L;
productBadCategoryId = 200L;
page = new PageImpl<>(List.of(productDTO));
when(service.findAllPaged(ArgumentMatchers.any())).thenReturn(page);
when(service.findById(existingId)).thenReturn(productDTO);
when(service.findById(nonExistingId)).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(existingId), any())).thenReturn(productDTO);
when(service.update(eq(nonExistingId), any())).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(productBadCategoryId), any())).thenThrow(NestedResourceNotFoundException.class);
doNothing().when(service).delete(existingId);
doThrow(ResourceNotFoundException.class).when(service).delete(nonExistingId);
doThrow(DatabaseException.class).when(service).delete(dependentId);
when(service.insert(any(ProductDTO.class))).thenReturn(productDTO); // IT WORKS! [2]
//when(service.insert( productDTO )).thenReturn(productDTO); // IT IT DOESNT WORKS! [3]
jsonBody = objectMapper.writeValueAsString(productDTO);
}
and finally the test method where the mock call triggers the exception
#Test
public void insertShouldReturnProductDTOCreated() throws Exception {
ResultActions result = mockMvc.perform(post("/products").content(jsonBody)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON));
result.andExpect(status().isCreated());
result.andExpect(jsonPath("$.id").exists());
result.andExpect(jsonPath("$.name").exists());
result.andExpect(jsonPath("$.description").exists());
}
Whenever I uncomment line [3] and comment line [2] the exception above is rised.
Of course I could let this way (its working), but If I want to raise an exception for the case the object to be inserted has some issue,I could build a new service.insert() mock to test the throw of the exception. As it is I can´t because I cant diferentiate a any() object from another one.I have read some similar problems like mine and the proposed solution was to add #Autowired annotation with service variable, but in my case still the issue remains.
The solution and a tentative of explanation what was happening:
Override HashCode/Equals methods in ProducDTO class.
When serializing ProductDTO class (this line-> jsonBody = objectMapper.writeValueAsString(productDTO) ) and upon reception on resource layer it turns again into an object but its new reference address it is not the same and for instance the non override equals will fail (it compares the reference address and not their content).

How to mock beans Autowired on service layer in #WebMvcTest

I am testing a REST API's in Spring boot gradle app, my mocked service using #MockBean is returning null. This mocked service return null if there are some beans Autowired in service class(I used constructor injection).
Here is sample Code(Not compiled, only for understanding)
#RestController
#RequestMapping("/xxx")
class TestController {
private RetriveDataService retriveDataService;
public TestControllerx(RetriveDataService retriveDataService) {
this.retriveDataService = retriveDataService;
}
#PostMapping(value = "/yyy")
public MyResponseModel myMethod(#RequestBody MyRequestModel model) {
return retriveDataService.retriveData(model);
}
}
#Service
class RetriveDataService {
private TokenService tokenService;
public RetriveDataService(TokenService tokenService) {
this.tokenService = tokenService;
}
public MyResponseModel retriveData(MyRequestModel model) {
String accessToken = tokenService.getToken().getAccessToken();
return retriveData(model, accessToken);
}
}
#RunWith(SpringRunner.class)
#WebMvcTest(TestController.class)
public class TestControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private RetriveDataService retriveDataService;
#Test
public void testRetriveData() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/xxx/yyy").content(objectMapper.writeValueAsString(new MyRequestModel()))
.contentType(MediaType.APPLICATION_JSON_UTF8)).andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
When I run this test, i am getting following output(If my service do not need another bean, I am getting expected output)
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Due to this response i facing problem on line .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));. also when i check response body(as body is also a null)
Sample project to reproduce the issue is here
Checking your repository confirmed assumption form the discussion in comments under question.
You specify expectations on your mock
MyModel requestMessage = new MyModel();
requestMessage.setMessage("Hello Request Post");
given(testService1.getMessage(requestMessage)).willReturn(responseMessage);
but the message received to in your controller in your #WebMvcTest is not equal to requestMessage specified in the test. This is due to the fact that MyModel class does not override equals method.
In this situation, Mockito will use its default behaviour:
By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate. For example 0 for an int/Integer and false for a boolean/Boolean.
You have two options to fix the problem:
override equals (and hashCode) in your request class.
Get acquainted with argument matchers
More info on option 2.:
Technically, your expectation is equivalent to:
given(testService1.getMessage(ArgumentMatchers.eq(requestMessage)))
.willReturn(responseMessage);
You can use other matcher, or even define your own. This is useful if you cannot modify code of your argument's type (type coming from 3-rd party library etc).
For example, you can use ArgumentMatchers.any(MyModel.class))

How to define Spring Data Repository scope to Prototype?

I'm using Spring data jpa & hibernate for data access along with Spring boot. All the repository beans are singleton by default. I want to define the scope of all my repositories to Prototype. How can I do that?
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Edit 1
The problem is related to domain object being shared in 2 different transactions which is causing my code to fail. I thought it is happening because repository beans are singleton. That's the reason I asked the question. Here is the detailed explanation of the scenario.
I have 2 entities User and UserSkill. User has 1-* relationship with UserSkills with lazy loading enabled on UserSkill relation.
In a UserAggregationService, I first make a call to fetch an individual user skill by id 123 which belongs to user with id 1.
public class UserAggregationService {
public List<Object> getAggregatedResults() {
resultList.add(userSkillService.getUserSkill(123));
//Throws Null Pointer Exception. See below for more details.
resultList.add(userService.get(1));
}
}
Implementation of UserSkillService method looks like
#Override
public UserSkillDTO getUserSkill(String id) {
UserSkill userSkill = userSkillService.get(id);
//Skills set to null avoid recursive DTO mapping. Dozer mapper is used
//for mapping.
userSkill.getUser().setSkills(null);
UserSkillDTO result = mapper.map(userSkill, UserSkillDTO.class);
return result;
}
In the call of user aggregation service, I call UserService to fetch userDetails. UserService code looks like
#Override
public UserDTO getById(String id) {
User user = userService.getByGuid(id);
List<UserSkillDTO> userSkillList = Lists.newArrayList();
//user.getSkills throws null pointer exception.
for (UserSkill uSkill : user.getSkills()) {
//Code emitted
}
....
//code removed for conciseness
return userDTO;
}
UserSkillService method implementation
public class UserSkillService {
#Override
#Transactional(propagation = Propagation.SUPPORTS)
public UserSkill get(String guid) throws PostNotFoundException {
UserSkill skill = userSkillRepository.findByGuid(guid);
if (skill == null) {
throw new SkillNotFoundException(guid);
}
return skill;
}
}
UserService method implementation:
public class UserService {
#Override
#Transactional(readOnly = true)
public User getByGuid(String guid) throws UserNotFoundException {
User user = userRepo.findByGuid(guid);
if (user == null) {
throw new UserNotFoundException(guid);
}
return user;
}
}
Spring boot auto configuration is used to instantiate entity manager factory and transaction manager. In the configuration file spring.jpa.* keys are used to connect to the database.
If I comment the below line of code, then I do not get the exception. I am unable to understand why change in the domain object is being affecting the object fetch in a different transaction.
userSkill.getUser().setSkills(null);
Please suggest If I have missed something.

My spring mvc session was changed implicitly after called a service method?

I'm using spring 3.2.5 via annotations and got some issue dealing with session.
My controller class is like this:
#Controller
public class WebController {
#Autowired
private IElementService elementService;
...
//in this method I set the "elementList" in session explicitly
#RequestMapping("/elementSearch.do")
public String elementSearch(
#RequestParam("keyword") String keyword,
HttpSession session){
List<Element> elementList= elementService.searchElement(keyword);
session.setAttribute("elementList", elementList);
return "searchResult";
}
//here I got my problem
#RequestMapping(value="/anotherMethod.do", produces="text/html; charset=utf-8")
#ResponseBody
public String anotherMethod(
...
//I called my service method here like
Element e = elementService.searchElement("something").get(0);
...
}
And I have a ElementServiceImpl class like this:
#Service
public class ElementServiceImpl implements IElementService {
#Autowired
private IBaseDAO baseDao;
#Override
public List<Metadata> searchElement(String keyword) {
List<Metadata> re = baseDao.searchElement(keyword);
return re;
}
}
And I have a BaseDAOImpl class implemented IBaseDAO and annonated with #Repository:
#Repository
public class BaseDAOImpl implements IBaseDAO {
...
}
Here is the problem, when I visit ".../anotherMethod.do", which will call the anotherMethod up there, my "elementList" in session was changed!
Then I looked into the anotherMethod() and found everytime
Element e = elementService.searchElement("something").get(0);
was called, my elementList was change to the new result returned by searchElement method(which returns a List).
But I didn't set session in that method, and I'm not using #SessionAttributes, so I don't understand how could my session attribute changed after calling a service method?
This problem is torturing me right now so any advise would be a great help, thanks!
update: I tried to print all my session attributes around that method call like this:
StringBuilder ss1 = new StringBuilder("-------------current session-------------\n");
session.setAttribute("test1", "test value 1");
log.info("sessionTest - key:test1 value:" + session.getAttribute("test"));
Enumeration<String> attrs1 = session.getAttributeNames();
while(attrs1.hasMoreElements()){
String key = attrs1.nextElement();
ss1.append(key).append(":").append(session.getAttribute(key)).append("\n");
}
log.info(ss1);
But I didn't see whether the "elementList" or the test value which I added just before print. And I do can get some value by
List<Element> elementList = (List<Element>) session.getAttribute("elementList");
and the elementList I get changed after calling service method, just like I posted before. Where my elementList stored? not in the session?
My goal is to show the elementList to the users in a table, and let them pick one of them, then I get the row number of the table and take it as a index of the elemntList, so I'll know which elemnt object the user picked. Are there any better way to do this so I can get rid of that problem?
Thanks again.

Hibernate Validator custom messages key with class name and field name

I've been trying to add custom messages for validation errors for a REST Service managed by Spring MVC within a #Controller class.
The Employee class:
public class Employee {
#NotNull
#NotEmpty
private String company;
...
}
My REST Service:
#ResponseStatus(value = HttpStatus.CREATED)
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody #Valid Employee employee) {
employees.add(employee);
}
And the validation errors parses
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public #ResponseBody
List<String> validationExceptions(MethodArgumentNotValidException e) {
List<String> errors = new ArrayList<String>();
for (FieldError error : e.getBindingResult().getFieldErrors()) {
errors.add(error.getDefaultMessage());
}
return errors;
}
So I've put a ValidationMessages.properties on the root of my classpath, and I'm not able to get my custom messages with the following key NotEmpty.employee.company.
I know there are many ways to do this with a ResourceBundle and error.getCode(), or even with the key org.hibernate.validator.constraints.NotEmpty.message, but I'd like have specific messages to specific field of specific objects.
I also don't want to do this with #NotEmpty(message = "NotEmpty.employee.company}"). I want it the simplest.
What should I do?
Have you tried to implement your own
org.springframework.validation.MessageCodesResolver
and then declaring your implementation in the config file:
<mvc:annotation-driven message-codes-resolver="org.example.YourMessageCodesResolverImpl"/>
I'd give it a try, it seems this one is able to build custom error codes like the ones you want:
String[] resolveMessageCodes(String errorCode, String objectName, String field, Class<?> fieldType)
The only and important thing I'm not sure is whether it'll override the error codes generated by the hibernate validators...
I hope it helps (and works).
Cheers,
Chico.

Resources