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);
Related
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);
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();
}
Given this SQL query
SELECT
ug.lookup_key,
count(ug.id) as count
FROM user u
INNER JOIN user_group ug on ug.id = u.id
WHERE
u.age >= 11 AND
u.age <= 20 AND
ug.lookup_key in('12345')
GROUP BY ug.lookup_key
HAVING count(ug.id) < 7
I have written this
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<UserGroup> d = query.from(UserGroup.class);
Join<UserGroup, User> join = d.join("users");
Predicate pred1 = criteriaBuilder.between(join.get("age"), ageFrom, ageTo);
Expression<String> exp = d.get("lookupKey");
Predicate pred2 = exp.in(lookupKeys);
query.where(pred1, pred2);
query.multiselect(d.get("lookupKey"), criteriaBuilder.count(d.get("id"))).groupBy(d.get("lookupKey"));
List<Object[]> results = entityManager.createQuery(query).getResultList();
for(Object[] object : results){
System.out.println(object[0] + " " + object[1]);
}
The SQL returns {"12345",4} whereas the code returns {"12345", 37}. The SQL is the correct result. There are 37 users in the database for groups with that lookup key, so I understand where the numbers are coming from but I do not understand how to do the JOIN, GROUP BY, HAVING with the CreateCriteria query so that I get the results. I don't want to use JPQL.
The entities...
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private int age;
private double salary;
#ManyToOne(optional=false,cascade=CascadeType.ALL, targetEntity=UserGroup.class)
#JsonBackReference
private UserGroup group;
// Getters and Setters //
}
#Entity
public class UserGroup {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String lookupKey;
#OneToMany(mappedBy="group",targetEntity=User.class, fetch=FetchType.EAGER)
#JsonManagedReference
private Collection users;
// Getters and Setters //
}
And also, here is the method in which it is implemented
public void summarizeGroupsByLookupKey(long ageFrom, long ageTo, List<String> lookupKeys, long numUsers){
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<UserGroup> d = query.from(UserGroup.class);
Join<UserGroup, User> join = d.join("users");
Predicate pred1 = criteriaBuilder.between(join.get("age"), ageFrom, ageTo);
Expression<String> exp = d.get("lookupKey");
Predicate pred2 = exp.in(lookupKeys);
query.where(pred1, pred2);
query.multiselect(d.get("lookupKey"), criteriaBuilder.count(d.get("id")));
query.groupBy(d.get("lookupKey"));
query.having(criteriaBuilder.<Long>lessThan(criteriaBuilder.count(d.get("id")), numUsers));
List<Object[]> results = entityManager.createQuery(query).getResultList();
for(Object[] object : results){
System.out.println(object[0] + " " + object[1]);
}
}
By way of info...using Spring Boot 1.5.1 and all the default JPA, Hibernate, etc. from there.
Can a JPA expert offer some help? Thanks!
Change the multiselect part to use countDistinct(..)
query.multiselect(d.get("lookupKey")
,criteriaBuilder.countDistinct(d.get("id")));
and also having(..)
query.having(criteriaBuilder.<Long>lessThan(
criteriaBuilder.countDistinct(d.get("id")), numUsers)
);
Original query returned row per matching user in which rows userGroup.id was then multiplied.
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);
For the entity defined as follows:
#Entity
public class Acl implements Serializable {
#Id
#GeneratedValue
#Column(name = "acl_id")
private Long id;
#ElementCollection(fetch=FetchType.EAGER)
#JoinTable(name = "acl_permits")
private Map<String, Integer> permits = new HashMap<String, Integer>();
Query:
query = em.createQuery("select a FROM Acl a " +
"JOIN a.permits p WITH KEY(p) = '" + user + "' and VALUE(p) = " + permit + "");
is converted to:
select acl0_.acl_id as acl1_1_ from Acl acl0_
inner join acl_permits permits1_ on acl0_.acl_id=permits1_.Acl_acl_id
and (permits1_.permits_KEY='user1'
and (select permits1_.permits from acl_permits permits1_ where acl0_.acl_id=permits1_.Acl_acl_id)=1)
What would be the query in order to get it converted into?:
select acl0_.acl_id as acl1_1_ from Acl acl0_
inner join acl_permits permits1_ on acl0_.acl_id=permits1_.Acl_acl_id
and (permits1_.permits_KEY='user1' and acl0_.acl_id=permits1_.Acl_acl_id=1)
You can use native SQL in JPA. So you can do this instead.
query = em.createNativeQuery("select acl0_.acl_id as acl1_1_ from Acl acl0_
inner join acl_permits permits1_ on acl0_.acl_id=permits1_.Acl_acl_id
and (permits1_.permits_KEY=?user and acl0_.acl_id=permits1_.Acl_acl_id=?permit_id)");