My entity of product looks like below:
#Entity
#Table(name = "order")
public class OrderEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "order_products",
joinColumns = #JoinColumn(name = "order_id", referencedColumnName = "order_id"),
inverseJoinColumns = #JoinColumn(name = "product_id", referencedColumnName = "id")
)
private Set<ProductEntity> products = new HashSet<>();
}
ProductEntity:
#Entity
#Table(name = "product")
public class ProductEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(unique = true)
private String name;
#ManyToMany(mappedBy = "products")
private Set<OrderEntity> orders = new HashSet<>();
}
I want to get all orders where product name is equal to wanted value. And I write sql query to get result from database, but I cannot write hibernate query for Spring Data JPA.
My query for postgreSQL looks like this:
SELECT o.order_id, op.product_id, p.name
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And this query return me an id of order, product_id and name of product. And this works. But I didn't know how to write this question as spring query using #Query.
I need a metohod in my repository:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("") <- place for my query in Hibernate sql
List<OrderEntity> findAllByProductName(#Param("name") String name);
}
try this: (it returns full OrderEntity objects )
#Query("select o from OrderEntity o join o.products prod where prod.name = :name")
List<OrderEntity> findAllByProductName(#Param("name") String name);
if you need fetch eager all data for products use : ....OrderEntity o join o.products... in query instead of OrderEntity o join o.products
This is a projection consisting of columns from many entties, so you would have to go for the Result Class strategy.
Basically, you create a POJO class with expected result fields an an equivalent constructor:
public class ResultClass{
private Integer orderId;
private Integer productId;
private String name;
public ResultClass(Integer orderId, Integer productId, String name){
// set the fields
}
}
Then you alter the query a bit:
SELECT new com.mypkg.ResultClass(o.order_id, op.product_id, p.name)
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And change the return type on the interface method:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("...")
List<ResultClass> findAllByProductName(#Param("name") String name);
}
Related
I'm struggling to understand how can I query all products that have specified category association.
I might need something like this
select p.* from product p
inner join product_category pc on p.product_id = pc.product_id
inner join category c on pc.category_id = c.category_id
where c.name = :categoryName;
but in HQL.
Assuming that you use the following mapping:
#Entity
#Table(name = "product")
class Product
{
#Id
#Column(name = "product_id")
private Long id;
#ManyToMany
#JoinTable(name = "product_category",
joinColumns = #JoinColumn(name = "category_id"),
inverseJoinColumns = #JoinColumn(name = "product_id")
)
private List<Category> categories;
// getters/ setters
}
#Entity
#Table(name = "category")
class Category
{
#Id
#Column(name = "category_id")
private Long id;
#Column(name = "name")
private String name;
// getters/ setters
}
you can use the following HQL:
select p from Product
join fetch p.categories c
where c.name = :categoryName
Have 3 entities
Group
#Entity
#Table(name = "`group`")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_group")
private Long id;
#OneToMany(mappedBy = "group",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#OrderColumn(name = "id_student")
private List<Student> students = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "id_faculty")
private Faculty faculty;
.....getters/setters
}
Student
#Entity
#Table(name = "student")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Long.class)
public class Student implements Serializable {
#Id
#Column(name = "id_student")
private Long id;
......
#OneToMany(mappedBy = "student",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private List<Rating> ratings = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "id_okr")
private OKR okr;
#ManyToOne
#JoinColumn(name = "id_group")
private Group group;
.....getters/setters
}
Rating
#Entity
#Table(name = "rating")
public class Rating implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_rating")
private Long id;
#Temporal(TemporalType.DATE)
#Column
private Date date;
#ManyToOne
#JoinColumn(name = "id_student")
private Student student;
#ManyToOne
#JoinColumn(name = "id_paragraph")
private Paragraph paragraph;
.....getters/setters
}
JPA Query
#Query(value = "SELECT g FROM Group g INNER JOIN FETCH g.students s LEFT JOIN FETCH s.ratings r WHERE g.id = :id AND s.group.id = g.id AND (r.student.id = s.id AND r.date BETWEEN :startMonth and :endMonth OR r IS NULL) GROUP BY s.id")
Group findGroupByStudentGroupId(#Param("id") Long id ,#Param("startMonth") Date startMonth, #Param("endMonth") Date endMonth );
I have the student with id 8000 and after extracting query the result list contains 8001 elements which contain 8 students that I have and 7993 null values. If I remove annotation #OrderColumn I have MultiBagException(cannot simultaneously fetch). If add #OrderColumn to rating #OneToMany association in the entity Student I have null values in Rating collection and in Student collection.
For me, the logic of #OrderColumn which return null values as biggest id in collection seems very strange. Is any way how to solve it?
Hibernate version 5.1.0 Final
Spring Data JPA 1.8.2
As you are trying to fetch students with particular group and ratings, you can have your query fetch from student entity and have join on Ratings entity. You don't have to explicitly add join for group entity as it is already in many to one mapping with student.
You can Change your query as follows:
#Query(value = "SELECT s FROM Student s LEFT JOIN s.ratings r WHERE s.group.id = :id AND (r.date BETWEEN :startMonth and :endMonth OR r IS NULL) GROUP BY s.id")
List<Student> findGroupByStudentGroupId(#Param("id") Long id ,#Param("startMonth") Date startMonth, #Param("endMonth") Date endMonth );
In my java spring mvc application, i have two classes:
#Entity
public class Transactions {
#Id
#GeneratedValue
long id;
#OneToOne
Customer customer;
#OneToOne
Merchant merchant;
double value;
}
and the customer class is:
#Entity
public class Customer {
#Id
#GeneratedValue
long id;
String name;
String ageClass;
String language;
String cardUserArea;
}
The repository for the Transactions is:
#Repository
public interface TransactionRepository extends JpaRepository<Transactions ,Long >{
#Query("SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ")
public double getOverAllValue(String merchantID,String ageGroup);
}
But it complains with:
org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [SELECT sum( value) FROM ee.seb.domain.Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ]
The query runns successfully against the database
The answer was in using nativeQuery
#Query(value = "SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ", nativeQuery=true)
public double getOverAllValue(String merchantID,String ageGroup);
Below code is ex. of entity and relationship with other entity :-
#Entity
#Table(name = "F_AREA")
#NamedQuery(name = "FArea.findAll", query = "SELECT f FROM FArea f")
public class FArea implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "F_AREA_AREAID_GENERATOR", sequenceName = "SEQ_F_AREA")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "F_AREA_AREAID_GENERATOR")
#Column(name = "AREA_ID")
private long areaId;
#Column(name = "AREA_NAME_EN")
private String areaNameEn;
#Column(name = "AREA_STATUS")
private BigDecimal areaStatus;
#Column(name = "AREA_UPDATED_DATE")
private Timestamp areaUpdatedDate;
// bi-directional many-to-one association to FCity
#ManyToOne
#JoinColumn(name = "CITY_ID")
private FCity FCity;
// bi-directional many-to-one association to FCustomer
#OneToMany(mappedBy = "FArea")
private List<FCustomer> FCustomers;
.
.
.
.
}
Please update your query with this:
#Query("select sum(t.value) from Transactions t inner join t.customer c where c.id= ?1 and c.ageClass= ?2 ")
public Double getOverAllValue(long merchantID,String ageGroup);
I have two tables with many to many relations.
products(id, description, price,image)----> products_category(idProducts, category _id)----> category(id, category_name).
Here is my enteties:
1. Products
#Entity
#Table(name = "products")
public class Products implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idProducts")
private long id;
#Column(name = "description")
private String description;
#Column(name = "price")
private String price;
#Column(name = "image")
private byte [] image;
public Products() {
}
public Products(String description, String price, byte[] image) {}
#ManyToMany
#JoinTable(
name = "category_products",
joinColumns ={#JoinColumn (name = "Products_idProducts", referencedColumnName = "idProducts")},
inverseJoinColumns = {#JoinColumn(name = "category_id", referencedColumnName = "id")}
)
List<Category> categories = new ArrayList<>();
#ManyToMany
#JoinTable(
name = "users_product",
joinColumns ={#JoinColumn (name = "Products_idProducts", referencedColumnName = "idProducts")},
inverseJoinColumns = {#JoinColumn(name = "users_id", referencedColumnName = "id")}
)
List<Users> usersList = new ArrayList<>();
2.Category
#Entity
#Table(name = "category")
public class Category {
#Id
#Column(name = "id")
private long id;
public Category() {
}
public Category(String category_name) {
this.category_name = category_name;
}
#Column (name = "category_name")
private String category_name;
#ManyToMany(mappedBy = "categories")
private List<Products> products = new ArrayList<>();
I'm try to write query for controller, which return all the products by previosly selected category object with id? i tried many query, but all throws exceptions.
public List<Products> list (Category category) {
//category - object with needed id
Query query;
query = entityManager.createQuery("SELECT c FROM Category c left join c.categories WHERE c.category = :category", Products.class);
query.setParameter("category", category);
return (List<Products>) query.getResultList();
}
java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: categories of: classes.Category [SELECT c FROM classes.Category c join c.categories WHERE c.category = :category]
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750)
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
If you need to retrieve products, you need to do a query that select Product entity, not Category.
So:
return all the products by previosly selected category object with
id
You need to do:
Query query = entityManager.createQuery("SELECT p FROM Product p
JOIN p.categories c
WHERE c.id = :idCategory");
query.setParameter("idCategory", category.getId());
You use LEFT JOIN but this is not necessary in your case, because the unique condition of your query is find a category with a specific ID. This condition will ignore the LEFT part of the JOIN, forcing always a JOIN.
I think there are some errors in your code:
You SELECT c which mean Category but you cast the result list to List<Product>, should be SELECT c.products
Your WHERE c.category = :category clause is not correct because you don't have any category attribute in your Category class, should be WHERE c.id = :id and query.setParameter("id", category.getId());
Hope that help.
I have two entities:
UnsubscribedPartner for unsubscribed from mailing partners
#Entity
#Table(schema = "mailing", name = "unsubscribed_partner")
public class UnsubscribedPartner {
#Id
#Column(name = "partner_id")
private int partnerId;
#Column(name = "unsubscription_date")
private Date date;
#OneToOne(targetEntity = Partner.class, fetch = FetchType.EAGER)
#JoinColumn(name = "partner_id")
private Partner partner;
//GET, SET
}
Partner partner's class
#Entity
#Table(schema = "partner", name = "partner")
public class Partner {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "email")
private String email;
#OneToOne(fetch = FetchType.EAGER, mappedBy = "partner")
private UnsubscribedPartner unsubscribedPartner;
//GET, SET
}
I constructed the following criteria query:
String email;
//...
Criteria criteria = getSession().createCriteria(Partner.class);
if(!(email == null)){
criteria.add(Restrictions.eq("email", email));
}
Criteria unsubscribedCrieria = criteria.createCriteria("unsubscribedPartner", "unsbcr");
unsubscribedCrieria.add(Restrictions.isNull("unsbcr.reason"));
But the result SQL query is
select
count(*) as y0_
from
partner.partner this_
inner join
mailing.unsubscribed_partner unsbcr1_
on this_.id=unsbcr1_.partner_id
where
unsbcr1_.unsubscription_reason_id is null
Inner join is not appropriate here, because the unsubscribed_partner tables may not any partner from the partner table, therefore I need LEFT OUTER JOIN instead. How can I fix that?
The documentation states that createCriteria(String, String) is functionally equivalent to createCriteria(String, String, int) using CriteriaSpecification.INNER_JOIN for the joinType.
So, try with createCriteria("unsubscribedPartner", "unsbcr", CriteriaSpecification.LEFT_JOIN) instead.