How to do JPA join in one to many situations? - spring

I have two entities Outlet and products having one to many relationship as follows-
#Entity
public class Outlet{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
#Fetch (FetchMode.SELECT)
#OneToMany(mappedBy = "outletProduct",fetch = FetchType.LAZY)
List<Product> products;
and
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Boolean isActive;
#ManyToOne
#JoinColumn(name = "outletId")
Outlet outletProduct;
Now i want Outlets where products isactive is true.
I am writing follwing JPQL for-
#Query("Select op From Outlet op join op.products pro where pro.isActive=1 order by op.id")
List<Outlet> findAllByVarietiesPriceTimePeriodIn( );
But i am still getting outlets with prodcuts isactive false.
Please suggest.
I want to achieve result which will come from this sql query-
SELECT * FROM outlet join product on product.outlet_id= outlet.id where product.is_active=1 ;

I want Outlet where it has at least one Product but i dont want Products which is isactive false. Right now I am getting all Products in the List
It's not recommended, but will work using JOIN FETCH:
Select op From Outlet op join fetch op.products pro where pro.isActive = 1 order by op.id

Related

Spring Data JPA - Lazy loading and #Fetch( FetchMode.JOIN)

2 Entity ProductMaster and Category
#Entity
#Table(name = "product_master")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#NamedQuery(name = "ProductMaster.findAll", query = "SELECT p FROM ProductMaster p")
public class ProductMaster implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String description;
//Many Product will have one categoary
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="category_id")
private Category category;
//get and set fn
}
#Entity
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#Table(name = "category")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
#Fetch( FetchMode.JOIN)
private Set<ProductMaster> products = new HashSet<ProductMaster>();
//get and set fn
}
while fetchAll in JPA repository of Product. Join is performed and its expected
Hibernate:
select
productmas0_.id as id1_1_,
productmas0_.category_id as categor11_1_,
productmas0_.created_by as created_2_1_,
productmas0_.created_dt as created_3_1_,
productmas0_.description as descript4_1_,
productmas0_.image as image5_1_,
productmas0_.is_favorite as is_favor6_1_,
productmas0_.price as price7_1_,
productmas0_.title as title8_1_,
productmas0_.updated_by as updated_9_1_,
productmas0_.updated_dt as updated10_1_
from
product_master productmas0_ limit ?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.category_name as category2_0_0_,
category0_.created_by as created_3_0_0_,
category0_.created_dt as created_4_0_0_,
category0_.category_desc as category5_0_0_,
category0_.updated_by as updated_6_0_0_,
category0_.updated_dt as updated_7_0_0_,
products1_.category_id as categor11_1_1_,
products1_.id as id1_1_1_,
products1_.id as id1_1_2_,
products1_.category_id as categor11_1_2_,
products1_.created_by as created_2_1_2_,
products1_.created_dt as created_3_1_2_,
products1_.description as descript4_1_2_,
products1_.image as image5_1_2_,
products1_.is_favorite as is_favor6_1_2_,
products1_.price as price7_1_2_,
products1_.title as title8_1_2_,
products1_.updated_by as updated_9_1_2_,
products1_.updated_dt as updated10_1_2_
from
category category0_
left outer join
product_master products1_
on category0_.id=products1_.category_id
where
category0_.id=?
but while fetchAll in JPA repository of category multiple query for product are fired. I want lazy loading behavior here for product while fetch all in Category
select
category0_.id as id1_0_,
category0_.category_name as category2_0_,
category0_.created_by as created_3_0_,
category0_.created_dt as created_4_0_,
category0_.category_desc as category5_0_,
category0_.updated_by as updated_6_0_,
category0_.updated_dt as updated_7_0_
from
category category0_ Hibernate:
select
products0_.category_id as categor11_1_0_,
products0_.id as id1_1_0_,
products0_.id as id1_1_1_,
products0_.category_id as categor11_1_1_,
products0_.created_by as created_2_1_1_,
products0_.created_dt as created_3_1_1_,
products0_.description as descript4_1_1_,
products0_.image as image5_1_1_,
products0_.is_favorite as is_favor6_1_1_,
products0_.price as price7_1_1_,
products0_.title as title8_1_1_,
products0_.updated_by as updated_9_1_1_,
products0_.updated_dt as updated10_1_1_
from
product_master products0_
where
products0_.category_id=? 2022-09-25 13:39:56.507 TRACE 14160 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [2] Hibernate:
select
products0_.category_id as categor11_1_0_,
products0_.id as id1_1_0_,
products0_.id as id1_1_1_,
products0_.category_id as categor11_1_1_,
products0_.created_by as created_2_1_1_,
products0_.created_dt as created_3_1_1_,
products0_.description as descript4_1_1_,
products0_.image as image5_1_1_,
products0_.is_favorite as is_favor6_1_1_,
products0_.price as price7_1_1_,
products0_.title as title8_1_1_,
products0_.updated_by as updated_9_1_1_,
products0_.updated_dt as updated10_1_1_
from
product_master products0_
where
products0_.category_id=?
Problem statement to resolved here is that Number of Product query will be number of row in category table. We want this to be lazy load and don't want to perform multiple query for product while selecting category.
If you want a single query you should use left join fetch :
select c from Category c left join fetch c.products
And for the problem of multiple categories read this article
https://vladmihalcea.com/jpql-distinct-jpa-hibernate/
OneToMany relationships are inherently lazy but setting Fetch( FetchMode.JOIN) will override this lazy behaviour so should be removed if you want lazy fetching of products

