JPA Criteria - where clause - java

I have a Entity class which contain some primitive data type and contain some collection of object which have OneToMany relation and ManyToOne relation.
And when I am Fetching data by using JPA Criteria then I am getting all the data but I want to filter the result by using JPA Criteria where clause , I can apply in its primitive data type and its work fine , I have to apply where clause in collection of object which have OneToMany relation some have ManyToOne relation but how to apply where clause in these case , can you tell me ?

Use a .join(). Here is an example from the Criteria API documentation.
For queries that navigate to related entity classes, the query must define a join to the related entity by calling one of the From.join methods on the query root object or another join object. The join methods are similar to the JOIN keyword in JPQL.
The target of the join uses the Metamodel class of type EntityType to specify the persistent field or property of the joined entity.
The join methods return an object of type Join<X, Y>, where X is the source entity and Y is the target of the join. In the following code snippet, Pet is the source entity, Owner is the target, and Pet_ is a statically generated metamodel class:
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);
Joins can be chained together to navigate to related entities of the target entity without having to create a Join instance for each join:
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);

I did that on this way:
Repository
#Query(value = "SELECT e FROM Employee e JOIN e.user u JOIN u.role r JOIN r.grants g where g = :grant")
List<Employee> findByGrant(#Param("grant") Grant grant);
Grant is related ManyToMany with Role wich is related OneToOne with employee.
So you just need to Join the Object and filter with an object setting the PKEY (ID).
Entities
#Entity
public class Employee {
#OneToOne
#JoinColumn(name = "user_id")
private User user;
}
#Entity
public class User implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 7854391295707311278L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name = "role_id")
private Role role;
}
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.MERGE)
#JoinTable(
name = "role_has_grant",
joinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "id")
},
inverseJoinColumns = {
#JoinColumn(name = "grant_id", referencedColumnName = "id")
})
#Fetch(FetchMode.SELECT)
private List<Grant> grants;
}
#Entity
public class Grant implements GrantedAuthority {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
Another relation
#Query(value = "SELECT a FROM Activity a JOIN a.task t WHERE t.project = :project AND a.start BETWEEN :start AND :end")
#Entity
public class Task {
#ManyToOne
#JoinColumn(name = "project_id")
private Project project;
#OneToMany(mappedBy = "task")
private List<Activity> activities;
}

Related

Spring Boot entity many-to-many native query

I have category class as list in entity. How can I fill this entity with a native query?
Product.java
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer pid;
private String name;
#ManyToMany
#JoinTable(
name = "product_category ",
joinColumns = #JoinColumn(
name = "pro_id", referencedColumnName = "pid"),
inverseJoinColumns = #JoinColumn(
name = "cat_id", referencedColumnName = "cid"))
private List<Category> Categories;
}
Category.java
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long cid;
private String catname;
private String desc;
#ManyToMany(mappedBy = "categories")
private List<User> users;
}
How should I write a query? How can I fill the Categories list
#Query(value = "*****", nativeQuery = true)
List<Product> productList();
Instead of native query you can use hibernate queries like the below to find something useful.
Find all
ProductRepository.findAll();
Hibernate query
#Query(select product from Product product join product.categories categories)
List<Product> getAllProducts();
you can also sort the above queries using Pageable Object.
Could you please elaborate your question. As you can get the collection list if it is mapped just by find all function of Product Repository.
However, if you are looking to insert collection you can use cascade type in Categories field of Product Repository. This will automatically insert the categories entity once you save the Product entity to database by setting categories list in Product entity.

How to use Hibernate to combine efficient queries on #ManyToMany association?

I have 2 entities with a ManyToMany association between them - FeedbackApp & FeedbackAppProfile and each of them has a tenant-id FK to Tenant entity.
FeedbackApp entity:
public class FeedbackApp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
/*
Marked as the owner side.
*/
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "feedbackApp_profile_mapping",
joinColumns = #JoinColumn(name = "feedbackApp_id"),
inverseJoinColumns = #JoinColumn(name = "profile_id"))
Set<FeedbackProfile> profiles;
}
The FeedbackProfile entity:
public class FeedbackProfile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
#ManyToMany(mappedBy = "profiles", fetch = FetchType.EAGER)
Set<FeedbackApp> feedbackApps;
}
The feedbackApp_profile_mapping join table has 2 columns and looks like this:
My question: I need to create a query that gets all feedback apps for a specific feedback profile and tenant id. Is it possible to get it with Hibernate/JPA OR I have to manually query my join table?
Let Jpa worry about the optimal Sql query to generate. Your Jpa/criteria/specification query should be something like
select fp.feedbackApps from FeedbackProfile fp LEFT JOIN FETCH fp.feedbackApps where fp.id=:feedback_profile_id and fp.tenant.id=:tenant_id
Since you are asking about efficiency, better remove fetch = FetchType.EAGER from the two many-to-many mappings and use join fetch or named entity graphs to do the joins only when you need to.
Thanks to #GabiM direction, I created a join fetch query which did the job for what I needed:
#Query(value = "SELECT f FROM FeedbackApp f JOIN FETCH f.profiles p WHERE p.id = ?1 AND p.tenant.id = ?2")
Set<FeedbackApp> getFeedbackAppsByProfileId(long profileId, long tenantId);

Spring data jpa operation between table views

