Which return value should I use for the one-to-one table when using spring boot jpa? - jpa

We are working on a club management project.
There is a club president on the club table and the user ID is received as a foreign key.
I want to join two tables while using jpa and get the results
If you specify the mapped club table as a type, an error appears (image 1)
If you interface the resulting field, only null values are returned. (Image 2)
How shall I do it?
(Image1)
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object[]] to type [#org.springframework.data.jpa.repository.Query com.wodongso.wodongso.entity.Society] for value '{호남대학교, 왕유저13, 두 발의 자유1, 스포츠, 두 바퀴만 있다면 지원가능!!1, 허벅지 터지도록 활동합니다1, true}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [#org.springframework.data.jpa.repository.Query com.wodongso.wodongso.entity.Society]
(Image2)
society entity
user entity
#Entity
#Data
public class User {
#Id
private String id;
private String name;
private String nickname;
private String password;
private String role;
private String contact;
#Column(name = "profile_url")
private String profileUrl;
private String region;
private String university;
private String major;
#Column(name = "class_of")
private Integer classOf;
private boolean enabled;
#Column(name = "created_at")
private Date createdAt;
}
SocietyRepository
#Repository
public interface SocietyRepository extends JpaRepository<Society, Integer> {
Page<Society> findByNameContaining(String searchKeyword, Pageable pageable);
#Query("SELECT s FROM Society s WHERE s.enabled = :isEnable")
Page<Society> findByEnabledPage(#Param("isEnable") boolean isEnable, Pageable pageable);
#Query("SELECT u.university, u.name, s.name, s.category, s.simpleDesc, s.detailDesc, s.enabled " +
"FROM Society s " +
"INNER join User u " +
"ON u.id = s.officerId " +
"WHERE u.university = :university")
List<Society> findAllByUniversity(#Param("university") String university);
}

Create a class which contains all the fields and add an all args constructor and than use the query:
#Query("SELECT new SocietyWithUser(u.university, u.name, s.name, s.category, s.simpleDesc, s.detailDesc, s.enabled) " +
"FROM Society s " +
"INNER join User u " +
"ON u.id = s.officerId " +
"WHERE u.university = :university")
List<SocietyWithUser> findAllByUniversity(#Param("university") String university);

Related

Spring Data: Fetching values from Entity with #ElementCollection

My Entity class is:
#Entity
#Data
#Table(uniqueConstraints = { #UniqueConstraint(columnNames = {"username" }, name = "uq_username") } })
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
private String firstName;
private String lastName;
#ElementCollection(targetClass = Role.class,fetch = FetchType.LAZY)
#JoinTable(name = "User_Roles", joinColumns={#JoinColumn(name = "user_id")})
#Enumerated(EnumType.STRING)
private List<Role> roles;
}
Role class is an enumeration:
public enum Role {
ADMIN,
OFFICE_USER,
SUPERVISOR,
MANAGER,
DATA_ENTRY
}
Property spring.jpa.hibernate.ddl-auto=create and the tables user and user_roles (fields: user_id, roles) are created and data inserted successfully. However, querying for data for users with a role is not working. My repository is:
#Component
public interface UserRepository extends CrudRepository<User, Integer>, JpaRepository<User, Integer> {
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%"
+ " AND usr.roles=:roles")
public List<User> fetchUsersByNameAndRole(String username, Collection<String> roles);
}
On passing the following request I am getting error
http://localhost:8080/api/users/search/fetchUsernamesByNameAndRole?username=Thom&roles=SUPERVISOR
[org.springframework.data.rest.webmvc.ResourceNotFoundException: EntityRepresentationModel not found!]
If I change Repository method (changing username parameter to String from Collection) to
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%"
+ " AND usr.roles=:roles")
public List<User> fetchUsersByNameAndRole(String username, String roles);
The error becomes:
[org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [SUPERVISOR] did not match expected type [java.util.Collection (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [SUPERVISOR] did not match expected type [java.util.Collection (n/a)]]
Question 1: How can I fetch all users with a name and a particular role (or set of roles) in this scenario?
The code is working if I remove the roles clause, like
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%")
public List<User> fetchUsersByName(String username);
I need the user names only. So I changed code to:
#Query(value = "select usr.firstName from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%")
public List<String> fetchUsersByName(String username);
But gets the error:
org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.String
Changing to native query also gives same error:
#Query(value ="select first_name from user where first_name like ?1%", nativeQuery = true)
Question 2: Why mapping to List is not working in Spring data? I remember it was working with normal Spring JPA

Query in Spring Boot JPA - #OneToMany List relation

I've got entity like this:
#Entity
#Table(name = "formula")
public class Formula {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "formula_id")
private Long formulaId;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "time")
private int time;
#OneToMany(mappedBy = "formula",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Product> productList = new ArrayList<>();
And another Entity:
#Entity
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long productId;
#Column(name = "product_name")
private String productName;
#Column(name = "amount")
private Double amount;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "formula_id")
private Formula formula;
I want to ask Query to DB which help me get every type of data (by key word). I've got all except List of <Product>. It look like this:
public interface FormulaRepository extends JpaRepository<Formula, Long> {
#Query("SELECT f FROM Formula f WHERE " + "CONCAT(f.name, f.description,
f.time)" + "LIKE %?1%")
List<Formula> findFormulaBy(String word);
How can add productList to Query and acomplished my searching? Is there any possibility to do this in findFormulaBy(String word); method?
Change query to include LEFT JOIN FETCH to eagerly fetch productList. Also include DISTINCT to prevent duplicate Formula objects in List
#Query("SELECT DISTINCT f FROM Formula f " +
"LEFT JOIN FETCH f.productList " +
"WHERE " + "CONCAT(f.name, f.description,f.time)" + "LIKE %?1%")
List<Formula> findFormulaBy(String word);
SQL generated by Hibernate
2022-09-10 10:16:38.287 DEBUG --- [ main] org.hibernate.SQL :
select
distinct formula0_.formula_id as formula_1_9_0_,
productlis1_.product_id as product_1_12_1_,
formula0_.description as descript2_9_0_,
formula0_.name as name3_9_0_,
formula0_.time as time4_9_0_,
productlis1_.amount as amount2_12_1_,
productlis1_.formula_id as formula_4_12_1_,
productlis1_.product_name as product_3_12_1_,
productlis1_.formula_id as formula_4_12_0__,
productlis1_.product_id as product_1_12_0__
from
formula formula0_
left outer join
products productlis1_
on formula0_.formula_id=productlis1_.formula_id
where
(
formula0_.name||formula0_.description||formula0_.time
) like ?
I see in your comment you have added f.productList list to the CONCAT function which is why you are getting a SQL error. If you want to search product fields in CONCAT function you will need to give p.productList an alias and reference the fields in this way
#Query("SELECT DISTINCT f FROM Formula f " +
"LEFT JOIN FETCH f.productList p " +
"WHERE " + "CONCAT(f.name, f.description,f.time,p.productName)" + "LIKE %?1%")
List<Formula> findFormulaBy(String word);
This seems a strange way to search formulae and products and you will be better off adding a second parameter to your SQL
#Query("SELECT DISTINCT f FROM Formula f " +
"LEFT JOIN FETCH f.productList p " +
"WHERE " + "CONCAT(f.name, f.description,f.time)" + "LIKE %?1% " +
"AND p.productName = ?2 ")
List<Formula> findFormulaBy(String word, String productName);

