Spring JPA Projection findAll - java

Is it possible to use "findAll" for a JPARepository returning a Collection/List of Projections?
Example:
#Entity
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Integer id;
private String name;
private String pass;
(...)
}
public interface LoginProjection {
public String getName();
}
#Repository
public interface LoginRepository extends JpaRepository<Login, Long> {
Login findByName(String name);
#Query(value = "SELECT name FROM login", nativeQuery = true)
List<LoginProjection> findAllLoginProjection();
}
Using #Query it works! But it is not possible to use
List<LoginProjection> findAll();
Because LoginProjection it does not extends T (Login).
I was thinking if it is possible to give a "alias" for findAll like findAllXYZ that does the same thing as findAll.
Using filters it works too, but I do not want to use them:
List<LoginProjection> findAllByName(String name);
My main goal would be something like this:
#Repository
public interface LoginRepository extends JpaRepository<Login, Long> {
Login findByName(String name);
List<Login> findAll();
List<LoginProjection> findAllLoginProjection();
}
Which is pretty easy and with "zero #Query"

And add a method to the repository:
List<LoginProjection> findAllProjectedBy();
The method name can be simplified to findBy() to match the documentation example at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

I assume you're not using Spring Data REST, so #Projection won't help here. #pavel-molchanov showed one form of expressing the projection, another form is:
List<LoginProjection> findBy();
If you have more than one projection, you can avoid creating a method in the repository for each projection by using dynamic projections like e.g. this:
<T> List<T> findBy(Class<T> projection);
// usage e.g.
... = findBy(LoginProjection.class);
... = findBy(UserSummaryProjection.class);

if you want to be more flexible you can add to the repository this method
<T> List<T> findBy(Class<T> projection);
and then you can call it with the following
List<LoginProjection> rows = loginRepository.findBy(LoginProjection.class);
The advantage of this approach is that you can use all the projections you want using the same query

Related

What is the best way to use findAll with Pageable in Spring?

I have employee class
#Entity
class Employee {
#Id
Integer id;
String name;
Integer age;
}
and another class EmployeeInfo:
class EmployeeInfo {
Integer id;
String name;
}
Now, I need to build a service to get a paginated list of EmployeeInfo by using findAll(Pageable pageable)
from the repository
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
what is the best way to do that? I want to avoid getting the page from findAll and create a new object of EmployeeInfo then adding it to a list in a loop
You can utilize spring-data-jpa projections.
There are many ways to use them (open/closed projections, class or interface based, etc.), but since you already have a EmployeeInfo class, it can be achieved by defining a new method in your repository:
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Page<EmployeeInfo> getAllBy(Pageable pageable);
}
Note that your projection DTO class properties must exactly match properties in the aggregate root (entity class).
Also reference documentation suggest to define .equals() and .hashcode() methods.
Other methods can be found in the official documentation.

Spring JPA selecting from where clause

I am using Spring JPA to perform all database operations. However I don't know how to select specific rows (connected by simple WHERE clause) from a table in Spring JPA?
For example:
SELECT * FROM user where name=agrawalo AND email=abc#example.com
User Class:
#Entity
Class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
// Getters and Setters
}
Repository:
public interface UserRepository extends JpaRepository<User, Integer> {
}
You don't need to write queries for such simple things if you are using spring-data-jpa. you can write a method name and spring-data will formulate a query based on your method name and get the results.
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByNameAndEmail(String name, String email)
}
Create a method like above and call the method with the required arguments.
If you don't want(not advisable) to use Optional, you can just use User as return type. In such case, if there are no entries matching your arguments then you would have null returned.
public interface UserRepository extends JpaRepository<User, Integer> {
public User findUserByNameAndEmail(String name,String email);
}
Implementation will be created on the fly.
I know I am very very late to this but I still want to provide another solution that one would like to use. This is particularly useful when you think the queries generated by method names do not serve the purpose that you want and you really want to write the native queries. To do that, you can actually use the #Query annotation in your repository and put your query inside it like below:
#Query(value = "SELECT * FROM user where name = ?1 AND email = ?2", nativeQuery = true)
List<User> getUserByNameAndEmail(String name, String email);
Here the #Query tells that the query provided in the value attribute needs to be executed and the nativeQuery attribute tells that it is a native sql query.
Notice that values for name and email are provided as ?1 and ?2 respectively which means these are placeholders and will be replaced by the parameters that getUserByNameAndEmail repository method will receive at runtime in variables name and email.
Simply you can declare below method in you repository interface, implementation will be taken care by Spring-data-jpa
User findByNameAndEmail(String name, String email);

Spring Boot extending CrudRepository

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

Filter child object in Spring Data Query

