How to return custom class from PagingAndSortingRepository in Spring Boot - spring-boot

I have a model that looks like this:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Sale {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(targetEntity = User.class)
private User customer;
#OneToOne(targetEntity = Product.class)
private Product product;
}
And I have a paging-and-sorting repository, like so:
public interface SaleRepository extends PagingAndSortingRepository<Sale, Long> {}
But I would like to the "findAll" method of the repository in order to return a custom class with certain fields (instead of all the data of the nested entities), while still maintain paging and sorting abilities. The list of objects I would like to return would contain something like:
saleId
customerId
customerName
productId
ProductName
prouctPrice
But I'm not sure how to actually do this.
My controller method accepts sorting and paging parameters and then does this:
Page<Sale> saleList = saleRepository.findAll(pageable);
return saleList;

You can create either and Interface or a DTO and use this as the result of a find declared in your repository:
Page<YourDTO> findAllAsDtos(Pageable pageable);
Read more about projections here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

Related

Spring data jpa find by between on id field

I am trying to retrieve some rows from my DB, like
select * from my_table where id between 1 and 100;
Is there any option in JpaRepository for between on primary key?
Any custom method can be written in JPARepo. You need to make sure you are following JPA rules. The field name should exist in the method, In bellow method Id is my field name in my Entity class.
Entity Class
#Entity
#Setter#Getter
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
}
Repo Class
#Repository
public interface CourseSprngDataRepo extends JpaRepository<Course, Long>{
List<Course> findByIdBetween(Long l, Long m);
List<Course> findByIdBetweenOrderByNameAsc(long l, long m);//Between and Order by another column ex
}

spring JPA query to find events by locationId and categoryId

This is my events entity.
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Events {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long eventId;
#NotBlank(message = "Please Add Event name ")
#Length(max =100 ,min =2)
private String eventName ;
private String eventDescription;
// Each event is going to be mapped to a Location
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(
name = "location_id",
referencedColumnName = "locationId"
)
#NotNull
private Location location ;
#Temporal(TemporalType.DATE)
Date eventStartDate;
#Temporal(TemporalType.DATE)
Date eventEndDate;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(
name = "category_id",
referencedColumnName = "categoryId"
)
#NotNull
private Categories categories;
}
In my controller ,i have access to locationId and categoryId as request params .
I am not getting how to define my eventsRepository to access by locationId and categoryId. What changes should i make to this repo for things to work .
#Repository
public interface EventsRepository extends JpaRepository<Events,Long> {
public Events findByCateoryAndLocation()
}
I think a few adjustments you need to get rid of the issue. The query builder uses actual column names, so if your column name is locationId, then use 'findByLocationId(Integer locationId)' as a prototype. And please make sure entity names suit table names.
#Repository
public interface EventRepository extends JpaRepository<Event, Integer>
{
Event findByLocationIdAndCategoryId(Integer locationId, Integer categoryId);
}
This is off-topic, but I would like to mention that please do not use Lombok in entity classes. Getter, setter, and construction generators are ok, but hascode and string generators would be dangerous if you use lazy initialization. You may not get benefits from lazy loadings.
You have 2 ways to get your jpa-query working:
Modify your JPA-Query:
#Repository
public interface EventsRepository extends JpaRepository<Events,Long>
{
public Events findByCateories_IdAndLocation_id(Long categoriesId, long locationId)
}
Use a custom query - annotate your jpa with #Query and use a native query
There is one additional point from my side.
Naming of your classes. You are using plural which conflicts with the business logic - especially to the DB-relations(see Events to Categories). I would use singular (Event, Category)
This is exactly what I did to solve this with the help of native query.
#Query(
value = "SELECT * FROM events where category_id = ?1 AND location_id = ?2",
nativeQuery = true
)
public List<Events> findByCategoryIdAndLocationIdIn(Long CategoryId , Long LocationId);

hibernate entity different implementation

I have a project with multiple implementation and an entity class Person.
In every implementation there is a different database, different table and different columns.
In the DAO layer and the business layer the code is the same.
How can I change only the persistence layer to have different implementation of Person entity class based on a profile and keep unchanged the rest of the code?
//I would like to change table and columns based on a profile
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
private String first_name;
private String last_name
//getters,setters
}
//I would like to keep DAO unchanged no matter the profile
public interface PersonDao {
public List<Person> listAll() throws Exception;
}
public class PersonDaoImpl implements PersonDao{
#Override
public List<Person> listAll() throws Exception{
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
...the rest of the code
}
}
Have a generic abstract Person and PersonDao which will be extended/implemented by other classes (e.g. MongoPerson, MysqlPersonDao, PersonV2... based on your requirements). But use only Person and PersonDao in your service layer.
Autowire with Spring using qualifiers and configurations

With JPA/JPQL is it possible to manually load and Entity with its OneToMany Association?