How to return a count column not exists in table by JPA

I want find a way to get extra column that count my records and return it in 1 mapping entity with extra filed.
I tried #transient on field but it will not return value when query.
Then I remove #transient but get an exception when save.
Also I tried #Formula but received null pointer exception.
Here's my repository code:
#Query(value = "select id,account,session_id,create_time,count from query_history a join " +
"(select session_id sessionId,max(create_time) createTime,count(*) count from query_history group by session_id) b " +
"on a.session_id = b.sessionId and a.create_time = b.createTime where account = ?1 order by create_time desc",
countQuery = "select count(distinct(session_id)) from query_history where account = ?1",
nativeQuery = true)
Page<QueryHistory> findByNtAndGroupBySessionAndAction(String account, Pageable pageable);
entity code:
#Entity
#Table(name = "query_history")
#Data
public class QueryHistory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String account;
#Column
private Long sessionId;
#Column
private long createTime;
#Transient
private Integer count;
}
Sorry about my English and thanks a lot for any advice.
I solved the problem by projections spring-data-projections, in fact I tried this before but in my sql:
select id,account,session_id,create_time,count
which should be:
select id,account,session_id sessionId,create_time createTime,count
PS:
projection interface:
public interface QueryHistoryWithCountProjection {
Long getId();
String getAccount();
Long getSessionId();
long getCreateTime();
Integer getCount();
}

