How to search nested property in Hibernate Criteria - spring

My entity User has a nested property (in Oracle, it maps to a string field format as JSON) like the following snippet:
#Entity(name = "users")
public class User extends Auditable implements Serializable {
private Long id;
private String username;
private String password;
#Convert(converter = UserInformationConverter.class)
private UserInfomation additionalInformation;
}
public class UserInfomation {
private String email;
private String phoneNumber;
}
And then, I would like to search by the "email" property in the "additionalInformation" field in Criteria query. I tried:
predicate = cb.and(predicate, cb.like(root.get("additionalInformation").get("email"), cb.parameter(String.class, "email")));
But I got the error:
"Illegal attempt to dereference path source [null.additionalInformation] of basic type; nested exception is java.lang.IllegalStateException: Illegal attempt to dereference path source [null.additionalInformation] of basic type"
Please suggest me some solutions.

You can use the criteriaBuilder like:
cb.like(cb.lower(root.get("additionalInformation").get("email"));

Related

Instructing Sping Data MongoDB to use correct mapping between ObjectId and its class

I cannot retrieve the 2nd level nested objects in Spring Data MongoDB
I have nested collection in MongoDB to retrieve with Spring. Imagine this schema
#Data
#Builder
#Document(collection = "emitted")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Emitter{
#Id
private String id;
#Field("installation")
#DocumentReference(lazy = true)
private Installaton installation;
// other fields
#Data
#Builder
#Document(collection = "installation")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Installation {
#Id
private String id;
#Field("subject")
#DocumentReference(lazy = true)
private Subject subject;
// other fields
#Data
#Builder
#Document(collection = "subject")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Subject {
#Id
private String id;
// other fields
Plus, I have MapStruct to map nested object field to string, for the purpose of avoiding cyclic reference introducing the search by id of the collection:
#ObjectFactory
public <T> T map(#NonNull final String id, #TargetType Class<T> type) {
return mongoTemplate.findById(id, type);
}
Everything works at first level, but at nested level I have this error:
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.bson.types.ObjectId] to type [com.package.collections.Subject]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.doConvert(MappingMongoConverter.java:1826)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.doConvert(MappingMongoConverter.java:1818)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:1337)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:1311)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$DefaultConversionContext.convert(MappingMongoConverter.java:2371)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$ConversionContext.convert(MappingMongoConverter.java:2174)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1936)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:638)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:549)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:527)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readDocument(MappingMongoConverter.java:491)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:427)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:423)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:120)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3326)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:2940)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2618)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2588)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:922)
at com.package.myapp.services.mapper.ReferenceMapper.map(ReferenceMapper.java:26)
at com.package.myapp.services.mapper.InstallationMapperImpl.toEntity(InstallationMapperImpl.java:102)
When asking the conversion, the findById works correctly and retrieve the object and the nested one. It fails when the request is for 2nd level nested object, where the ObjectId is retrieved but cannot be converted and fails.
I'm answering myself because I found a solution suited for my problem.
I only needed the entity object with the id, so I wrote a converter:
public class ObjectIdToSubjectConverter implements Converter<ObjectId, Subject> {
#Override
public Subject convert(ObjectId source) {
return Subject.builder().id(source.toString()).build();
}
}
And register it as a bean:
#Configuration
public class MongoConfig {
#Bean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Collections.singletonList(new ObjectIdToSubjectConverter());
}
}

Invalid parameter index! You seem to have declared too little query method parameters

I have an entity:
#Document(collection = "userData")
#Data
public class UserData {
#Id
#Field("_id")
private String id;
#Field("orderColumns")
private List<String> orderColumns;
#Field("ownerId")
private String ownerId;
}
And I have a repository:
#Repository
interface UserDataRepository extends MongoRepository<UserData, String> {
Optional<UserData> findByOwnerIdAndOrderColumnsExists(String ownerId);
}
When I execute this method, I get an exception:
Invalid parameter index! You seem to have declared too little query method parameters!

How to solve Spring Boot findBy method with underscore variable