Suppose I have 2 entities, Parent and Child, where the parent contains 1..n children:
#Entity
#Data #NoArgsConstructor
public class Parent {
#Id #GeneratedValue
private long id;
private String basic;
private String detail;
#OneToMany(fetch = FetchType.EAGER)
private Set<Child> children = new HashSet<>();
public Parent(String basic, String detail, Set<Child> children) {...}
}
#Entity
#Data #NoArgsConstructor
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private String basic;
private String detail;
public Child(String basic, String detail) {...}
}
I can load Parent-entities by using a JpaRepository:
public interface ParentRepository extends JpaRepository<Parent, Long> { }
// in Controller or Service
List<Parent> parents = parentRepository.findAll();
I'm trying to use projections. For this reason I want to know if it is possible to manually load the parents with a Query, so that i could only load needed data. In a perfect world this could look something like this:
// Dtos, the String detail is not required for both Parent and Child
#Value
public class ParentDto {
long id;
String basic;
Collection<Child> children;
public ParentDto(long id, String basic, Collection<ChildDto> children) {...}
}
#Value
public class ChildDto {
long id;
String basic;
public ChildDto(long id, String basic) {...}
}
and
public interface ParentRepository extends JpaRepository<Parent, Long> {
// Projection - *NOT WORKING*, this is what i would like
#Query("select p.id, p.basic, p.children.id, p.children.basic from Parent p")
List<ParentDto> findAllProjected();
}
This obviously fails, because it will join the all parents with their child, resulting in amount_of_parents * amount_of_their_children rows. The ParentDto would need a contrustor public ParentDto(long id, String basic, ChildDto child) {...}, so i had n ParentDtos per parent where n is the number of children the parent has.
Do i have to manually group the rows by the parents Id's and collect the ChildDtos together? Can I solve this using a subselect? I know Jpa solves this by selecting the parents and doing 1 select for each parent, fetching their children (when i use the autogenerated repository method). I was really hoping that projections/views would be much easier as they are such a fundamental requirement for me and most likely many other applications. Having to always load all data, load only the wanted data - but without their associations or having to load the associations manually for each entity seems like a bumper.
Note: i did try InterfaceProjection but do not want to use it due to it loading all data and stripping away unnessecary parts only when serializing it to json.
Thanks in regards!

How to fetch only selected attributes of an entity using Spring JPA?

I'm using Spring Boot (1.3.3.RELEASE) and Hibernate JPA in my project. My entity looks like this:
#Data
#NoArgsConstructor
#Entity
#Table(name = "rule")
public class RuleVO {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 128, nullable = false, unique = true)
private String name;
#Column(name = "tag", length = 256)
private String tag;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleOutputArticleVO> outputArticles;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleInputArticleVO> inputArticles;
}
My repository looks like this:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}
In some cases I need to fetch only id and name attributes of entity RuleVO. How can I achieve this? I found a notice it should be doable using Criteria API and Projections but how? Many thanks in advance. Vojtech
UPDATE:
As has been pointed out to me, I'm lazy and this can very well be done hence I'm updating my answer after having looked around the web for a proper one.
Here's an example of how to get only the id's and only the names:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT r.id FROM RuleVo r where r.name = :name")
List<Long> findIdByName(#Param("name") String name);
#Query("SELECT r.name FROM RuleVo r where r.id = :id")
String findNameById(#Param("id") Long id);
}
Hopefully this update proves helpful
Old Answer:
Only retrieving the specific attributes name/id is not possible as this is not how spring was designed or any SQL database for that matter as you always select a row which is an entity.
What you CAN do is query over the variables in the entity, for instance:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public RuleVo findOneByName(String name);
public RuleVo findOneByNameOrId(String name, Long id);
public List<RuleVo> findAllByName(String name);
// etc, depending on what you want
}
You can modify these however you want w.r.t. your needs. You can call these methods directly via the autowired repository
See http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Section 5.3 for more options and examples
interface IdOnly{
String getId();
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public List<IdOnly> findAllByName(String name);
}
I notice that this is a very old post, but if someone is still looking for an answer, try this. It worked for me.
You can also define custom constructor to fetch specific columns using JPQL.
Example:
Replace {javaPackagePath} with complete java package path of the class
use as a constructor in JPQL.
public class RuleVO {
public RuleVO(Long id, String name) {
this.id = id;
this.name = name;
}
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name")
List<RuleVO> findIdByName(#Param("name") String name);
}
Yes, you can achieve it with projections. You have many ways to apply them:
If you could upgrade to Spring Data Hopper, it provides an easy to use support for projections. See how to use them in the reference documentation.
Otherwise, first of all create a DTO with the attributes you want to load, something like:
package org.example;
public class RuleProjection {
private final Long id;
private final String name;
public RuleProjection(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Of course, you could use Lombok annotations also.
Then, you can use in the JPQL queries like this:
select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name
Another option, if you want to avoid using DTO class names in your queries, is to implement your own query method using QueryDSL. With Spring Data JPA, you have to:
Create a new interface with the new method. Ex:
public interface RuleRepositoryCustom {
public List<RuleProjection> findAllWithProjection();
}
Change your repository to extend the new interface. Ex:
public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
...
Create an implementation of the Custom repository using the Spring Data JPA QueryDSL support. You have to previously generate the Q clases of QueryDSL, using its Maven plugin. Ex:
public class RuleRepositoryImpl {
public List<RuleProjection> findAllWithProjection() {
QRuleVO rule = QRuleVO.ruleVO;
JPQLQuery query = getQueryFrom(rule);
query.orderBy(rule.name.asc());
return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
}
}
You can do it by using #Query annotation(HQL).
Please refer to the Spring docs below:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
(search for #Query in spring document)

Resources