RollbackException after using #Service inside ConstraintValidator isValid() method - java

I am creating my custom validation annotation so I needed to create my own ConstraintValidator implementation class.
public class SomethingValidator implements ConstraintValidator<Annotation, String>{
#Autowired
private SomeService someService;
public boolean isValid(...){
//here I need to use the service
someService.someMethod();
return true;
}
}
And in this class, in isValid method I need to use SomeService class that is annotated as #Service
#Service
public class SomeService {
public Something someMethod(){
// stuff
}
}
My application flow is something like this:
User enters Register page, fills the form and hits Register
In the #Controller class I check if bindingResult.hasErrors()
IF ERRORS EXIST: return to the register page and displays error messages
IF ERRORS NOT EXIST: calls autowired someService instance in the #Controller class and saves the entity.
And the problem is, if in my ConstrainValidation class, in isValid method I use someService then I receive RollbackException, even if the return is always true (statically added as in the code snipped above)
So finally: I cant use SomeService inside isValid method, because it results with RollbackException (stacktrace below)
Everything seems to work properly, but using the SomeService.
I know that the problem may be transactions, maybe I manage EntityManager wrong? or should I add any arguments to #Transactional method inside Service?
I am using Spring Data JPA and JpaRepository (so I dont obtain the #PersistenceContext EntityManager entityManager)
I will very appreciate any kind of help..
Stacktraces: (I will paste all of it into pastebin) http://pastebin.com/FTjD5gRR

Related

Why can't I use a JPA repository in more than one controller?

I am building a web application using Java Spring Boot. I'm trying to use a JPA repository in two different controllers. However, it only works inside one controller and not in the other one.
MyRepository.java
public interface MyRepository extends JpaRepository<Favorite, Long> { }
It works fine in OneController without any problem.
OneController.java
#RestController
#RequiredArgsConstructor
public class OneController {
#Autowired
private final MyRepository myRepository;
}
However, when I try to also use it in AnotherController, I get the following message: Variable 'myRepository' might not have been initialized.
AnotherController.java
#Controller
#RequiredArgsConstructor
public class AnotherController {
#Autowired
private MyService myService;
#Autowired
private final MyRepository myRepository;
private final DateUtil dateUtil;
public AnotherController(DateUtil dateUtil) {
this.dateUtil = dateUtil;
}
}
What's the problem here? One is RestController and the other is Controller, but that doesn't seem to be an issue.
EDIT:
Sorry for the confusion. I have omitted a constructor in AnotherController in my original question, so I have added it.
For some reason, after removing a constructor, I no longer see the message Variable 'myRepository' might not have been initialized. Without a constructor, everything works fine. Why is it that?
Also, #Autowired doesn't seem to be required for repositories, but the service needs it. Without the annotation, the whole page won't work. Why is it the case?
When you're using field autowiring, fields cannot be final. First the instance is created (with null values), then the autowired fields get updated. That can't be done if they're final.
Since you're using #RequiredArgsConstructor, you can just remove the #Autowired. This will make Spring use constructor autowiring; it will find the dependencies when calling the constructor.

Null Pointer Exception JUnit Mockito

I m new to JUnit and Mockitio. When I run the below code, I get,
java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getStatusCodeValue()" because the return value of "com.learnit.testing.MyController.getUser(java.lang.Long)" is null
#Controller
#RequestMapping("/api")
public class MyController {
#Autowired
private MyService service;
#GetMapping("/")
#ResponseBody
public ResponseEntity<Object> getUser(Long id) throws Exception {
return service.myResponse(id);
}
}
#Service
public class MyService {
#Autowired
private MyRepository repository;
public ResponseEntity<Object> myResponse(Long id) throws Exception{
MyData data=repository.findById(id).orElse(null);
if(data!=null)
return new ResponseEntity<>(HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Testing
#ExtendWith(MockitoExtension.class)
public class MyTest {
#InjectMocks
private MyController controller;
#Mock
private MyRepository repo;
#Mock
private MyService service;
#Test
public void checkService() throws Exception {
when(repo.findById((long)1)).thenReturn(null);
assertEquals(controller.getUser((long)1).getStatusCodeValue(), 500);
}
}
There are several issues:
You did not stub the behaviour of the MyService#myResponse method. You need to mock the MyService instance as this is the direct dependency of the MyController instance. Therefore, the MyRepository don't needs to be mocked in this test case (unless you want to stub its methods for some reason, but I don't it in the current code). I would recommend you to replace the following:
when(repo.findById((long)1)).thenReturn(null);
with stubbing behaviour of the MyService mock instance:
when(service.myResponse(1L)).thenReturn(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
When you do this, the MyRepository test suite mock instance can be removed, as it is not required anymore.
Check your imports, based on the usage of #ExtendWith annotation I assume you are using JUnit 5 here. Make sure that you actually use other classes from org.junit.jupiter.api(for example #Test and Assertions). By default, the JUnit does not support backward compatibility with the JUnit 4. Still, I you want to keep the compatibility you should include the junit-vintage-engine artifact in your test runtime path.
P.S You need to think which class you actually want to test, that determines which instances should be mocked.
IMHO you need to add a #RunWith(some.mockito.TestSuiteIDontRememberName.class) annotated to the test class. The #Mock annotation requires a test suite that searches for the annotation and gathers the information to inject the values there.
If I were you, I'd first try to initialize the mocked instances in a #Before method and see if the test runs... then, I'd read the Mockito (sorry, but I normally use EasyMock) library documentation, and check how is the #Mock annotation used (I can tell you you have chances for this to be the problem)
And there's a problem in your test code... you are telling the mocked instance to return null when calling method findById(), so most probably if that was to happen, you'll get a NPE in the main code, when trying to use the .orelse() method. Probably you don't know what to return, or you need to return an also mocked object instance... but as such, it is impossible to repair your code without implementing something completely different to your intention.
Anyway, you have eliminated enough things in your code to make it untestable, and in this process you have not included how do you call the method to be tested at all.
Please, read the page How to create a Minimal, Reproducible Example and edit your question to include testable code, showing that NPE, so we can, at least, see how and where it is occuring.

How to make Depedency Inject in a not "RequestMapping" method

Can i make my repository be accessed in a class who isn't a RestController or something like that ?
I have an WatchService who listen a folder, to reads the files and after persist to a database. My watchservice works just like reading files, but I want persist using my JPARepository to persists, can i do that?
Springboot Application v2.1.6.RELEASE
#Repository
public interface MyRepository extends JpaRepository<MyClass, Long> { }
public class MyWatchService implements Runnable{
#Autowired
private MyRepository myRepository;
// SOME CODES COMES HERE
#Override
public void run() {
// SOME CODES COMES HERE
myRepository.save(MyClass); // In this point give a nullPointerException
}
}
I get that Exception:
java.lang.NullPointerException
at com.WatchService.run(WatchService.java:515)
at java.base/java.lang.Thread.run(Thread.java:835)
You get the NullPointerException because the dependency did not get injected. You used the annotation correctly, but the dependencies do not get injected, by some magic.
In order for this to work (i.e. for the beans to get injected), you need to let the DI- or IoC Container instantiate the bean for your (in JEE this would be CDI, in Spring it is the Spring IoC Container). This can be done by injection (d'uh! Injection-inception) or programmatically.
A Spring-centric solution is explored in this question.

How to invoke injected bean methods from instance initializer block

I am using Spring and SpringMvc and I want to invoke a method of a service in some controller, and both them
are managed by Spring. When I try to do this I got NullPointerException, but I find that the Service's constructor truly called before I invoke this method.
I think maybe this Service has been added in Spring, but the property here in this controller has not be set.
How can I get it from SpringContext?
My code like this:
#Controller
#RequestMapping("/test")
public class SomeController {
#Resource
private SomeService someService;
{
someService.serviceMethod();
//And something more
}
#RequestMapping("/someMethod")
private void controllerMethod(){
}
}
You are trying to call spring-injected service from an initializer block. The resource you annotated with #Resource has not been injected into the controller by spring yet! That's why you are getting the NullPointerException
Create a separate function annotated with #PostConstruct instead.
#Controller
#RequestMapping("/test")
public class SomeController {
#Resource
private SomeService someService;
#PostConstruct
public void postConstruct(){
someService.serviceMethod();
}
}
That "postConstruct" method will be invoked after the bean (i.e. the controller) has been created and all the dependencies (including SomeService) has been injected into the bean. Of course, it satisfies your requirement that it should only be called once.
Your specific problem has to do with the way Java handles Instance Initalizer Blocks:
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.
If you want to stick with the initializer block you can solve the problem by adding a constructor to your controller and inject your service into it:
#Autowired
public SomeController(SomeService someService) {
this.someService = someService;
}
Alternatively, your could remove the initializer block and add a #PostConstruct annotated setup method.
#PostConstruct
public void setupSomeController(){
someService.serviceMethod();
}

Spring transaction propagation - Service vs DAO

I have a Service class like below:
#Service("MyService")
public class MyService {
#Autowired
MyDao dao;
public void process() {
getFromDao();
// getMoreFromDao();
// process();
// if all good, then
doStuff();
}
public void getFromDao() {
// do some stuff
dao.getData();
}
#Transactional(transactionManager="simpleDatasourceTxMgr", propagation=Propagation.REQUIRED)
public void doStuff() {
dao.saveData(1);
dao.saveData(2);
dao.saveData(3);
}
}
The DAO called is:
#Repository
public class MyDao {
#Autowired
#Qualifier("myjdbcTemplate")
NamedParameterJdbcTemplate jdbcTemplate;
public void saveData(obj a) {
jdbcTemplate.execute("Query", ...);
}
}
I want my doStuff() method in the service class to run within a transaction and rollback everything if there is an exception in the saveData() method. But this is not running in transaction.
If I add #Transaction to a DAO method looks like it runs in separate transaction. Is this correct?
Update: I have added a process() method to my Service and I call getFromDao() and doStuff() from process(). process() is called from the controller. So looks like if I make the service class #Transactional, then everything executes within a transaction. But I don't want getFromDao() to execute in transaction.
We use just JDBC and no Hibernate.
You can place the #Transactional annotation before an interface
definition, a method on an interface, a class definition, or a public
method on a class. However, the mere presence of the #Transactional
annotation is not enough to activate the transactional behavior. The
#Transactional annotation is simply metadata that can be consumed by
some runtime infrastructure that is #Transactional-aware and that can
use the metadata to configure the appropriate beans with transactional
behavior. In the preceding example, the
element switches on the transactional behavior.
Or if you want annotations you can enable it with
It is not sufficient to tell you simply to annotate your classes with
the #Transactional annotation, add #EnableTransactionManagement to
your configuration, and then expect you to understand how it all
works. This section explains the inner workings of the Spring
Framework’s declarative transaction infrastructure in the event of
transaction-related issues.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

Categories