Can't get result when running the query from Spring Data Jpa

When running the following query in pgAdmin, everything is good but when I run the query from java code i get an error.
I tried to cast the variables but no result, I get an error of cast syntax
The query interface:
public interface SeanceRepository extends JpaRepository<Seance, Integer>{
#Query(value = "select * from seance, patient where extract(month from date) = ?1 "
+ "and extract(year from date) = ?2 and patient.id = seance.patient_id",
nativeQuery = true)
public List<Object> getSeanceByMonthYear(String month, String year);
}
The error :
org.postgresql.util.PSQLException: ERREUR: l'opérateur n'existe pas : double precision = character varying
Indice : Aucun opérateur ne correspond au nom donné et aux types d'arguments.
Vous devez ajouter des conversions explicites de type.
Position : 62
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[postgresql-42.2.5.jar:42.2.5]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183) ~[postgresql-42.2.5.jar:42.2.5]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308) ~[postgresql-42.2.5.jar:42.2.5]
Patient.java
#Entity
#Table(name = "Patient")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String firstName;
private String lastName;
private String cin;
private Date birthDate;
private String phoneNumber;
private Boolean active;
//getters and setters
}
seance.java
#Entity
#Table(name = "Seance")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Seance {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private Date date;
#OneToOne
private Patient patient;
#OneToOne
private TypeConsultation typeConsultation;
private String motif;
//getters and setters
It is because the function extract a double and you compare it to a string
As you can see in https://w3resource.com/PostgreSQL/extract-function.php
The extract function() is used to retrieves subfields such as year or
hour from date/time values. The source must be a value expression of
type timestamp, time, or interval. The field is an identifier or
string that selects what field to be extracted from the source value.
Syntax:
extract(field from timestamp) or extract(field from interval)
Return Type: double precision.
PostgreSQL Version: 9.3
extract returns a value in double presision and you try to compare it to a varchar. It can not be done by postgresSQL automatically, you have to do it explicitly:
#Query(value = "select * from seance, patient where extract(month from date)::varchar(255) = ?1 "
+ "and extract(year from date)::varchar(255) = ?2 and patient.id = seance.patient_id",
nativeQuery = true)

My JPQL query with Outer Join returns empty list while using GroupBy, OrderBy and Sum Function

This is my Product entity:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank
private String name;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Category category;
...
}
And this is the productOrder junction table for the relation product-order:
#Entity
public class ProductOrder {
#EmbeddedId
private ProductOrderId pk;
#Min(value = 1)
private int quantity;
...
}
And this is the embeddedId class:
#Embeddable
public class ProductOrderId {
#ManyToOne
#JoinColumn(name = "product_id")
private Product product;
#ManyToOne
#JoinColumn(name = "orderr_id")
private Order order;
...
}
I have this JPQL Query works perfectly which finds all the products from a category, sorting them based on their total sold quantity. Since it is a outer join, i get also the ones which haven't been sold yet:
#Query(value = "select p, sum(po.quantity) as total_quantity " +
"from ProductOrder po " +
"right join po.pk.product p where p.category = (?1) " +
"group by p.id, p.name " +
"order by total_quantity desc nulls last")
Page<Object[]> findBestSellerProductsByCategory(Category category, Pageable pageable);
The problem is, when there is not any sold product in a category, i get an empty page. So in order to use this query, my category has to have at least 1 sold product, then i get also the others which haven't been sold yet. What is the problem here?
Since i make a "select from junctionTable (ProductOrder)", when there is not any element from the category in this table, i get an empty list as i understand. But i could not make it the other way around "select from product", since product has no reference of productOrder table, i cannot make a join relationship in that case..
INFO: When i remove the groupBy, it works. I get my products from a category even if none of them is sold yet.
#Query(value = "select p " +
"from ProductOrder po " +
"right join po.pk.product p where p.category = (?1)")
Page<Object[]> findProductsByCategory(Category category, Pageable pageable);