How to search by prefix using a Spring JPA Specification? - java

I'm stuck on a JPA Specification task.
I have two entities, prefixes and country codes.
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "prefixes")
public class PrefixEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String prefix;
#OneToMany(mappedBy = "prefix", cascade = CascadeType.ALL)
private List<CountryCodeEntity> countryCodes;
#Data
#Builder
#Entity(name = "country_codes")
public class CountryCodeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String countryCode;
#ManyToOne
#JoinColumn(name = "prefixes_id")
private PrefixEntity prefix;
}
I want to get country code from "country_codes" table searching by prefix in "prefixes" table. I have already written a native query.
#Query(value = "SELECT cc.country_code FROM prefix_country_codes.prefixes p" +
" JOIN country_codes cc ON p.id = cc.prefixes_id" +
" WHERE p.prefix=?1", nativeQuery = true)
List<String> getCountryCodeByPrefix(String prefix);
How can I do this using Spring JPA Specification?
Something like:
public interface PrefixSpecification<P> {
static Specification<PrefixEntity> joinTest(String prefix) {
return (root, query, criteriaBuilder) -> {
Join<PrefixEntity, CountryCodeEntity> countryCodes = root.joinList("countryCodes");
...
};
}
}
and
phoneNumberRepository.findAll(Specification.where(PrefixSpecification.joinTest("371")));

Related

duplicate [ID] alias error for sql statement with join

I created a search button with a SQL query which is including JOIN 2 times, but its throwing error like:
org.hibernate.loader.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias ID during auto-discovery of a
native-sql query
This is the Repository method:
#Query(value = "select * from reports r join patients p on r.patient_id = p.id join lab_technicians lt on r.lab_technician_id = lt.id where p.name like '%:patientName%' and lt.name like '%:labTechnicianName%' and p.identity_no like '%:patientIdentityNo%'", nativeQuery = true)
List<Report> findBySearch(#Param("patientName") String patientName, #Param("labTechnicianName") String labTechnicianName, #Param("patientIdentityNo") String patientIdentityNo);
To make you understand the project templates, these are the entities classes:
Person class:
#MappedSuperclass
public abstract class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
Patient class:
#Entity
#Table(name = "patients")
public class Patient extends Person{
#Column(name = "identity_no") private String identityNo;
#OneToMany(mappedBy = "patient") private List<Report> reports;
LabTechnician class:
#Entity
#Table(name = "lab_technicians")
public class LabTechnician extends Person{
#Column(name = "hospital_id")
private String hospitalId;
#OneToMany(mappedBy = "labTechnician")
private List<Report> reports;
and lastly Report class:
#Entity
#Table(name = "reports")
public class Report {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "lab_technician_id")
private LabTechnician labTechnician;
#ManyToOne
#JoinColumn(name = "patient_id")
private Patient patient;
#Column(name = "file_number")
private String fileNumber;
#Column(name = "diagnostic")
private String diagnostic;
#Column(name = "detail_of_diagnostic")
private String detailOfDiagnostic;
#Column(name = "date_of_report")
private Date dateOfReport;
I changed List in #OneToMany relationships to Set but its not changed anything
#Query(value = "select * from reports r join patients p on r.patient_id =
p.identity_no join lab_technicians lt on r.lab_technician_id =
lt.hospital_id where p.name like '%:patientName%' and lt.name like
'%:labTechnicianName%' and p.identity_no like '%:patientIdentityNo%'",
nativeQuery = true)
List<Report> findBySearch(#Param("patientName") String patientName,
#Param("labTechnicianName") String labTechnicianName,
#Param("patientIdentityNo") String patientIdentityNo);
Use this query it will work fine

Not able to delete in #OneToMany relationship spring data jpa

In my spring boot project, I have one LineItem entity below is the code
#Entity
#Table(name = "scenario_lineitem")
#Data
#NoArgsConstructor
public class LineItem implements Cloneable {
private static Logger logger = LoggerFactory.getLogger(GoogleConfigConstant.class);
#Id
#GeneratedValue(strategy = IDENTITY)
private BigInteger lineItemId;
#Column
private String name;
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "line_item_meta_id")
private List<QuickPopValue> quickPopValues;
}
Another entity is
#Entity
#Table(name = "quick_pop_value")
#Data
#NoArgsConstructor
public class QuickPopValue implements Cloneable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "quick_pop_value_id", columnDefinition = "bigint(20)", unique = true, nullable = false)
private BigInteger quickPopValueId;
#Column(name = "column_name")
private String columnName;
#Column(name = "value")
private String value;
#Column(name = "formula", columnDefinition = "longtext")
private String formula;
}
Now I am trying to delete QuickPopValue one by one but it's not getting deleted and not getting any exception as well.
Below is the delete code :
List<QuickPopValue> quickPopValues = sheetRepository.findByColumnName(columnName);
for (QuickPopValue qpValue : quickPopValues) {
quickPopValueRepository.delete(qpValue);
}
Such behavior occurs when deleted object persisted in the current session.
for (QuickPopValue qpValue : quickPopValues) {
// Here you delete qpValue but this object persisted in `quickPopValues` array which is
quickPopValueRepository.delete(qpValue);
}
To solve this you can try delete by id
#Modifying
#Query("delete from QuickPopValue t where t.quickPopValueId = ?1")
void deleteQuickPopValue(Long entityId);
for (QuickPopValue qpValue : quickPopValues) {
quickPopValueRepository.deleteQuickPopValue(qpValue.getQuickPopValueId());
}

