JPQL Custom Query to Retrieving data with ExampleMatcher & Pageable - java

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);

Related

Java - JPA-Specification: how to create criteria/specification on a field that belongs to a nested object?

I am using jdk 1.8 , hibernate and jpa in my project. And using specification/criteria to build my search query.
I have a class A ( an hibernate entity) which has class B as an attribute. So, roughly, it looks like :
#Entity
class A {
Long id;
String comment;
#OneToOne
B b;
}
and...
#Entity
class B {
Long id;
String type;
}
My repository class looks like (roughly):
public interface ARepository extends PagingAndSortingRepository<A, Integer>,
JpaSpecificationExecutor<A> {
}
Most of the simple JPA queries are working as expected. Even the specification/criteria based directly on Class A is working. However, I need to create a dynamic query and that should be executed under "findAll" method of PagingAndSortingRepository class. This query should be equivalent to
select * from A a left join B b on a.b_id = b.id
where b.type='final' and a.comment='blah';
I created a similar logic as above in a specification like :
public Specification<A> getSpecification() {
return (itemRoot, query, criteriaBuilder) -> {
.........
List<Predicate> partialQueries = new ArrayList<>();
partialQueries.add(criteriaBuilder.equal(itemRoot.get("b.type"), "final"));
partialQueries.add(criteriaBuilder.equal(itemRoot.get("comment"), "blah"));
//Other queries to be added...
return criteriaBuilder.and(partialQueries.toArray(new Predicate[0]));
};
}
And getting error :
Unable to locate Attribute with the the given name [b.type] on this ManagedType [com.something.domain.A]
Any insight on how to create criteria/specification on a field that belongs to a nested object?
If you want to filter nested object. You can write
itemRoot.get("NestedTableName").get("nestedfieldname")
In your case - itemRoot.get("B").get("type")
itemRoot.get("Name of the nested object field in the root class").get("nestedfieldname");
Example:
cb.equal(root.get("b").get("type"),value)
In your case - itemRoot.get("b").get("type");

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.

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 findAll method in spring boot with cruderepository

My UserRepository:
public interface UserRepository extends CrudRepository<User, Integer> {
List<User> findAll(List<Integer> ids);
}
Error:
Caused by:
org.springframework.data.mapping.PropertyReferenceException: No
property findAll found for type User
Refer - http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html?is-external=true#findAll-java.lang.Iterable-
Can some one tell me how to get list of User objects based on List of Id's.
This is working
#Query(" select new User(id,x,y,z) from User b where b.id in ?1 ")
List<User> findById(List<Integer> id);
Firstly, I would rename the repository to UserRepository, because having 2 User classes is confusing.
findAll(), by definition, is meant to get all the models with no criteria. You should add a method named
findByIdIn(Collection<Integer> ids)
Use List<User> findAll(Iterable<Integer> ids) or List<User> findByIdIn(List<Integer> ids)

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

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.

Categories

Resources