How to use #NamedQuery in spring a CrudRepository #Query? - java

I want to make use of a #NamedQuery inside a JpaRepository. But it does not work:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
#Query(name = MyEntity.FIND_ALL_CUSTOM)
List<MyEntity> findAllCustom(Pageable pageable);
}
#Entity
#NamedQuery(
name = MyEntity.FIND_ALL_CUSTOM, query = "select * from MyEntity me where me.age >= 18"
)
public class MyEntity {
public static final String FIND_ALL_CUSTOM = "findAllCustom";
}
Result:
org.springframework.data.mapping.PropertyReferenceException: No property findAllCustom found for type MyEntity!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:270)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:241)
at org.springframework.data.repository.query.parser.Part.<init>(Part.java:76)
at org.springframework.data.repository.query.parser.PartTree$OrPart.<init>(PartTree.java:235)
at org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:373)
at org.springframework.data.repository.query.parser.PartTree$Predicate.<init>(PartTree.java:353)
at org.springframework.data.repository.query.parser.PartTree.<init>(PartTree.java:84)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:61)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:94)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:205)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:72)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:369)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:192)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
... 28 more
Update:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
List<MyEntity> findAllCustom(Pageable pageable);
}
#Entity
#NamedQuery(
name = "MyEntity.findAllCustom", query = "select * from MyEntity me where me.age >= 18"
)
public class MyEntity {
}
Still same exception:
PropertyReferenceException: No property findAllCustom found for type MyEntity!

Take a look at the documentation of Spring Data JPA - Using JPA NamedQueries.
I advise you follow the conventions set in the documentation (starting with the simple name of the configured domain class, followed by the method name separated by a dot). Cut the underscore and name the query like
#NamedQuery(name = "MyEntity.findAllCustom", query="...")
or even better add a suggestive name like findByAge or sth.
To allow execution of these named queries all you need to do is to specify MyEntityRepository as follows:
public interface MyEntityRepository extends JpaRepository <MyEntity, Long> {
List<MyEntity> findAllCustom();
}
I implemented it with the JpaRepository as the documentation exemplifies. But you could try with a simple CrudRepository and see if that works.
I think the problem was you where using #Query and the Queries annotated to the query method will take precedence over queries defined using #NamedQuery. Read the docs for the #Query usage, i think you where also using it wrong.
Update
To use the Pageable, according to this answer
to apply pagination, a second subquery must be derived. Because the
subquery is referring to the same fields, you need to ensure that your
query uses aliases for the entities/tables it refers to
that means you would rewrite your query like
query ="select * from MyEntity me where me.age >= 18".
The example was used for #Query, but that is also a named query so it should apply to your case as well. The only difference is that with #Query you actually bind them directly rather than annotating them to the domain class.
Update 2
I tried in my own app.
First off you should have the query using the alias instead of * (i.e me).
Secondly the string you use FIND_ALL_CUSTOM is not following the convention which is "MyEntity.findAllCustom".
Solution
Copy paste this:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
List<MyEntity> findAllCustom(Pageable pageable);
List<MyEntity> findAllCustom();
}
#Entity
#NamedQuery(
name = MyEntity.FIND_ALL_CUSTOM, query = "select me from MyEntity me where me.age >= 18"
)
public class MyEntity {
public static final String FIND_ALL_CUSTOM = "MyEntity.findAllCustom";
}
Both will work. For the one with the pageable method argument call it as myEntityRepository.allCustom(new PageRequest(0,20)). Ofc, you know that myEntityRepository is injected.

Related

What is the right way to create custom query in spring-data-jpa