Two-side Joining in JPA Criteria Building

I've three entities, like that:-
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#Expose
private long id;
#ManyToOne
#JoinColumn(name = "users_id")
#Expose
private User user;
}
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#Expose
private long id;
}
#Entity
#Table(name = "users_phone")
public class UserPhone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#Expose
private int id;
#ManyToOne
#JoinColumn(name = "users_id")
#Expose
private User user;
}
Now, from I want to get results from product table based on joining the three tables. So I made a Specification so that I can pass it into the repository. Here is the code for Specification.
Specification<ProductPost> productSpecification = new Specification<ProductPost>() {
#Override
public Predicate toPredicate(Root<ProductPost> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
criteriaQuery = criteriaBuilder.createQuery();
if (userName.length() > 0) {
predicates.add(criteriaBuilder.like(root.join("user").<String>get("fullName"), "%" + userName + "%"));
}
if (phoneNumber.length() > 0) {
// Below line isn't working actually as join("UserPhone") - 'user' table has no reference for 'userPhone'. But 'userPhone' has 'user'.
predicates.add(criteriaBuilder.like(root.join("user").join("UserPhone").<String>get("phoneNumber"), "%" + postType + "%"));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
Yes, It will be easy if I have 'userPhone' reference in 'user' table like - product -> user -> userPhone rather than product -> user, userPhone -> user. But my schema is like that. Now I face troubles joining the 3 tables and fetch the results.
If cross-join is appropriated instead of join you can try this solution
if (phoneNumber.length() > 0) {
Root<UserPhone> userPhone = query.from(UserPhone.class);
predicates.add(criteriaBuilder.equal(
root.join("user").get("id"),
userPhone.get("user").get("id")
));
predicates.add(criteriaBuilder.like(
userPhone.get("phoneNumber"),
"%" + postType + "%"
));
}

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

<expression> expected got <from> in spring-data hibernate

Working with spring-data and hibernate and trying to write this request in a repository :
#Query("select u from AppUser u inner join u.roles r inner join u.contrats c where r = :role and not exists ( from c.project p where p = :project)")
My goal is to get List of AppUser (Users) from entity AppUer ,but with a condition , only those who don't have the entity project .
To explain more , there is an entity Contrat between Project and AppUser , it contains Project project , AppUser appuser .
There is a mapping between one_to_many between AppUser and Contrat(AppUser can have multiple contrats) .
Also a mapping of one_to_many between Contrat and project ( One project can have multiple contrat ) .
The problme is that this request returns a empty list while it should return a list of 12 users as they don't have the specified project i passe to it.
I remark in this request (from c.project p where p = :project) 'from' is underlined with red line saying :
<expression> expected got <from> in spring-data hibernate.
Any idea ?
Edit
Entity AppUser
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
private String prenom;
private String nom;
private Long tel;
private String cin;
private String email ;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" )
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference(value="appuser-contrat")
private Collection<Contrat> contrats = new ArrayList<Contrat>();
public void addToContrats(Contrat contrat){
this.contrats.add(contrat);
}
}
Entity Contrat
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Contrat implements Serializable{
#Id #GeneratedValue
private Long id;
private Date dateDebut ;
private Date dateFin ;
private Long idDevloppeur;
#ManyToOne
#JoinColumn(name = "Id_Project")
#JsonBackReference(value="projet-contrat")
private Project project;
#ManyToOne
#JoinColumn(name = "Id_AppUser")
#JsonBackReference(value="appuser-contrat")
private AppUser appUser;
}
Entity Project
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" )
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference(value="projet-contrat")
private Collection<Contrat> contrats = new ArrayList<Contrat>();
public void addToContrats(Contrat contrat){
this.contrats.add(contrat);
}
}
(Posting answer to summarize solution in comments)
In given select query "inner join u.contrats" was used, but there were no contracts entities for users. Solution: replace with "left join u.contrats".
Inner join vs left join already solved here
What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

Categories