Optimal way of checking if user already upvoted/downvoted a comment on a post - Spring JPA

Post entity:
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "post")
private List<PostComment> postComments;
...
}
PostComment entity:
public class PostComment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
#OneToMany(mappedBy = "postComment")
private Set<PostCommentUpvote> postCommentUpvotes;
#OneToMany(mappedBy = "postComment")
private Set<PostCommentDownvote> postCommentDownvotes;
...
}
PostCommentUpvote entity (PostCommentUpvote and PostCommentDownvote have the exact same fields - these entities act like counters)
public class PostCommentUpvote {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "post_comment_id")
private PostComment postComment;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
...
}
All relations are bi-directional as you can see from the annotations.
The goal: When a user (authenticated) upvotes/downvotes a PostComment I want to do the following:
Check if user already upvoted/downvoted the PostComment.
For this I have Post id (even though this is not needed) and PostComment id and both are indexed.
There are three possible 'states' when User up/downvotes the comment:
User hasn't yet up/downvoted that comment, so it is either new upvote or new downvote
User has already upvoted and if he upvotes again, it will remove the upvote (same with downvote)
User has already upvoted and if he downvotes, upvote is removed and new downvote is added (and vice-versa)
What would be the most optimal way of doing this? Get the PostComment by its id and then loop through the List of PostCommentUpvote/PostCommentDownvote and check the User on every iteration? Or perform a tactical SQL request, which must be faster than looping in Java? If so, what would this SQL query look like? Or any other approach to make this performant. I am open to any suggestion.
Thanks
Assuming you have the post comment id and user id, the following JPA query (or close to it) will return true if the user has upvoted on the post comment and false otherwise:
select case when count(postCommentUpvote) > 0 then 'true' else 'false'
from PostCommentUpvote postCommentUpvote
join postCommentUpvote.postComment postCommnent
where postComment.id = :postCommentId
and user.id = :userId
You would then have to perform the same query using the PostCommentDownVote entity. An alternative would be to remove the up and down vote entities, simply create a PostCommentVote entity which has a boolean attribute that indicates up or down, and helper methods isUpvote() and isDownVote() that would interpret the boolean for you. You could get everything you need with a single query that returns a PostCommentVote if the user has up or down voted and null otherwise.
You did not indicate what you want to do if the user has already commented on the post; ignore the request or update the PostComment. Either way the most optimal way of doing this would be not checking at all. Create a unique index on (user_id, post_comment_id) or drop the the id column and make a composite PK of those columns. Then just insert without checking. Use the On Conflict to either ignore or update the request. You may also want to add an Up/Down vote indicator column.