I am facing an issue where when I run my spring boot application with my custom query method, I am getting an exception.
My repository class is below
public interface ProductRepository extends Repository<Product,Integer> {
#Query(name = "SELECT p.rate FROM Product p WHERE p.id=:id")
public Optional<String> findRateById(#Param("id") int id);
#Modifying
#Query(name = "UPDATE Product p set p.productName=:productName WHERE p.id=:id")
public int updateProductNameById(#Param("id") int id,#Param("productName") String
productName);
}
And the exception that I'm getting
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract int com.spring.data.jpa.repository.ProductRepository.updateProductNameById(int,java.lang.String)! No property updateProductNameById found for type Product!
The name property within the #Query annotation determines the use of a NamedQuery.
If you want to use Named Query than you should use the #NamedQuery annotation in your entity.
#Entity
#Table
#NamedQuery(name="updateProduct", query="UPDATE PRODUCT ....")
public class Product
You can read more about it here JPA
If you don't want to use namedquery than, just remove the name property inside the #Query annotation.

Mapping #Query with multiple column selection to an Java object in a #Repository - is it possible out of the box?

Is it possible to map the results of a Hibernate #Query like this (in a #Repository interface extending #JpaRepository):
#Query("select u.id, u.email, u.status from user u")
public SimpleUserDTO getAllUsersSimpleData();
directly to a Java object like this:
public class SimpleUserDTO {
private Long id;
private String email;
private String status;
}
What I know, is that doing something like this:
Query query = session.createQuery("select u.id, u.email, u.status from user u");
the result can be extracted to a List<Object[]>:
List<Object[]> users = (List<Object[]>) query.list();
But is it possible to map it directly to a List<SimpleUserDTO> without writing additional method that will map the values to SimpleUserDTO?
You have two options.
Option 1: Constructor Expression:
#Query("select new <insert_package_here>.SimpleUserDTO(u.id, u.email, u.status) from user u")
public List<SimpleUserDTO> getAllUsersSimpleData();
Option 2: Use Interface projection
Turn your DTO into an interface and you can use it without #Query annotation
public List<SimpleUserDTO> getAllUsersSimpleData();
Please find out more about projections and Spring Data JPA in the documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

JPQL Custom Query to Retrieving data with ExampleMatcher & Pageable

How to using JPQL Custom query using ExampleMatcher?
I'm trying to retrieve the child from parent size.
Since I'm not allowed to add another method on the child side.
I need to filter and paging for showing the child because the child contains so-many row data.
This is my repository.
#Repository
public interface ParentRepository extends JpaRepository<Parent, String> {
#Query(value = "SELECT c FROM Child c where c.parent.id =:id")
public List<Child> findChildById(String id, Example example, Pageable pageable);
}
Note: This query is working fine, if without example and pageable as parameters.
That method give me the an error :
Error creating bean with name 'parentRepository':
Invocation of init method failed; nested exception is java.lang.IllegalStateException:
Using named parameters for method public abstract java.util.List com.example.test.ParentRepository
.findChildById(java.lang.String,org.hibernate.criterion.Example,org.springframework.data.domain.Pageable)
but parameter 'Optional[example]' not found in annotated query
'SELECT c FROM Child c where c.parent.id =:id'!
Parameters you pass to method annotated with #Query should be used in query, in your example this should be something like this:
#Query(value = "SELECT c FROM Child c where c.parent.id =:id")
public List<Child> findChildById(#Param("id") String id);

How to find letter in all columns with Spring Data JpaRepository

Here is my Repository
public interface NoteRepository extends JpaRepository<Note,Long> {
List<Note> findByContentContains(String content);
and my method in class NoteController which return me words with "e" in column Content
#GetMapping("/notesletter")
public List<String> getLetters(){
return noteRepository.findByContentContains("e")
.stream()
.map(note -> note.getContent())
.collect(Collectors.toList());
}
Please, help me to find a method which will return every words with letter "e",for example, from all columns.
I am working with Postman
Of course you can you native queries
#Query("select u from Note u where u.COL1 = ?1 and u.COL2 = ?1")
List<Note> findByContentContains(String content)
or use jpa feature as given below, Note findByLastnameOrFirstnameStartingWith is mentioned to understand easily, so replace your firstname,lastname as colu1name,col2name respectively.Refer here for more jpa method conventions`
public interface NoteRepository extends JpaRepository<Note,Long> {
List<Note> findByLastnameOrFirstnameStartingWith(String param1,String param2)
static List<Note> findByContentContains(String content){
findByLastnameOrFirstnameStartingWith(content,content);
}
}
You can try the or operation in the method name like below.
Or findByLastnameOrFirstname
https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html
Other approaches are possible too.
If you know the native query or JPQL equivalent that returns the results checking all the columns of the entity.
Use of
#Query above the method in repository interface can take a JPQL or native query.
#NamedQuery annotation is also available if you know the query. This annotation to be used over the Entity class, name attribute is used to specify the accessing method name which returns the data, this can be used in repository interface.

How to use createQuery with spring boot

Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation

Categories

Resources