How to write query method for sorting the results based on the field which is part of parent entity spring data jpa - spring-boot

In spring data JPA we can write query methods to execute query. I have two entities
class A {
#Id
#Column
private String id;
private String name;
#ManyToOne
#JoinColumn(name = "b_field")
private B b;
}
class B {
#Id
#KeyField
#Column
private String id;
private String b_field
}
I want to write a query method "findByNameOrderByb_field" i.e find all the rows having name as provided and order the results on the basis of class B's field i.e b_field. Writing above function is not returning ordered results. Is there any way I can write this orderby query method in spring boot.
public interface ARepository extends CrudRepository<A, String> {
List<A> findByNameOrderByb_field(String name);
}
the above function is not returning ordered results based on B's b_field.

To access properties of B you have to include the field name of B in the declaration of the query method.
public interface ARepository extends CrudRepository<A, String> {
List<A> findByNameOrderByb_b_field(String name);
}
More information how SpringData resolves nested Properties can be found under:
Query Property Expressions

Related

Spring Data JPA save child object with the ID of parent object

I have two objects, one parent and one child as follows :
#Entity
#Table(name="category")
public class CategoryModel {
private #Id #GeneratedValue Long id;
private String name;
#OneToMany(mappedBy="category", cascade=CascadeType.PERSIST)
private List<AttributeModel> attributes;
}
#Entity
#Table(name="attribute")
public class AttributeModel {
private #Id #GeneratedValue Long id;
private String name;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="category_id")
private CategoryModel category;
}
I also have dtos which maps to these model objects but I ommited them.
When I try to save a category object with this payload Attribute values are also created in the attribute table but with null category ids.
{
"name":"Chemicals",
"attributes":[
{"name": "volume"}, {"name":"humidity"}
]
}
What can I do to have my attribute values persisted into the database with the category id which is created before them?
First of all, this problem is not a "Spring Data JPA" problem, it is a JPA (probably Hibernate) problem.
Analysis
Since you left out the code for the controller and the JSON mapping, I have to guess a bit:
fact 1: The relationship between category and attributes is controlled by the attribute AttributeModel.category but not by CategoryModel.attributes. (That is how JPA works).
observation 2: Your JSON object define CategoryModel.attributes (i.e. opposite to how JPA works).
Without knowing your JSON mapping configuration and controller code, I would guess that the problem is: that your JSON mapper does not set the AttributeModel.category field when it deserialises the JSON object.
Solution
So you need to instruct the JSON mapper to set the AttributeModel.category field during deserialisation. If you use Jackson, you could use:
#JsonManagedReference and
#JsonBackReference
#Entity
#Table(name="category")
public class CategoryModel {
...
#JsonManagedReference
#OneToMany(mappedBy="category", cascade=CascadeType.PERSIST)
private List<AttributeModel> attributes;
}
#Entity
#Table(name="attribute")
public class AttributeModel {
...
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="category_id")
private CategoryModel category;
}
I solved this by manually setting child object's reference to the parent object as follows :
public Long createCategory(CategoryDto categoryDto) {
CategoryModel categoryModel = categoryDto.toModel(true,true);
categoryModel.getAttributes().forEach(a -> a.setCategory(categoryModel));
return categoryRepository.save(categoryModel).getId();
}

Filter based on Integer Comparison in Spring data redis

I am using spring-data-redis to communicate with database.
I have entity class like below
#RedisHash(value = "employee")
public class Employee
{
#Id
private long id;
#Indexed
private String name;
#Indexed
private int age;
private Address address;
...... ...... ......
}
I want to filter the employees based on age group. For example, age lesser than 35 (age<35). How to achieve this in below repository?
#Repository
public interface EmployeeRepo extends CrudRepository<Employee, Long>
{
public Employee findByName(String name);
}
I dont prefer to load complete data from table and do search using any loop/stream.
I don't think it does. I tried to implement what you tried to do using
#Index
long lastUpdatedOn;
when I checked the redis it gives a key entity:lastUpdatedOn:160.... and I tried searching for it using ZRANGE which gives no results.

