Spring boot static where clause with JPA repositories - java

I'm working on a RESTful Spring Boot project that fetches data from MySQL DB.
I want to print just all categories that have active field equal to 1
and I want to apply that for all method in CategoryRepository Class: findAll, findByParentId ..etc.
package com.userService.repositories;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.userService.entities.Category;
public interface CategoryRepo extends JpaRepository<Category, Integer> {
#Query("where active =1")
public List<Category> findByParentId(int id);
}
I tried to use the query method but it doesn't work and gave me an exception

If you are using Hibernate as your persistence provider, you can take advantage of #Where clause on the entity level:
#Where(clause = "active =1")
#Entity
public class Category{
This will be applied to all queries that go through the persistence provider.

it could be helpful to you if you are using the query method you should specify
select alias_name from Category c where condition
or
from Category where condition
or
directly by using method
findByActive(int id);
public interface CategoryRepo extends JpaRepository<Category, Integer> {
#Query("select c from Category c where c.active =1")
public List<Category> findByParentId(int id);
}

#Query annotation allows to execute native queries. So, I think, you should specify the complete sql as follows
#Query("select c from Category c where c.active =1")

Related

How created multi update query with JPA data?

I try to create query setAll , because a entity with top isValid and I want all value to false before I use saveAll for udpade my database. I use this methods synchronize my database with a batch 6000-7000 lines actually.
import org.springframework.data.repository.CrudRepository;
public interface DomRepository extends CrudRepository<Dom, String> {
public Domaine findDomByName(String dom);
public List<Dom> findAll();
public void setIsValidAll(boolean isValid);
}
import javax.persistence.*;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedDate;
#Entity
#DynamicUpdate
public class Dom{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
// more attribute
private boolean isValid;
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'setIsValidAll' found for type 'Dom'!
It's not entirely clear what you want to achieve based on the description you have provided but if I get this right, you would like to create a repository when that will effectively update the value of the isValid field for all Dom objects. If that is indeed the case then you could simply create a custom repository method that will accept your flag and will make use of a simple HQL or JPQL to update all records in the database. For example something like this could work for this case:
#Modifying
#Query("UPDATE Dom SET isValid = :valid")
void updateValidAll(boolean valid);
If on the other hand you want to perform the update in memory on the fetched entities and then perform a saveAll you could do something like this:
#Transactional
public void updateAllRecords2(boolean isValid) {
var records = repository.findAll()
.stream()
.peek(dom -> dom.setValid(isValid))
.toList();
repository.saveAll(records);
}
Note that the latter is not very optimal as it uses peek which should not be used on operations that have side-effects (i.e mutations). On top of that will will perform an update query for every single entity that is present, meaning N queries for N entities.

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

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.

Spring Data - Spring Repository: selecting distinct values for a class attribute

I have the following
#Entity
public class Restaurant{
#ManyToOne
private City c;
// more
}
#Entity
public class City{
private String name;
// more
}
I also have a repository
public interface RestaurantRepository extends JPARepository<Restaurant, Long> {
// something to put here
}
===EDIT====
I have the list of ALL cities, but only some of them are associated to restaurants.
I need to write a method in such respository to extract all cities that are referred by a Restaurant.
In SQL I would just do the following:
SELECT CITY.id, CITY.name FROM CITY WHERE ID NOT IN (SELECT DISTINCT(city_id) FROM RESTAURANT)
Is there a way to obtain the same result using the name conventions as of http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html ?
Thanks.
As I do not see any support for the IS [NOT] EMPTY operator listed in Spring Data's documentation around query creation strategy, you can try manually defining a JPQL query:
public interface CityRepository extends JPARepository<City, Long> {
#Query(value="SELECT c FROM CITY c WHERE c.restaurants IS EMPTY")
List<City> findCitiesWithNoRestaurants();
}
which I'm guessing will translate to the native SQL you gave as an example.
Otherwise looks like you would need to find a list of cities with Restraunts and then pass that to a method which used the NotIn pattern. This will obviously be less performant than the above.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

Spring Data Repository (JPA) - findByXXX using entity relation

A Schedule entity has a one to one relationship with a Market entity as well as some other "simple" properties.
Here is my ScheduleRepository:
#RepositoryRestResource(path = "schedule")
public interface ScheduleRepository extends JpaRepository<Schedule, Long>
{
Collection<Schedule> findByMarket(Market market);
}
"findByMarket" method works fine when invoking the method programmatically. However, when invoking directly from a web application (http://localhost:8080/schedule/search/findByMarket), the request type must be GET.
My question is how do I pass a Market JSON object using GET? Using POST wouldn't be an issues but findXxx methods must use GET. I tried passing something like:
?market={marketId:60}
in the querystring but to no avail.
Not knowing what your controller looks like, I would assume that if you wanted to pass marketId on a get it would look like.
?marketId=60
And your method would look like. The method you use will handle converting to and from JSON.
#Get
#Path("/query")
#Produces({"application/xml", "application/json"})
public Todo whatEverNameYouLike(#PathParam("marketId") String marketId)
In the documentation it is referenced to use the #Param annotation, so you can call to your rest service giving a query parameter. Here you have an example:
package hello;
import java.util.List;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
I ended up using a #Query annotation, like jdepedro suggested:
#Query("select s from Schedule s where s.market.marketId = :marketId and s.locale.localeId = :localeId and s.offline >= :offline order by s.placement, s.slot, s.online")
Collection<Schedule> findByMarket(#Param("marketId") Integer marketId, #Param("localeId") Integer localeId, #Param("offline") Date offline);

Categories

Resources