Put Reference from Audit table to Another Table in Hibernate Envers

I'm using Hibernate Envers for Auditing Change Data, I have a Class that store information about companies like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Compnay {
private String name;
private String code;
}
and it's using Envers for keeping the changes of companies.
also, I have a class for Keep the data of items that manufacture in any of this company, the class will be like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Item {
#Column(name = "NAME", nullable = false)
private String name ;
#Column(name = "CODE", nullable = false)
private String code;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COMPANY_ID", nullable = false)
private Compnay compnay;
}
Consider that there is a company in company table like this :
ID
NAME
CODE
1
Apple
100
2
IBM
200
and the data in the item's table will be like this :
ID
NAME
CODE
COMPANY_ID
3
iPhone
300
1
4
iPad
400
1
if I edit the information of Apple company and change the code from 100 to 300 how can I fetch the information of Items that were saved before this change with the previous code? Is there is any way to reference to audit table?
Yes, you can write a HQL query that refers to the audited entities. Usually, the audited entities are named like the original ones, with the suffix _AUD i.e. you could write a query similar to the following:
select c, i
from Company_AUD c
left join Item_AUD i on i.id.revision < c.id.revision
where c.originalId = :companyId

Join 2 tables using Spring JPA

I have two entities(Invoice and InvoiceItems) without adding any relationship.
Invoice
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invoiceID;
#Column(name="code")
private String code;
//other columns
}
Invoice Items
public class InvoiceItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invItemID;
#Column(name="invoice_id")
private Integer invoiceId;
//other columns
}
Can I join these entities and get data without adding relationship using JPA?
If it isn't possible how to join 2 entities using JPQL or Native query?
If your data is valid then using native query you can do that
#Query(nativeQuery = true, "select * from Invoice i join InvoiceItems im on i.id = im.invoice_id")
public List<Invoice> findData();
But that is not a good way join without relation using JPA.
Yes, you can join these entities and get data without adding relationship using JPA, but it's a little bit losing the purpose of using JPA.
You need to create a java class first, which will be the returning data object from the DB. After that you can use entityManager's createNamedQuery method to get the result.
createNamedQuery(String sqlString, ResultClass.Class)
sqlString may be something like:
SELECT INV.INVOICE_ID
INV.CODE
INV_ITEMS.INV_ITEM_ID
FROM INVOICE INV
JOIN INVOICE_ITEMS INV_ITEMS
ON INV.INVOICE_ID = INV_ITEMS.INVOICE_ID;
And the corresponding ResultClass:
public class ResultClass {
private Long invoiceID;
private String code;
private Long invItemID;
// other columns
}
Or you can even use RowMapper to map the object all by yourself for more flexibility by using JdbcTemplate with query() method.

spring data jpa findAll() not working properly

I am having below classes
#Entity
#Table(name = "USR_E_GROUPS")
public class GroupEntity {
#Id
#Column(name = "UIDUSERGROUP")
#GenericGenerator(name = "generator", strategy = "uuid2")
#GeneratedValue(generator = "generator")
private String id;
.........
#OneToMany(mappedBy = "group", cascade = CascadeType.PERSIST)
private List<UserGroupEntity> users;
same is for UserGroupEntity
now if I use groupRepoository.findAll()
It's is firing select query for every Group and inside different select query for UserGroupEntity. so it's taking too much time.
I want to make it to fire select with join so it will be a single query.
This is probably an n + 1 issue.
From the docs
By default, Hibernate3 uses lazy select fetching for collections and
lazy proxy fetching for single-valued associations. These defaults
make sense for most associations in the majority of applications.
By default the children are fetched lazily. Use JOIN FETCH to get the result in a single query.
In your GroupRepoository
#Query("SELECT g FROM GroupEntity g JOIN FETCH g.users gu")
List<GroupEntity> findAllEager();

Resources