Spring Data JPA findAll throws NullPointerException - java

I have following autowired repository
#Repository
public interface ProsessiAjossaRepository extends JpaRepository<ProsessiAjossa, Long> {
ProsessiAjossa findByProsessi(final Prosessi prosessi);
}
automatically generated findById() method works fine, but findAll() throws null pointer exception for some reason. What may be wrong?

The most likely cause of the null pointer exception when using the findAll() method is that there is no data available in the database that matches the criteria specified. To troubleshoot this issue, try adding a few entries into the database and then running the findAll() method again. Additionally, you can also check the database for correct data type and field configurations, as these can lead to unexpected results.

Related

Issue with JPA native queries saying No property found for type

I have a spring boot application using JPA native queries. These queries are located in a separate properties file. I have 3 queries called getSomething and getSomethingWithSomeParam and getSomethingWithOtherParam. When it was just getSomething, the endpoint worked fine. When I added getSomethingWithSomeParam endpoint/query, it also worked as expected.
However when I added getSomethingWithOtherParam, JPA started throwing this error:
IllegalArgumentException: Failed to create query for method ... DataAccessLayer.getSomethingWithOtherParam(...)! No Property getSomething found for type DTO!
This is what the data access layer looks like:
#Query(nativeQuery = true)
public List<String> getSomething();
#Query(nativeQuery = true)
public List<String> getSomethingWithSomeParam(#Param("some") String some);
#Query(nativeQuery = true)
public List<String> getSomethingWithOtherParam(#Param("other") String other);
Does JPA not allow multiple queries with similar names if they have the same number and type of parameters? Why is JPA getting confused in which query to map each query method to?
I think your code is not a problem because I have used this method many times.
Because methods are called statically from another layer, there is no problem defining methods and parameters in your code.
You may have set up server-side settings that cause errors. To do this, you can create a simple project to test this.

How to test EntityManager query with H2 in memory DB

I have a Spring Boot project and I want to test some queries. I want to insert a predefined set of data and execute the Repository query to check the result is the desired one.
To do this I'm using in-memory H2 DB and the problem (I think) is not there, everything related with DB is ok. The main problem is I can't mock properly the EntityManager field in repository and the query is always null.
My repository is like this:
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public Result runQuery() {
TypedQuery<Result> query = entityManager.createQuery(
"SELECT ...", Result.class);
return query.setParameter("...", "...") // here 'query' is always null
.setMaxResults(1)
.getResultStream()
.findFirst()
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found"));
}
}
It works nice when is executed out of tests, but trying to run this test file it throw an error:
#RunWith(SpringRunner.class)
public class MyRepositoryTest {
#Mock
EntityManager entityManager;
#InjectMocks
MyRepositoryImpl repository;
#Test
public void it_should_works() {
Result r = repository.runQuery();
assertNull(r);
}
}
The repository is mocked and is not null, I can call the method. But inside the repository, as query field is null, it throw a NullPointerException when try to execute.
I've searched over the internet and I've found many ways to test the JPARepository and #Query inside the interface, but not an EntityManager query.
Also I've found a few ways to mock the result for the query, something like when(runQuery()).thenReturn(result) but I don't want that, I've the data in the memory DB so I want to execute the query and get the result.
So, now, the main problem I think is how to mock the EntityManager object properly inside repository class.
Thanks in advance.
Edit:
I've follow this link and is like another SO questions: It's only to mock the JpaRepository.
I've used this code:
#Test
public void it_should_works() {
Result r = repository.findAll();
assertNotNull(r);
}
And works perfectly, but using my own query fails with error:
org.springframework.orm.jpa.JpaSystemException: could not advance using next(); nested exception is org.hibernate.exception.GenericJDBCException: could not advance using next()
...
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: El objeto ya está cerrado
The object is already closed [90007-200]
So the question is: It is related to my DB? Why using JpaRepository method it works but my own query don't?
Edit:
Solved adding #Transactional into repository.
Since you're using an h2 in-memory database to run tests, and you want to actually use that database in your tests, you shouldn't really be mocking anything.
Your mocking doesn't work because the MyRepositoryImpl is typically initialized by Spring, and that process is much more complicated than plugging in an EntityManager.
I think what you want to do is more like what's described here https://www.baeldung.com/spring-testing-separate-data-source
So you would have a src/test/resources/application.properties file that overrides the datasource properties. Then you just #Autowired your repository into your test class like normal.

Disable DB Updates in Spring boot JPA when changing the value of a property after it is fetched from the repository using findall or other queries

I am having a service that gets the data from the database which has a column which is stored with encrypted value.
After fetching from the DAO, i will update the value of the property to decrypted value and then send it as response for the API.
I assume that the entity is having change tracking enabled for select queries also because after i get the data, the data is updated in the DB with the decrypted password. I have googled and found that the use of EntityManager solves the problem, but for this implementation I have to do a lot of code changes in many entities.
from this link, i see that we have to write custom stateless bean and inject to the code, but it looks like not right. Please suggest me the best approach to handle this problem.
My DAO:
#Repository
public interface EnvironmentDao extends JpaRepository<Environment, Long> {
//custom methods go here with native queries
}
My Service
#Override
public List<Environment> getEnvironmentsByIds(List<Long> environmentIds) throws Exception {
if (environmentIds == null || environmentIds.size() < 1) {
return null;
}
return decryptPassword(environmentDao.findAllById(environmentIds));
}
Inside the decryptPassword method, i am just looping through all the records and then setting the decrypted password like
e.setDB_Password(encryptionService.decrypt(e.getDB_Password()));
One case that i noticed yesterday is that for a similar entity on any error, there was a DB save and that time the values got updated, so after fixing the error, this change was not happening.
Please help me as I am not an expert in java and taking more time to analyze and could not understand. In the case of C#, i would use .AsNoTracking(), but i don't know java much and fiddling around.
Tried the following in the Service
#Autowired
EntityManager entityManager;
In the method,
Optional<Environment> environment = environmentDao.findById(id);
entityManager.detach(environment.get());
return managePassword(environment.get(), false);
I would suggest two options to overcome the entity being updated unintentionally:
Instead of returning the entity itself I would suggest creating a DTO class and creating an instance of that class and setting relevant properties on to the DTO instance so that no changes will be made to the entity itself. So the code will be sth like:
public List<EnvironmentDTO> getEnvironmentsByIds(List<Long> environmentIds) throws Exception {
if (environmentIds == null || environmentIds.size() < 1) {
return null;
}
return createEnvironmentDTOs(environmentDao.findAllById(environmentIds));
}
private LisT<EnvironmentDTO> createEnvironmentDTOs(List<Environment> environments) {
return environments.stream().map((env) -> {
EnvironmentDTO envDto = new EnvironmentDTO();
// Copy all relevant fields to DTO (you can even use some Mapper library for this, i.e. http://modelmapper.org/)
envDto.setDB_Password(encryptionService.decrypt(e.getDB_Password()));
})
}
If you want to return the entity no matter what instead of creating a DTO class and instance from it; you can detach the entity so that changes to the entity will not be reflected to database. So what you need to do is detaching entity after you are done with decrypting the password and setting it back to the entity: entityManager.detach(environment)

Spring Data JPA aggregate functions on an empty resultset

I am working on a project involving Spring and JPA/Hibernate. The database driver used in my development environment is H2. My application has a page that displays statistics, one example of such a statistic is the average age of my users. However, when I try to fetch the average age using JPQL, I receive an exception
Result must not be null!
Assume for simplicity reasons that I store age as an integer on every User object (in my application this is of course not the case, but that's not important for my problem).
User model
#Entity
public class User implements Identifiable<Long> {
private int age;
// more fields and methods, irrelevant
}
User repository
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
#Query("SELECT AVG(u.age) FROM #{#entityName} u")
long averageAge();
}
I cannot seem to figure out why calling UserRepository#averageAge(); is throwing the exception. I have tried replacing the function AVG in the query by COUNT and this behaves as expected. I have also tried to use an SQL query and setting nativeQuery = true in the annotation, yet to no avail. I can ofcourse solve it by fetching all the users and calculate the average age in plain Java, but this wouldn't be very efficient.
Stacktrace:
Caused by: org.springframework.dao.EmptyResultDataAccessException: Result must not be null!
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:102)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy150.averageAge(Unknown Source)
at my.test.application.StatisticsRunner.run(StatisticsRunner.java:72)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:809)
... 30 more
Solved
The exception was caused by the fact that AVG() returns null when performed on an empty table. I fixed it by modifying the query (inspired by the answer to this question) as follows:
#Query("SELECT coalesce(AVG(u.age), 0) FROM #{#entityName} u")
long averageAge();
If you use Spring Data, and if your method returns null when Hibernate can't find a match, make sure you add #org.springframework.lang.Nullable to your method signature:
public interface SomeRepositoryCustom {
#org.springframework.lang.Nullable
public Thing findOneThingByAttr(Attribute attr) {
/* ...your logic here... */
}
}
This is because Spring Data checks the nullability of your method, and if the annotation is missing, it's going to enforce that you always need to return an object:
/* org.springframework.data.repository.core.support.MethodInvocationValidator */
#Nullable
#Override
public Object invoke(#SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
/* ...snip... */
if (result == null && !nullability.isNullableReturn()) {
throw new EmptyResultDataAccessException("Result must not be null!", 1);
}
/* ...snip... */
I used Spring Boot version 2.1.1.RELEASE and Spring Data 2.1.4.RELEASE.
It seems that the EmptyResultDataAccessException exception is thrown when a result from a query was expected to have at least one row (or element) but none was returned.
Related documentation about this can be found here.
I would suggest to run the same query this attempts to run in order to further validate this theory. Now the good question's what to do with this.
You have two options. Either catch the EmptyResultDataAccessException exception at your calling point and handle it directly in there or alternatively you can have an ExceptionHandler which will be tasked with handling such exceptions.
Both ways of handling this, should be OK and you may choose between each depending on your scenario.
i am not complete sure, but i think the problem it is because of the type of return long, maybe you should use the Long wrapper, long does not allow null because it is a primitive, try to change to
#Query("SELECT AVG(u.age) FROM #{#entityName} u")
Long averageAge();

Spring JPA Query returns Null instead of List

I have an #Entity Video having a one-to-many relation with a List<Tag> tags as one of its fields. I use the following #Repository using Spring Data to get the most popular tags:
#Repository
public interface TagRepository extends CrudRepository<Tag, Integer>{
#Query("SELECT t FROM Tag t WHERE (SELECT SUM(v.views) FROM Video v WHERE t MEMBER OF v.tags) > 0")
public List<Tag> findMostViewedTags(int maxTags);
}
The Query is processed and considered valid by Spring, I tested the generated SQL vs my database locally and it returned 2 Tags. In my Code however, I receive the value Null when I call the method findMostViewedTags(100).
The Query lookup strategy is the default "CREATE_IF_NOT_FOUND".
If there are no results found, should the method return an empty list or Null? My desired behavior is to receive an empty list.
Why does the method call return Null instead of a List<Tag> with size() 2?
The normal behavior is indeed returning an empty list if no results are found. If a List<Object> is the return value of the method in the defined interface, the method should never return Null.
The problem is that a parameter is given to the method and is not used anywhere in the Query. For some reason Spring decides to return a Null in that case. Solution: remove the unused parameter or use the parameter in the Query.
I have experienced similar problem. The cause was that I was using Mockito and have not correctly mocked the data with when().

Categories

Resources