Spring data jpa query to return with multiple properties - spring

I have to fetch Users by id and state.The method takes a set of usersIds as input.
I need a jpa query like below:
public Set<Users> fetchUsersByIdsContainingStateId(Set<Integer> userIds, Integer stateId)
Is there a query like the above in Spring Data JPA or I need to resort to named query

you can use spring data query:
public interface UserRepository extends CrudRepository<User, Long> {
Set<User> findUserByIdInAndStageId(Set<Long> userIds , Long stageId);
#Query("select u from User u where u.id in (:userIds) and u.stage.id=:stageId")
Set<User> findUserByUserIdsAndStageId(#Param("userIds") Set<Long> userIds , #Param("stageId") Long stageId);
}
for method findUserByIdInAndStageId spring data generates something
like
-- whereuser0_.id in (? , ? , ?...)) and stage1_.id=?
method findUserByUserIdsAndStageId do the same as findUserByIdInAndStageId , but in Query annotation you should write your query.
second search method findUserByUserIdsAndStageId named just for example , but I'll change it into something more readable like findUserWithStage(......)
PC don't use for domain model plural name like Users , use singular User

Related

How do I define a custom JOIN condition in a query method of a JpaRepository

We have an ArticleEntity that has several Prices, and the Prices are only valid in a certain time range, and there is only one valid price at a time.
In our entities this is modeled:
#Entity
public class ArticleEntity {
...
#OneToMany(mappedBy = "article", fetch = FetchType.LAZY)
private Collection<PriceEntity> prices;
}
Now, in our ArticleRepository I am trying to add custom query so that only valid prices are retrieved:
public interface ArtikelRepository extends JpaRepository<ArtikelEntity, Integer> {
#Query(value = "select a, p from ArtikelEntity a left join fetch PriceEntity p on p.article = a " +
"and (p.start is null or p.start <= :date) " +
"and (p.end is null or p.end >= :date) ")
Collection<ArtikelEntity> findAllWithPriceOnDate(#Param("date") LocalDate date);
}
Unfortunately this does not work, as Hibernate/JPA is requesting all prices afterwards again, instead of populating the price list with just the result of this join query.
Can I tell Hibernate/JPA to use the data from the custom query? And how?
It's not really possible unless you use Hibernate filters. I would recommend you to look into a DTO solution though, because even with filters, you could run into a situation where you change the collection and all of a sudden Hibernate flushes the state i.e. removes elements which are filtered out.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(ArtikelEntity.class)
public interface ArtikelDto {
#IdMapping
Long getId();
String getName();
#Mapping("prices[(start is null or start <= :date) " +
"and (end is null or end >= :date)]")
Set<PriceDto> getValidPrices();
#EntityView(PriceEntity.class)
interface PriceDto {
#IdMapping
Long getId();
BigDecimal getPrice();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ArtikelDto a = entityViewManager.find(entityManager, ArtikelDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ArtikelDto> findAll(#OptionalParam("date") LocalDate date, Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

How to get a List in one of the fields of DTO JPA Repository

I have a DTO and one of its field is a list. So, there is a main table and another table with one to many relations.
Code:
public class DTO {
id;
List<String> name;
}
public interface Repository extends JpaRepository<Table1, Long> {
#Query("SELECT new abc.com.DTO (" +
" r.id," +
" name"+
" )" +
" FROM Table1 r" +
" join Table2 c on c.xyz.id = r.abc.id"+
" WHERE r.type = :type"
)
List<DTO> find(#Param("type") final String type);
}
say I have in table T1 a row against which we have 3 rows in table 2.
I want dto to get me object obj1 with a list of all 3 names for table2
Currently it returns me 3 separate dtos with 3 rows of table2
As it is stated in the JPA specification (see section 4.8 SELECT Clause):
The SELECT clause can contain one or more of the following elements: an identification variable that ranges over an abstract schema type, a single-valued path expression, a scalar expression, an aggregate expression, a constructor expression.
Note that the SELECT clause must be specified to return only single-valued expressions. So, the query like below is therefore not valid:
SELECT o.lineItems FROM Order AS o
This is not possible with JPA directly as constructor expressions always use flat results. Spring Data Projections should support this, but in an inefficient way i.e. it selects entities and just provides a "view" of these entities through an interface.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Table1.class)
public interface DTO {
#IdMapping
Long getId();
#Mapping("Table2[xyz.id = VIEW(abc.id)].name")
Set<String> getNames();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
DTO a = entityViewManager.find(entityManager, DTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
public interface Repository extends JpaRepository<Table1, Long> {
List<DTO> findByType(#Param("type") final String type);
}

How to get the specific property value from .properties file in Spring Data Repository interface method #Query

I am able to get the property value in Spring classes like below:
#Value("${database.name}")
private String databaseName;
I have to execute a native query by joining different tables which are in different databases.
#Query(value="select t1.* FROM db1.table1 t1 INNER JOIN db2.table2 t2 ON t2.t1_id1 = t1.id1")
Instead of hard coding database names i.e., db1 and db2 here, I have to get them from properties file.
how to get the property value inside the #Query annotation in Spring Data JPA Repository ?
I don't know if it is possible, but if not, you can consider this approach:
Instead of using properties in Repository's #Query directly, you can use params in the query but when you call the actual method - you can provide values from .properties.
Imagine you have simple repository:
public interface UserRepository extends JpaRepository<User, Long> {
// query with param
#Query("select u from User u where u.lastname = :lastname")
User findByLastname(#Param("lastname") String lastname);
}
Then, let's say you have some Service or Controller where you need to use your Repository - you can inject properties there and pass them to your method:
#Service
public class UserService {
// this comes from .properties
#Value("${user.lastName}")
private String userLastName;
#Autowired
private UserRepository userRepository;
public User getUser() {
// you pass it as param to the repo method which
// injects it into query
return userRepository.findByLastname(userLastName);
}
}
This is just an example. But I believe it may be useful.
Happy hacking :)

select distinct values using spring data rest

I'm using java 8 and spring-data-rest to create API on my data.
I have a table Car(id, name, date...)
I'm trying to have an endpoint to retrieve distinct car names.
here's my Repository :
#RepositoryRestResource(path = "cars")
public interface CarRepository extends JpaRepository<Car, Long> {
//What i want to do
//#Query(value = "select distinct c.name as name from Car c")
#Query(value = "select distinct c from Car c")
List<Car> findDistinctName();
}
The query commented does not work , i have an exception
java.lang.IllegalArgumentException: PersistentEntity must not be null!
apparently this is the normal behavior of SDR.
I tried another solution by using Projections
#Projection(name = "name", types = {Car.class})
public interface CarName {
String getName();
}
but i cant get the distinct values, any idea?
Thank you :)
All you need, to do if you need a distinct list of cars, is such this query method:
public interface CarRepository extends JpaRepository<Car, Long> {
List<Car> findDistinctBy();
}
Spring Data JPA supports the Distinct keyword in repository query methods to set a distinct flag on the query to be created.
I found an (ugly) workaround, using jpql :
#Query(value = "select c from Car C WHERE c.id IN (SELECT min(ca.id) FROM Car ca Group by ca.name)")
I think you should remove the #Query annotation.
Just
List<Car> findDistinctName(); or List<Car> findNameDistinct(); should suffice.
It will automatically generate the query select c.name from Car c

Spring Data: "delete by" is supported?

I am using Spring JPA for database access. I am able to find examples such as findByName and countByName, for which I dont have to write any method implementation. I am hoping to find examples for delete a group of records based on some condition.
Does Spring JPA support deleteByName-like delete? Any pointer is appreciated.
Regards and thanks.
Deprecated answer (Spring Data JPA <=1.6.x):
#Modifying annotation to the rescue. You will need to provide your custom SQL behaviour though.
public interface UserRepository extends JpaRepository<User, Long> {
#Modifying
#Query("delete from User u where u.firstName = ?1")
void deleteUsersByFirstName(String firstName);
}
Update:
In modern versions of Spring Data JPA (>=1.7.x) query derivation for delete, remove and count operations is accessible.
public interface UserRepository extends CrudRepository<User, Long> {
Long countByFirstName(String firstName);
Long deleteByFirstName(String firstName);
List<User> removeByFirstName(String firstName);
}
Derivation of delete queries using given method name is supported starting with version 1.6.0.RC1 of Spring Data JPA. The keywords remove and delete are supported. As return value one can choose between the number or a list of removed entities.
Long removeByLastname(String lastname);
List<User> deleteByLastname(String lastname);
2 ways:-
1st one Custom Query
#Modifying
#Query("delete from User where firstName = :firstName")
void deleteUsersByFirstName(#Param("firstName") String firstName);
2nd one JPA Query by method
List<User> deleteByLastname(String lastname);
When you go with query by method (2nd way) it will first do a get call
select * from user where last_name = :firstName
Then it will load it in a List
Then it will call delete id one by one
delete from user where id = 18
delete from user where id = 19
First fetch the list of object, then for loop to delete id one by one
But, the 1st option (custom query),
It's just a single query
It will delete wherever the value exists.
Since in 2nd option it is making multiple DB query, try to use the first option.
Go through this link too https://www.baeldung.com/spring-data-jpa-deleteby
If you take a look at the source code of Spring Data JPA, and particularly the PartTreeJpaQuery class, you will see that is tries to instantiate PartTree.
Inside that class the following regular expression
private static final Pattern PREFIX_TEMPLATE = Pattern.compile("^(find|read|get|count|query)(\\p{Lu}.*?)??By")
should indicate what is allowed and what's not.
Of course if you try to add such a method you will actually see that is does not work and you get the full stacktrace.
I should note that I was using looking at version 1.5.0.RELEASE of Spring Data JPA
If you will use pre defined delete methods as directly provided by spring JPA then below two queries will be execute by the framework.
First collect data(like id and other column) using by execute select query with delete query where clause.
then after getting resultSet of first query, second delete queries will be execute for all id(one by one)
Note : This is not optimized way for your application because many queries will be execute for single MYSQL delete query.
This is another optimized way for delete query code because only one delete query will execute by using below customized methods.
#NamedNativeQueries({
#NamedNativeQuery(name = "Abc.deleteByCreatedTimeBetween",
query = "DELETE FROM abc WHERE create_time BETWEEN ?1 AND ?2")
,
#NamedNativeQuery(name = "Abc.getByMaxId",
query = "SELECT max(id) from abc")
})
#Entity
public class Abc implements Serializable {
}
#Repository
public interface AbcRepository extends CrudRepository {
int getByMaxId();
#Transactional
#Modifying
void deleteByCreatedTimeBetween(String startDate, String endDate);
}
It works just
import org.springframework.transaction.annotation.Transactional;
#Transactional
Long removeAddressByCity(String city);
Yes , deleteBy method is supported
To use it you need to annotate method with #Transactional
here follows my 2 cents. You can also use native queries, like:
#Modifying
#Query(value="delete from rreo r where r.cod_ibge = ?1 and r.exercicio= ?2", nativeQuery = true)
void deleteByParameters(Integer codIbge, Integer exercicio);
#Query(value = "delete from addresses u where u.ADDRESS_ID LIKE %:addressId%", nativeQuery = true)
void deleteAddressByAddressId(#Param("addressId") String addressId);

Resources