When I run the below project, I receive the following error. How can I fix it?
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract com.example.pharmanic.model.Rdhs_Hospital_Current_Stock com.example.pharmanic.repositories.Rdhs_Hospital_Current_StockRepository.findBysr_no(java.lang.String)! No property sr found for type Rdhs_Hospital_Current_Stock!
This is my Rdhs_Hospital_Current_Stock model class.
#Entity
#Data
#Table(name = "Rdhs_Hospital_Current_Stock")
public class Rdhs_Hospital_Current_Stock {
#Id
private Long batchId;
private int quantity;
private String expiredate;
#ManyToOne
private Hospital_By_Rdhs hospital_by_rdhs;
#ManyToOne
#JoinColumn(name = "sr_no", nullable = false, referencedColumnName = "sr_no")
private Medicine medicine;
}
sr_no is the foreign key of the Medicine table.
This is my Medicine entity:
#Data
#Entity
public class Medicine {
private #Id String sr_no;
private String name;
private String side_effect;
private String description;
public Medicine() {
}
public Medicine(String sr_no, String name, String side_effect, String description) {
this.sr_no = sr_no;
this.name = name;
this.side_effect = side_effect;
this.description = description;
}
}
When I use sr_no with my findBy() function:
#GetMapping("/rhstock/{id}")
ResponseEntity<?> getMedicine(#PathVariable String id){
Optional<Rdhs_Hospital_Current_Stock> rdhs_hospital_current_stock = Optional.ofNullable(rdhs_hospital_current_stockRepository.findBysr_no(id));
return rdhs_hospital_current_stock.map(response->ResponseEntity.ok().body(response)).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
This is my repository:
public interface Rdhs_Hospital_Current_StockRepository extends JpaRepository<Rdhs_Hospital_Current_Stock,Long> {
Rdhs_Hospital_Current_Stock findBysr_no(String id);
}
Inspired from: Spring-Data-Jpa Repository - Underscore on Entity Column Name
The underscore _ is a reserved character in Spring Data query derivation (see the reference docs for details) to potentially allow manual property path description.
Stick to the Java naming conventions of using camel-case for member variable names and everything will work as expected.
Change sr_no to srNo.
Update repository function
Rdhs_Hospital_Current_Stock findBymedicine_srNo(String id);
I solve this error. I change in Reposity Interface & Controller class like as below
Repository Interface -:
#Query(value="select * from Rdhs_Hospital_Current_Stock h where h.sr_no = :sr_no",nativeQuery=true)
List<Rdhs_Hospital_Current_Stock> findBySr_no(#Param("sr_no")String sr_no);
Controller class -:
#RequestMapping(value = "/rhstocksr/{sr_no}", method = RequestMethod.GET)
List<Rdhs_Hospital_Current_Stock> getBatchByMedicine(#PathVariable("sr_no") String sr_no) {
return rdhs_hospital_current_stockRepository.findBySr_no(sr_no);
}
To keep using a derived query you may change sr_no to srNo.
You could solve this by starting using a native query, but my advice is to use derived querys everytime as it is possible to use it.

How to write query for many to one mapped entity in JpaRepository

I have two entities and mapped those using many-to-one annotation but after writing a query for find object using another table id I got an error when I commented out that line and method called to that application work but I want to implement that functionality and please help me
These are my entity classes:
#Entity
#Table(name = "Contract")
public class Contract implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "contractId")
private long contractId;
#Column(name="start_date")
private Date st_date;
#Column(name="end_date")
private Date end_date;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "hotel_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Hotel hotel;
// getters and setters
Second entity
#Entity
#Table(name="Hotel")
public class Hotel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="hotel_id")
private long hotel_id;
#Column(name="hotel_name")
private String hotel_name;
#Column(name="hotel_location")
private String hotel_location;
#Column(name="hotel_email")
private String hotel_email;
#Column(name="hotel_telephone")
private String hotel_telephone
// getters and setters
My contract service class
#Service
public class ContractService {
#Autowired
private ContractRepository contractRepository;
#Autowired
private HotelRepository hotelRepository;
public List<Contract> getAllContracts(){
return contractRepository.findAll();
}
public List<Contract> findByHotelId(Long hotelId,Pageable pageable){
return contractRepository.findByHotelId(hotelId, pageable);
}
public ResponseEntity<?> deleteContract(Long hotelId, Long contractId)
{
return contractRepository.findByIdAndHotelId(contractId,
hotelId).map(Contract -> {
contractRepository.delete(Contract);
return ResponseEntity.ok().build();
}).orElseThrow(() -> new ResourceNotFoundException("Comment not found
with ContractId " + contractId + " and hotelId " + hotelId));
}
My contract repository
#Repository
public interface ContractRepository extends JpaRepository<Contract, Long> {
List<Contract> findByHotelId(Long hotelId, Pageable pageable);
Optional<Contract> findByIdAndHotelId(Long id, Long hotelId);
}
I got this error when running my project
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'contractController': Unsatisfied dependency expressed through field 'contractService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'contractService': Unsatisfied dependency expressed through field 'contractRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contractRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.sunTravel.sunRest.repository.ContractRepository.findByHotelId(java.lang.Long,org.springframework.data.domain.Pageable)! No property id found for type Hotel! Traversed path: Contract.hotel.
First Solution: based on your stack trace, Spring data is looking for id variable (primary key) in your Hotel class. So please change private long hotel_id; to private long id;
Another solution (no need to change anything just add your own query):
write your own JPA query using #Query.
Example:
#Query("SELECT contract from Contract as contract where contract.hotel.hotel_id = :hotelId")
List<Contract> findByHotelId(Long hotelId, Pageable pageable);
You should rename your Primary Key from hotel_id to id then only your repository method will work.
#Entity
#Table(name="Hotel")
public class Hotel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="hotel_id")
private long id;
#Column(name="hotel_name")
private String hotel_name;
#Column(name="hotel_location")
private String hotel_location;
#Column(name="hotel_email")
private String hotel_email;
#Column(name="hotel_telephone")
private String hotel_telephone
// getters and setters

Project embedded document fields after lookup operation

I want to do a join between Timesheet:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document(collection = TIMESHEET_COLLECTION)
public class Timesheet {
#Id
private ObjectId id;
private ObjectId employeeId;
private LocalDate date;
private String occupationTitle;
private BigDecimal salary;
private List<TimesheetEntry> entries;
}
and Employee (as embedded document):
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document(collection = Employee.EMPLOYEE_COL)
public class Employee {
#Id
private ObjectId id;
private String registry;
private String cpf;
private String firstName;
private String lastName;
private String nickname;
private String phone;
private LocalDate dateOfBirth;
private LocalDate admissionDate;
private EmployeeOccupation occupation;
private EmployeePaymentPreferences paymentPreferences;
private Map<String, String> equipmentPreferences;
private Boolean active;
}
So I have this aggregation query, with match, lookup, unwind and projection operations.
Aggregation aggregation = Aggregation.newAggregation(matchTimesheetFilter(timesheetFilter), lookupEmployee(), unwindEmployee(), projectEmployee());
There are lookup and unwind implementations. I'm unwinding because employee should be a single object, not an array.
private LookupOperation lookupEmployee(){
return LookupOperation.newLookup()
.from("employee")
.localField("employeeId")
.foreignField("_id")
.as("employee");
}
private UnwindOperation unwindEmployee(){
return Aggregation.unwind("employee");
}
It returns successfully a Timesheet document with a embedded Employee document. The point is: I don't want all data from employee. I only want a few fields.
So, I tried to exclude unwanted fields from employee, using my projection operation:
private ProjectionOperation projectEmployee() {
return Aggregation.project().andExclude("employee.nickname", "employee.firstName", "employee.fullName");
}
It didn't work. My embedded employee is still being returned with all fields. However I can successfully exclude fields from Timesheet, if I do something like this:
private ProjectionOperation projectEmployee() {
return Aggregation.project().andExclude("startDate", "endDate");
}
How can I project custom fields from a document embedded through a lookup operation?
i think you need to exclude "employee.nickname", "employee.firstName", "employee.fullName", instead of "nickname", "firstName", "fullName"
Try this:
private ProjectionOperation projectEmployee() {
return Aggregation.project().andExclude("employee.nickname", "employee.firstName", "employee.fullName");
}
i did it this way (not sure if it's right but it works):
private LookupOperation lookupEmployee(){
return LookupOperation.newLookup()
.from("employee")
.localField("employeeId")
.foreignField("_id")
.as("employeeLookup");
}
no unwind used
Aggregation.project().and("employeeLookup.firstName").as("employee.firstName")

Resources