Retrieving foreign key attributes in DTO

I am using java+Spring framework+Hibernate for creating rest api but I have stumbled upon retrieving details of a table using foreign key attributes.
I have the following tables::
https://i.stack.imgur.com/lG7UR.png
I am retrieving all the ratings given using product id and then mapping to DTO, now I also want to populate the username using idusers as this is my foreign key.
Same is the case when I try to retrieve ratings given by the users, instead of displaying idproducts I want to display the product name and product description as It is a foreign key.
Any advice on how to do so using DTO's.
This is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
Assuming you have an entity model like this
#Entity
public class User {
#Id
Integer id;
String role;
String username;
String password;
boolean enabled;
}
#Entity
public class Product {
#Id
Integer id;
String imageUrl;
String category;
int productPrice;
int productQuantity;
String productName;
String productDesc;
#OneToMany(mappedBy = "product")
Set<Rating> ratings;
}
#Entity
public class Rating {
#Id
Integer id;
int rating;
String review;
String ratingscol;
#ManyToOne(fetch = LAZY)
Product product;
#ManyToOne(fetch = LAZY)
User user;
}
A DTO mapping for your model could look as simple as the following
#EntityView(Rating.class)
interface RatingDto {
Integer getId();
UserDto getUser();
ProductDto getProduct();
}
#EntityView(User.class)
interface UserDto {
Integer getId();
String getUsername();
}
#EntityView(Rating.class)
interface ProductDto {
Integer getId();
String getProductName();
String getProductDesc();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
RatingDto dto = entityViewManager.find(entityManager, RatingDto.class, id);
But 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
It will only fetch the mappings that you tell it to fetch
You can use ModelMapper when converting a DTO to an Entity bean and back from Entity bean to a DTO.
Add ModelMapper to your project
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
Define the ModelMapper bean in your Spring configuration
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
Assuming the following models based on the given ER diagram you have given
public class UserDto {
Integer userId;
String role;
String username;
String password;
boolean enabled;
...default and parameterized constructor
...getter and setter methods
}
public class ProductDto {
Integer productId;
String imageUrl;
String category;
int productPrice;
int productQuantity;
String productName;
String productDesc;
...default and parameterized constructor
...getter and setter methods
}
public class RatingDto {
#Id
Integer id;
int rating;
String review;
String ratingscol;
ProductDto productDto;
UserDto userDto;
...default and parameterized constructor
...getter and setter methods
}
You can retrieve the ratings of a product using product id along with the user details by using the following method
#Repository
public interface RatingRepository extends JpaRepository<Rating, Integer>{
List<Rating> findByProduct_ProductId(Integer productId);
}
Then mapping rating objects to DTO
RatingDto ratingDto = modelMapper.map(rating, RatingDto.class);
Now you can retrieve username as following
ratingsDto.getUserDto().getUserName()
The same way you can retrieve the ratings by userId and access product details

Fetch List Using DTO projections using a Constructor Expression and JPQL

Perform a search on DisabScreenRequest and fetch its child details also. Using DTO projections using a Constructor Expression and JPQL.
The parent entity with a child table.
#Entity
#Table(name = "SCREEN_REQUEST")
public class DisabScreenRequest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long requestId;
#Column(name = "CIVILID")
private Long civilId;
#ManyToMany()
#JoinTable(name = "_DISAB_SCREEN_REQ_DETAILS", joinColumns = {
#JoinColumn(name = "REQUEST_ID") }, inverseJoinColumns = { #JoinColumn(name = "DISABILTY_TYPE_ID") })
private Set<DisabMaster> disabilities = new HashSet<DisabMaster>();
public DisabScreenRequest() {
}
}
This is the disability table.
#Entity
#Table(name="DISAB_MASTER")
#Immutable
public class DisabMaster implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="DIS_TYPE_ID")
private long disabilityTypeId;
#Column(name="DIS_TYPE_DESC")
private String disTypeDesc;
public DisabMaster() {
super();
}
}
Had to fetch all the requests along with the disability for each request.
Search DTO(using this I had other joins to add other than one mentioned here).
public class RequestSearchDto {
private long requestId;
private Long civilId;
private Set<DisabMaster> disabilities;
public RequestSearchDto() {
super();
}
public RequestSearchDto(long requestId, Long civilId) {
super();
this.requestId = requestId;
this.civilId = civilId;
}
public RequestSearchDto(long requestId, Long civilId, Set<DisabMaster> disabilities) {
super();
this.requestId = requestId;
this.civilId = civilId;
this.disabilities = disabilities;
}
}
This is my JPQL query
public interface ReposJPQL {
public String GET__REQUEST = "SELECT DISTINCT new org.test.RequestSearchDto "
+ "(dsr.requestId, dsr.civilId, dsr.disabilities)"
+ " FROM DisabScreenRequest dsr WHERE 1=1 ";
}
This will get an
org.hibernate.exception.SQLGrammarException: could not extract ResultSet.
What Iam I doing wrong here, how can I fetch the child table data ?
Let me know if you need any info
Stack trace :
Caused by: java.sql.SQLException: ORA-00936: missing expression
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:113)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:754)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:219)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:813)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1051)
at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:854)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1156)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3415)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3460)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
If you need to fetch parent entity with a collection of its nested child entities you can use this simple approach using #EntityGraph annotation or JPQL with join fetch:
#Entity
public class Parent {
//...
#OneToMany
private List<Child> children;
}
#Entity
public class Child {
//...
}
interface ParentRepo extends JpaRepository<Parent, Integer> {
// with #EntityGraph
#EntityGraph(attributePaths = "children")
#Override
List<Parent> findAll();
// or manually
#Query("select distinct p from Parent p left join fetch p.children")
List<Parent> findWithQuery();
}
Note to use distinct in your query to avoid duplicate records.
Example: duplicate-parent-entities
More info: DATAJPA-1299
AFAIK, you can't use constructor expression which take a Collection.
See the JPA 2.2 Spec, section 4.14 BNF, read about the constructor expression:
constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable
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 defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A mapping for your model could look as simple as the following
#EntityView(DisabScreenRequest.class)
interface RequestSearchDto extends Serializable {
#IdMapping
long getRequestId();
Long getCivilId();
Set<DisabMaster> getDisabilities();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
RequestSearchDtodto = entityViewManager.find(entityManager, RequestSearchDto.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/1.4/entity-view/manual/en_US/#spring-data-features

How to use Spring Data JPA findAll method in Spring Boot with custom parameter?

I have a Entity class
#Entity
public class SampleEntity {
#Id
#Column(name = "ID")
private Long id;
#NotNull
#Column(length = 2000)
private String name;
#NotNull
#Column(length = 2000)
private String type;
#NotNull
#Column(length = 2000)
private String something;
// getters and setters
}
What I need is to findAll by the custom column of my entity type
how I can achieve this in Spring boot and JpaRepository
Follow https://docs.spring.io/spring-data/jpa/docs/1.5.0.RC1/reference/html/jpa.repositories.html which will list all possible method to find out records from DB.
For your problem , use List<SampleEntity> findByType(String type) in your repository. This method will return all SampleEntity by type values passed in query.
Besides the findByType(String type) you can use the Spring Data repository syntax to fine tune your queries, using the same parameter String type.
Some examples:
findByTypeIgnoreCase: will return all that match the given type string ignoring case differences between that string and the SampleEntity type field.
findFirstByType: will return the first element with the given type if there is any.
findFirst100ByType: will return the first 100 elements.
findByTypeOrderByIdAsc: will return all SampleEntity with the given type, in ascending order by their id field.
countByType: will return the number of SampleEntity with the given type.
removeByType: will delete all the SampleEntity with that type.

Resources