in my spring data application i have two TABLE VIEW mapped:
the first view
#Entity
#Immutable
#Table(name="VD_CONT")
#NamedQuery(name="VdContr.findAll", query="SELECT d FROM VdContr d")
public class VdContr {
#Id
#Column(name="CONTR_ID")
private Long id;
#Column(name="CF")
private String cf;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="vdcontr")
private List<VdArr> vdArr;
}
and the second view
#Entity
#Immutable
#Table(name="VD_ARR")
#NamedQuery(name="VdArr.findAll", query="SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name="ARR_ID")
private Long id;
#Column(name="FK_CONTR_ID")
private Long fkContrId;
#ManyToOne(fetch=FetchType.LAZY)
public VdContr vdcontr;
}
If i put a relationship "OneToMany" and "ManyToOne" (1, first view : many, second view), i receive errors.
My question is: is it possibile create a relationship between two table view?
you need to add a #JoinColumn to VdContr.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "vdcontr_id", nullable = false)
In general, views are mapped in the same way as tables.
By looking at your classes, the problem is that Hibernate cannot find the correct join column. You need to specify it.
Also, in your VdArr you should delete the fkContrId, because hibernate will need to use this column to map the VdContr relationship.
By looking at your code, the join column is FK_CONTR_ID, so you need to specify it by using #JoinColumn.
#Entity
#Immutable
#Table(name = "VD_ARR")
#NamedQuery(name = "VdArr.findAll", query = "SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name = "ARR_ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_CONTR_ID")
public VdContr vdcontr;
}

QueryDsl invert manyToMany join

Given we have the following entities that form a many-to-many relationship:
#Entity
public class A {
#Id
private Long id;
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "A_B",
joinColumns = #JoinColumn(name = "id_a"),
inverseJoinColumns = #JoinColumn(name = "id_b"))
private Set<B> listing;
}
#Entity
public class B {
#Id
private Long id;
}
I need to write a query that fetches B and applies some WHERE criteria on A side.
Since the relationsip is modeled from A entity's side it's very easy to write a query that joins these itsself:
new JPAQuery<>(entityManager)
.select(QB.b)
.from(QA.a)
.join(QA.a.listing,b)
.where(QA.a.name.eq("test"))
.fetch();
However since A_B table can be duplicated, this query can produce duplicate entries, which does not do for my scenario. So instead I need to start FROM B and JOIN A. And this is where I need help. I tried:
new JPAQuery<>(entityManager)
.select(QB.b)
.from(QB.b)
.join(QA.a).on(QA.a.listing.any().eq(QB.b))
.where(QA.a.name.eq("test"))
.fetch();
But that does not work as any() merely produces a subselect, instead of many to many join.
How do I write this query in Querydsl?

Eager fetch performs left join in hibernate but fires seperate sql queries in springboot/JPA

I see a lot of posts where Eager fetch performs left join of child table parent table in hibernate. But when I use springboot , hibernate fires seperate sql queries - means one select query for parent table and one select query for child table. Why is there a difference? Has there been any upgrades in springboot or is it something I am doing wrong ?
Below are the entities I am using:
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#Id
#Column(name = "ORDER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int orderId;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private Customer customer;
}
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#Id
#Column(name = "CUSTOMER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long customerId;
#Column(name = "CUSTOMER_NAME")
private String customer_name;
#Column(name = "CUSTOMER_address_id")
private int customer_address_id;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Order> order;
}
Controller:
#RequestMapping(value="/getByCustid/{id}", method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_VALUE)
public Customer getByCustid (#PathVariable Long id) {
Customer s1 = customerRepository.findByCustomerId(id);
return s1;
}
Repository:
public interface CustomerRepository extends JpaRepository<Customer,Long> {
public Customer findByCustomerId(Long customerId);
}
Below are the queries that are getting executed:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_,
customer0_.CUSTOMER_address_id as CUSTOMER2_0_,
customer0_.CUSTOMER_NAME as CUSTOMER3_0_,
customer0_.DAT_SRC_ID as DAT_SRC_4_0_
from
Customer customer0_
where
customer0_.CUSTOMER_ID=?
select
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_1_,
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_1_,
order0_.DAT_SRC_ID as DAT_SRC_2_5_1_
from
Ordertable order0_
where
order0_.ORDER_CUSTOMER_ID=?
EDIT : is this related to #Fetch(FetchMode.JOIN) -- Link:JPA eager fetch does not join
Hibernate: Multiple select queries made by Hibernate for Fetch mode Eager
To check whether FetchMode.JOIN works, I have added FetchMode.JOIN in entities as shown below but still no success with Join Query:
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private List<Order> order;
}
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#JsonIgnore
#ManyToOne()
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private Customer customer;
}
The findByCustomerId will actually generate a query based on that method instead of using em.find. It will create something along the lines of SELECT c FROM Customer c WHERE c.customerId=:customerId. afterwards it will notice the fetch strategy and obtain the needed references. This is also explained here. The query will do exactlly what you instruct it to do.
If you want to eagerly load the reference you would need to write the query yourself along the lines of SELECT c FROM Customer c JOIN FETCH c.orders o WHERE c.customerId=:customerId, this will automatically retrieve the orders.
However the customerId is actually the primary key or identitifier for your entity and thus you should actually be using the findById or findOne method (depending on your Spring Data JPA version). This will use the EntityManager.find which should take the mapping information into account and create the appropriate query.

Categories