I am using spring data and my DAO looks like
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public findAllOrderByIdAsc(); // I want to use some thing like this
}
In above code, commented line shows my intent. Can spring Data provides inbuilt functionality
to use such a method to find all records order by some column with ASC/DESC?
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public List<StudentEntity> findAllByOrderByIdAsc();
}
The code above should work. I'm using something similar:
public List<Pilot> findTop10ByOrderByLevelDesc();
It returns 10 rows with the highest level.
IMPORTANT:
Since I've been told that it's easy to miss the key point of this answer, here's a little clarification:
findAllByOrderByIdAsc(); // don't miss "by"
^
AFAIK, I don't think this is possible with a direct method naming query. You can however use the built in sorting mechanism, using the Sort class. The repository has a findAll(Sort) method that you can pass an instance of Sort to. For example:
import org.springframework.data.domain.Sort;
#Repository
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentDAO studentDao;
#Override
public List<Student> findAll() {
return studentDao.findAll(sortByIdAsc());
}
private Sort sortByIdAsc() {
return new Sort(Sort.Direction.ASC, "id");
}
}
Simple way:
repository.findAll(Sort.by(Sort.Direction.DESC, "colName"));
Source: https://www.baeldung.com/spring-data-sorting
Please have a look at the Spring Data JPA - Reference Documentation, section 5.3. Query Methods, especially at section 5.3.2. Query Creation, in "Table 3. Supported keywords inside method names" (links as of 2019-05-03).
I think it has exactly what you need and same query as you stated should work...
Yes you can sort using query method in Spring Data.
Ex:ascending order or descending order by using the value of the id field.
Code:
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public findAllByOrderByIdAsc();
}
alternative solution:
#Repository
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentDAO studentDao;
#Override
public List<Student> findAll() {
return studentDao.findAll(orderByIdAsc());
}
private Sort orderByIdAsc() {
return new Sort(Sort.Direction.ASC, "id")
.and(new Sort(Sort.Direction.ASC, "name"));
}
}
Spring Data Sorting: Sorting
I try in this example to show you a complete example to personalize your OrderBy sorts
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.data.domain.Sort;
/**
* Spring Data repository for the User entity.
*/
#SuppressWarnings("unused")
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List <User> findAllWithCustomOrderBy(Sort sort);
}
you will use this example :
A method for build dynamically a object that instance of Sort :
import org.springframework.data.domain.Sort;
public class SampleOrderBySpring{
Sort dynamicOrderBySort = createSort();
public static void main( String[] args )
{
System.out.println("default sort \"firstName\",\"name\",\"age\",\"size\" ");
Sort defaultSort = createStaticSort();
System.out.println(userRepository.findAllWithCustomOrderBy(defaultSort ));
String[] orderBySortedArray = {"name", "firstName"};
System.out.println("default sort ,\"name\",\"firstName\" ");
Sort dynamicSort = createDynamicSort(orderBySortedArray );
System.out.println(userRepository.findAllWithCustomOrderBy(dynamicSort ));
}
public Sort createDynamicSort(String[] arrayOrdre) {
return Sort.by(arrayOrdre);
}
public Sort createStaticSort() {
String[] arrayOrdre ={"firstName","name","age","size");
return Sort.by(arrayOrdre);
}
}
Combining all answers above, you can write reusable code with BaseEntity:
#Data
#NoArgsConstructor
#MappedSuperclass
public abstract class BaseEntity {
#Transient
public static final Sort SORT_BY_CREATED_AT_DESC =
Sort.by(Sort.Direction.DESC, "createdAt");
#Id
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
#PrePersist
void prePersist() {
this.createdAt = LocalDateTime.now();
}
#PreUpdate
void preUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
DAO object overloads findAll method - basically, still uses findAll()
public interface StudentDAO extends CrudRepository<StudentEntity, Long> {
Iterable<StudentEntity> findAll(Sort sort);
}
StudentEntity extends BaseEntity which contains repeatable fields (maybe you want to sort by ID, as well)
#Getter
#Setter
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
class StudentEntity extends BaseEntity {
String firstName;
String surname;
}
Finally, the service and usage of SORT_BY_CREATED_AT_DESC which probably will be used not only in the StudentService.
#Service
class StudentService {
#Autowired
StudentDAO studentDao;
Iterable<StudentEntity> findStudents() {
return this.studentDao.findAll(SORT_BY_CREATED_AT_DESC);
}
}
Related
as the title indicate i have a native SQL query in my repository like that
#Repository
public interface BesoinRepository extends CrudRepository<Statistic, Long>{
#Query(value="SELECT etat_besoin AS 'state',COUNT(DISTINCT id) AS 'number' FROM besoin WHERE YEAR(date_creation)=:year GROUP BY etat_besoin ",nativeQuery=true)
List<Object> getStatistic(#Param("year") int year);
}
class statistic
package fr.solinum.management.model;
public class Statistic {
private String state;
private int number;
public String getState() {
return state;
}
public int getNumber() {
return number;
}
}
and this is working ok but i want the return type of getStatistic to be List<Statistic> and without creating the table Statistic in my database since i dont need it. in other word i want only to read from the database and return the result as a class. so what are the changes in the model and in the repository or in the controller or what is the optimal approach for my problem? note that i modified my model class as following to solve that but i dont think that this is the optimal or the good approach.
package fr.solinum.management.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "besoin")
public class Statistic {
#Id
private int id;
private String state;
private int number;
public String getState() {
return state;
}
public int getNumber() {
return number;
}
}
I can think of three approaches:
Use a Database View.
With this, you can have your entity as you want mapped as any other table to the View. You can use a Repository with no problems and you don't create a different table.
Map your List in a class and access this data from a service/DAO.
This is more labor intensive and might not "fit" with the rest of your code. Instead of using your repository directly, you would use another class. This class would do the querying of Objects and map them before delivering them to the controller or service where they are being requested. It's like doing JPA's work, really.
Take the following code as example:
#Component
public class StatisticDAO {
#Autowired
private BesoinRepository besoinRepository;
public List<Statistic> getStatistic(int year) {
List<Statistic> result = new ArrayList<>();
List<Object[]> temp = besoinRepository.getStatistic(year);
temp.stream().forEach(data -> result.add(build(data)));
return result;
}
private Statistic build(Object[] data) {
return new Statistic(String.valueOf(data[0]), (Integer)data[1]);
}
}
You'll need to change the type of the list to Object[] and check the order in which order JPA is returning the attributes.
Use interface-based projections.
I'd recommend approaches 1 and 3, but is up to you and whatever suits you better.
I study the spring+Hibernate bundle there Is an entity:
public class PersonEntity {
private Long id;
private String name;
private Integer age;
private City city;
private Countrycountry;
...
}
I need to perform filtering. data in this table to display in the browser window. I was thinking of making an implementation in the service of the following methods:
.findByName(name);
.findByNameAndAge(name, age);
.findByNameAndAge(name, age, city);
.findByNameAndAge(name, city);
...
But it turns out that there are too many options for methods. How to make one universal method, i.e. something like a collection in which you can add as many parameters as you need. I started reading on this issue and got completely confused. Somewhere they write about #Filter, somewhere about Hibernate Search, there is also Spring Data Elasticsearch. Tell me the easiest and most relevant way to implement this. If there are links to real examples, I would be very grateful.
Dao:
public interface PersonDao extends GeneralDAO<PersonEntity>{
public List<PersonEntity> searchName(String name);
public List<PersonEntity> searchAllFields(
String name,
Integer age,
City city);
}
GeneralDAO describes all standard methods such as get, save, etc. Repository:
#Repository
public interface PersonRepository extends JpaRepository<PersonEntity, Long> {
List<PersonEntity> findByNameIgnoreCase(String name);
List<PersonEntity> findByNameAndAgeAndCity(
String name,
Integer age,
City city);
}
Service
#Service
#Transactional
public class PersonService implements PersonRepository {
#Autowired
private PersonRepository personRepository;
...
описание всех стандартных методов чтения-записи в БД
#Override
public List<PersonEntity> searchName(String name) {
return productTypeRepository.findByNameIgnoreCase(name);
}
#Override
public List<PersonEntity> searchAllFields(
String name,
Integer age,
City city) {
return personRepository.findByNameAndAgeAndCity(
name,
age,
city);
}
}
In the ad and call controller:
#Autowired
private PersonService personService;
...
personService.searchAllFields(...);
The searchName method works fine, but searchAllFields doesn't. It always returns an empty list, even if I specify one name, the rest = null
I tried to change the method in the service:
List<PersonEntity> findByNameIsNullAndAgeIsNullAndCityIsNull
Spring responds with an error:
"Error creating bean with name personRepository. At least 1 parameter(s) provided but only 0 parameter(s) present in query".
searchAllFields Method is returning an empty list because it contains findByNameAndAgeAndCity which means all the parameters are mandatory and the condition between them is AND so better change to OR (findByNameOrAgeOrCity) so that if you pass single value like name and rest = null then also you will get data and vice-versa.
You should really consider using Criteria API since you are using Spring & Spring Data, you can use JPA Specifications as a complete example see the following example:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
....
public interface PersonRepository extends JpaRepository<PersonEntity, Long>, JpaSpecificationExecutor {
}
// Notice the the second extended interface JpaSpecificationExecutor ^
in service:
import org.springframework.data.jpa.domain.Specification;
....
public List<PersonEntity> list(PersonEntityFilter personFilter) {
List<PersonEntity> filteredPersons = personsRepository.findAll
(Specification.where(PersonEntitySpecs.findByFilters(personFilter)));
return filteredPersons;
}
PersonEntityFilter is the payload coming from your controller submitted by your clients or your UI and it is a simple class that groups all fields you want to filter by
public class PersonEntityFilter {
private String name;
private Integer age;
private City city;
// getters & setters
}
PersonEntitySpecs is where you put your specs (criteria query logic)
public class PersonEntitySpecs {
public static Specification<PersonEntity> findByFilters(PersonEntityFilter personEntityFilter) {
return (root, query, cb) -> {
final Collection<Predicate> predicates = new ArrayList<>();
if (personEntityFilter.getName() != null) {
predicates.add(cb.like(root.get("name"), "%" + personEntityFilter.getName() + "%")));
}
if (personEntityFilter.getAge() != null) {
predicates.add(cb.equal(root.get("age"), personEntityFilter.getAge()));
}
if (personEntityFilter.getCity() != null) {
Join<PersonEntity, CityEntity> personCityJoin = root.join("city");
predicates.add(cb.equal(personCityJoin.get("id"), personEntityFilter.getCity().getId()));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
I am using #RepositoryRestResource facility,but I would like to do a calculation (depending on external service) over a transient field each time the user executes a read operation GET, find etc..
If only there was an AfterGet/AfterFind event at list I could handle the modification by extending AbstractRepositoryEventListener
Any clean suggestion?
I found the way through #Alan Hay suggestion.
#Entity
#EntityListeners(TransientFieldResolver.class)
public class Entity {
private Long id;
private String transientField;
}
#Component
public static class TransientFieldResolver {
private static ExternalService externalService;
#Autowired
public void setExternalService(ExternalService externalService) {
TransientFieldResolver.externalService = externalService;
}
#PostLoad
public void onPostLoad(final Entity entity) {
if (Objects.isNull(entity.getTransientField())) {
TransientFieldResolver.externalService.fillTransientField(entity);
}
}
}
I am using spring data and my DAO looks like
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public findAllOrderByIdAsc(); // I want to use some thing like this
}
In above code, commented line shows my intent. Can spring Data provides inbuilt functionality
to use such a method to find all records order by some column with ASC/DESC?
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public List<StudentEntity> findAllByOrderByIdAsc();
}
The code above should work. I'm using something similar:
public List<Pilot> findTop10ByOrderByLevelDesc();
It returns 10 rows with the highest level.
IMPORTANT:
Since I've been told that it's easy to miss the key point of this answer, here's a little clarification:
findAllByOrderByIdAsc(); // don't miss "by"
^
AFAIK, I don't think this is possible with a direct method naming query. You can however use the built in sorting mechanism, using the Sort class. The repository has a findAll(Sort) method that you can pass an instance of Sort to. For example:
import org.springframework.data.domain.Sort;
#Repository
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentDAO studentDao;
#Override
public List<Student> findAll() {
return studentDao.findAll(sortByIdAsc());
}
private Sort sortByIdAsc() {
return new Sort(Sort.Direction.ASC, "id");
}
}
Simple way:
repository.findAll(Sort.by(Sort.Direction.DESC, "colName"));
Source: https://www.baeldung.com/spring-data-sorting
Please have a look at the Spring Data JPA - Reference Documentation, section 5.3. Query Methods, especially at section 5.3.2. Query Creation, in "Table 3. Supported keywords inside method names" (links as of 2019-05-03).
I think it has exactly what you need and same query as you stated should work...
Yes you can sort using query method in Spring Data.
Ex:ascending order or descending order by using the value of the id field.
Code:
public interface StudentDAO extends JpaRepository<StudentEntity, Integer> {
public findAllByOrderByIdAsc();
}
alternative solution:
#Repository
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentDAO studentDao;
#Override
public List<Student> findAll() {
return studentDao.findAll(orderByIdAsc());
}
private Sort orderByIdAsc() {
return new Sort(Sort.Direction.ASC, "id")
.and(new Sort(Sort.Direction.ASC, "name"));
}
}
Spring Data Sorting: Sorting
I try in this example to show you a complete example to personalize your OrderBy sorts
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.data.domain.Sort;
/**
* Spring Data repository for the User entity.
*/
#SuppressWarnings("unused")
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List <User> findAllWithCustomOrderBy(Sort sort);
}
you will use this example :
A method for build dynamically a object that instance of Sort :
import org.springframework.data.domain.Sort;
public class SampleOrderBySpring{
Sort dynamicOrderBySort = createSort();
public static void main( String[] args )
{
System.out.println("default sort \"firstName\",\"name\",\"age\",\"size\" ");
Sort defaultSort = createStaticSort();
System.out.println(userRepository.findAllWithCustomOrderBy(defaultSort ));
String[] orderBySortedArray = {"name", "firstName"};
System.out.println("default sort ,\"name\",\"firstName\" ");
Sort dynamicSort = createDynamicSort(orderBySortedArray );
System.out.println(userRepository.findAllWithCustomOrderBy(dynamicSort ));
}
public Sort createDynamicSort(String[] arrayOrdre) {
return Sort.by(arrayOrdre);
}
public Sort createStaticSort() {
String[] arrayOrdre ={"firstName","name","age","size");
return Sort.by(arrayOrdre);
}
}
Combining all answers above, you can write reusable code with BaseEntity:
#Data
#NoArgsConstructor
#MappedSuperclass
public abstract class BaseEntity {
#Transient
public static final Sort SORT_BY_CREATED_AT_DESC =
Sort.by(Sort.Direction.DESC, "createdAt");
#Id
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
#PrePersist
void prePersist() {
this.createdAt = LocalDateTime.now();
}
#PreUpdate
void preUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
DAO object overloads findAll method - basically, still uses findAll()
public interface StudentDAO extends CrudRepository<StudentEntity, Long> {
Iterable<StudentEntity> findAll(Sort sort);
}
StudentEntity extends BaseEntity which contains repeatable fields (maybe you want to sort by ID, as well)
#Getter
#Setter
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
class StudentEntity extends BaseEntity {
String firstName;
String surname;
}
Finally, the service and usage of SORT_BY_CREATED_AT_DESC which probably will be used not only in the StudentService.
#Service
class StudentService {
#Autowired
StudentDAO studentDao;
Iterable<StudentEntity> findStudents() {
return this.studentDao.findAll(SORT_BY_CREATED_AT_DESC);
}
}
I'm using Hibernate in a Spring Boot app. I'm making a new CrudRepository for all my Model objects, to do basic CRUD tasks. They look like this:
#Repository
public interface FoobarCrudRepo extends CrudRepository<Foobar, Long> {
}
But then I always need to do some additional things, like custom search queries with inequalities and such. I follow a pattern like this:
#Repository
public class FoobarDao {
#PersistenceContext
EntityManager em;
public List<Foobar> findFoobarsByDate(Date date) {
String sql = "select fb from Foobar fb where createdDate > :date";
...
return query.getResultList();
}
}
My question is, can I combine these two concepts into a single class? I tried making it an abstract class, like so:
#Repository
public abstract class FoobarCrudRepo extends CrudRepository<Foobar, Long> {
#PersistenceContext
EntityManager em;
public List<Foobar> findFoobarsByDate(Date date) {
String sql = "select fb from Foobar fb where createdDate > :date";
...
return query.getResultList();
}
}
But then Spring didn't create a bean for it.
How can I accomplish this?
Thanks!
There are lots of ways you could probably accomplish this. If you really need absolute control try this
interface FoobarRepositoryCustom{
List<Foobar> findFoobarsByDate(Date date);
}
interface FoobarRepository extends CrudRepository<Foobar, Long>, FoobarRepositoryCustom
public class FoobarRespoitoryImpl implements FoobarRepositoryCustom{
#PersistenceContext private EntityManager em;
public List<Foobar> findFoobarsByDate(Date date) {
String sql = "select fb from Foobar fb where createdDate > :date";
...
return query.getResultList();
}
}
There is also the possibility to go a simpler route and the query can be auto generated for you based on the method name. In your example you could just add this to your FoobarCrudRepo and Spring should do the rest assuming Foobar has a property named CreatedDate
List<Foobar> findByCreatedDateGreaterThan(Date date);
For reference on how Spring can generate queries based on the method name see this http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
Completely new to Spring Data, but having searched a bit it is my impression that you do not have to leave the interface to create custom logic - rather you would create either an annotated interface method, an interface method that follows a special naming scheme or a default interface method with custom logic:
Screenshot from Baeldung: Introduction to Spring.
Here is a link to the documentation. Notice "table 4. Supported keywords inside method names" which can be used to create interface methods, whose name conveys information to the code generator about which query to create (See part of table below).
The problem here is abstract keyword.
#Repository
public abstract class FoobarCrudRepo extends CrudRepository<Foobar, Long>
Spring will not create a bean for a class unless it is a concrete class.
That's why you are getting a bean for it.
This is what worked for me...
#SpringBootApplication(scanBasePackages = { "com.myproject" })
#EnableJpaRepositories(basePackages="com.myproject.sprinbootapp.repository")
#EntityScan("com.myproject.sprinbootapp.model")
public class SpringbootAppWithDatabaseApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAppWithDatabaseApplication.class, args);
}
}
#Service
public class TopicService {
#Autowired
private TopicRepository topicRepository;
private List<Topics> topics = new ArrayList<Topics>();
public List<Topics> getAllTopics(){
List<Topics> listOfTopics = new ArrayList<Topics>();
topicRepository.findAll().forEach(listOfTopics::add);;
return listOfTopics;
}
}
#Entity
public class Topics {
#Id
private String id;
private String name;
public Topics(){
}
getters and setters...
}
public interface TopicRepository extends CrudRepository<Topics, String> {
}
we can use the JPA EntityManager for direct sql actions:
public interface VerificationsRepository extends
CrudRepository<Verification, Integer>,
DAOAccess
{ }
interface DAOAccess {
List findByEmail(String email);
}
class DAOAccessImpl implements DAOAccess {
#PersistenceContext private EntityManager em;
public List findByEmail(String email) {
String sql =
"select * from verifications where email = ?";
Query query = em.createNativeQuery(sql, Verification.class)
.setParameter(1, email);
return query.getResultList();
}
}