I have a following domain model:
Playlist -> List<PlaylistItem> -> Video
#Entity
class Playlist{
// id, name, etc
List<PlaylistItem> playlistItems;
// getters and setters
}
#Entity
class PlaylistItem{
// id, name, etc.
Video video;
// getters and setters
}
#Entity
class Video{
// id, name, etc.
boolean isDeleted;
// getters and setters
}
And my repository:
public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
List<Playlist> findAll();
}
Now, how do I return a playlist with only existing videos, ie, if there are three videos in the database assigned to that playlist item and one of those videos has isDeleted set to true, then I need to get only two items instead.
All you have to do is declare this method on your PlaylistRepository interface:
List<Playlist> findByPlaylistItemsVideoIsDeleted(boolean isDeleted);
And call it like this:
playListRepository.findByPlaylistItemsVideoIsDeleted(false);
That will return all playlist with videos that are not removed.
You may have already resolved this issue, but I thought I would contribute this in hopes it might help you, or anyone else visiting this page.
Using Spring JPA Specifications, you would:
Enable your PlaylistRepository to use JPA Specifications
Write the Specification as a reusable method
Make use of the Specification as the query
Here are the details.
1. Implement JpaSpecificationExecutor
Update PlaylistRepository to implement JpaSpecificationExecutor. This adds find* methods that accept Specification<T> parameters to your PlaylistRepository.
public interface PlaylistRepository extends JpaRepository<Playlist, Long>,
JpaSpecificationExecutor<Playlist> {
}
2. Create the Specification
Create a class with a static method for use in creating a reusable Specification.
public final class PlaylistSpecifications {
private PlaylistSpecifications() {}
public static Specification<Playlist> hasExistingVideos() {
return (root, query, cb) -> {
return cb.equal(root.join("playlistItems").join("video")
.get("isDeleted"), false);
};
}
}
Using root.join (and subsequent joins) is similar to using JOIN in SQL. Here, we are joining on the fields of classes, instead of on columns of tables.
3. Issue the Query
I don't know how you plan to issue your query, but below is an example of how it could be done in a "service" class:
#Service
public class PlaylistService {
#Autowired
private PlaylistRepository playlistRepository;
public List<Playlist> findPlaylistsWithExistingVideos() {
Specification<Playlist> spec = PlaylistSpecifications.hasExistingVideos();
return playlistRepository.findAll(spec);
}
}
Hope this helps!
Maksim, you could use the #query annotation like this :
public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
#Query("select playlist from Playlist playlist
fetch join playlist.playlistItems itens
fetch join itens.video as video
where video.isDeleted = false")
List<Playlist> findAll();
}
Or even better way :
public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
#Query("select playlist from Playlist playlist
fetch join playlist.playlistItems itens
fetch join itens.video as video
where video.isDeleted = :hasVideo ")
List<Playlist> findPlayList(#Param("hasVideo") boolean hasVideo);
}
You can look into Spring Data Specifications. You use them by calling repository.findAll(s);
Specifications allow you add on arbitrary conditions to your query, including the filter you want to add. Another nice thing about Specifications is that they can be type-safe. See here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications

How to delete items in MongoRepository using query annotation?

I'm using Spring Data with MongoDB using MongoRepository.
I was wondering if it is possible do a delete by filter using query annotation. I have been looking here and google and I cannot find any documentation.
#Query(value="{'id' : $0}", delete = true)
public Person deleteById (String id);
Maybe you can use repository delete queries. Here is an example from documentation:
public interface PersonRepository extends MongoRepository<Person, String> {
List <Person> deleteByLastname(String lastname);
Long deletePersonByLastname(String lastname);
}
Using return type List will retrieve and return all matching documents before actually deleting them. A numeric return type directly removes the matching documents returning the total number of documents removed.
Try this, it's work for me.
#Repository
public interface DepartmentDao extends MongoRepository<Department, String> {
#DeleteQuery
void deleteByDepartment(String department);
}
OR
#Query(value="{'_id' : ?0}", delete = true)
public void deleteById(String id);
Unfortunately spring data doesn't provides any method to delete documents based on a query. And the #Query annotation is only for find documents.
What you could do is implement a custom repository that deletes documents based on what you want.
How to delete a list of ids in the query ?
#Query(value="{idList : $0}", delete = true)
Repository:
#Component
public interface SomeRepository extends MongoRepository<SomeObject, String> {
#Query("{ '_id' : ?0 }")
SomeObject findById(String _id);
}
Code in some class:
#Autowired
private SomeRepository pRepo;
public void delete(String id) {
pRepo.delete(pRepo.findById(id));
}
#Repository
public interface DepartmentDao extends MongoRepository<Department, String> {
void deleteByDepartment(String department);
}
is clean and shorter.

Categories

Resources