How do i get all Questions belonging to one Survey? - java

First of all im not too experienced with all that DB stuff so pls have mercy on me.
I have 2 entity classes. Survey and Question.
#Entity
#Table
public class Survey {
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String surveyName;
private int questionCount;
private String owner;
private int participant;
#OneToMany(mappedBy= "survey", cascade=CascadeType.ALL)
private List<Question> question = new ArrayList<Question>();
and
#Entity
#Table
public class Question {
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String question;
private int index;
#ManyToOne
#JoinTable
private Survey survey;
I have a hard time to write the right query to get all Questions which belong to one Survey with HQL. Maybe somebody can give me a hint in the right direction or give me an example how to do it.

There are 2 ways to get all Questions of a Survey:
1. Traverse a managed association
You modeled a bidirectional many-to-one association. So, if you already loaded the Survey entity, you can call the getSurvey() method on that entity. Hibernate will then generate and execute the query to fetch the Survey entities.
There are several ways to optimize that if you're experiencing n+1 select issues. But that's a more advanced topic.
2. Implement a query
If you don't need the Survey entity, it's better to use a JPQL query which only loads the Survey entities. JPQL's syntax is very similar to SQL, and I explained it in great details in my guide to JPQL.
Here is the query that returns all Survey entities associated with a Query.
TypedQuery<Question> q = em.createQuery("SELECT q FROM Survey s JOIN s.question q WHERE s.id = :id", Question.class);
q.setParameter("id", id);
q.getResultList();

Related

How to get Count of OneToMany field in JPA Enity?

How can we get the count of OneToMany field of JPA entity as querying count for each parent entity while fetching as a list is costly and there is no way in JPA Repository.
I want to get the number of likes and comments for each PostEntity. The field is Lazy fetch type and if I call likes.size() or comments.size() then it will load all of the comments and likes from database and there can be thousands of comments and likes.
I know I can create a seperate repo for likes and comments to get the counts but while calling method from PostRepository how to get the counts for each and every entity? What is the best and efficient way?
Parent Entity
#Entity
#Table(name = "posts")
#Getter
#Setter
public class PostEntity extends MappedSuperClassEntity<UserEntity> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Nullable
private String title;
#Nullable
private String postText;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private UserEntity user;
#Nullable
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "community_id")
private CommunityEntity community;
#OneToMany(fetch = FetchType.LAZY)
private List<CommentEntity> comments;
#OneToMany(fetch = FetchType.LAZY)
private List<LikeEntity> likes;
#Transient
private int numberOfLikes;
#Transient
private int numberOfComments;
}
I would like to get the likes and comments count for each PostEntity while querying for the list of posts.
My Repo
public interface PostsRepository extends PagingAndSortingRepository<PostEntity, Integer> {
#Query(value = "SELECT P FROM PostEntity P WHERE P.user.id = :userId ORDER BY P.createdDate DESC")
Page<PostEntity> getUserPosts(int userId, Pageable pageable);
#Query(value = "select P from PostEntity P where p.community.id = :communityId order by P.createdDate desc")
Page<PostEntity> getCommunityPosts(int communityId, Pageable pageable);
}
I searched for a lot and someone suggested to use #Formula annotation for custom queries on the entity field but #Formula is hibernate specific and don't know if it works with #Transient field. Is there any JPA specific way to do that as it's a common problem.
You need "LazyCollection" annotation with EXTRA option.
#OneToMany(fetch = FetchType.LAZY)
#LazyCollection(LazyCollectionOption.EXTRA)
private List<CommentEntity> comments;
This annotation would allow to access "size()" without loading.
You can check this article.
https://www.baeldung.com/hibernate-lazycollection
Sometimes, we're only concerned with the properties of the collection, and we don't need the objects inside it right away. For example, going back to the Branch and the Employees example, we could just need the number of employees in the branch while not caring about the actual employees' entities. In this case, we consider using the EXTRA option. Let's update our example to handle this case. Similar to the case before, the Branch entity has an id, name, and an #OneToMany relation with the Employee entity. However, we set the option for #LazyCollection to be EXTRA:
I try to add comment but i have no writing comment access because of reputation so i send an answer.

Solution to N+1 issue in JPA and Hibernate with multiples Many-To-One

I found some solutions to avoid N+1 problem but this solutions works only for single Many-To-One relationship.
For example, the following question: What is the solution for the N+1 issue in JPA and Hibernate?
The problem that I'm trying to solve is this:
#Entity
public class Book implements Serializable {
#Id
private Long id;
private String title;
#ManyToOne(fetch = FetchType.LAZY)
private Author author;
#ManyToOne(fetch = FetchType.LAZY)
private Brand brand;
}
Solutions like try to fetch through JPQL doesn't work and fetch just one relationship, for example:
SELECT b FROM Book b
INNER JOIN FETCH b.author
INNER JOIN FETCH b.brand
In this case, only 'author' relationship would be fetched and N+1 problem will happen with 'brand' relationship.
Do you know any solution to solve this specific problem?
Thank you!
Try this with criteria api:
CritieriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
Root<Book> root = cq.from(Book.class);
root.fetch("author"); //root.fetch(Book_.author) if use metamodel
root.fetch("brand"); //root.fetch(Book_.brand) if use metamodel
cq.select(root);
List<Book> result = entityManager.createQuery(cq).getResultList();

Hibernate Named Query (Select all instances of entity not appearing in other entity)

I have a problem of creating NamedQuery with Hibernate. The problem is I need to select a list of Books not appearing in Orders. My classes looks something like this:
#Entity
#NamedQueries({ #NamedQuery(name = "Book.findAvailable",
query = "SELECT b FROM Book b WHERE b.id not in ????????") })
public class Book {
#Id
#GeneratedValue
private Long id;
......
and Order:
#Entity(name = "orders")
public class Order {
#Id
#GeneratedValue
private Long id;
#ElementCollection
private List<Book> items;
.....
As you see, I keep my Books in order in a list. The Query I need should pull out all the books from the DB which don't apear in any order.
Any help is appreciated. Many thanks.
Try
SELECT b
FROM Book b
WHERE NOT EXISTS (
SELECT o
FROM Order o
WHERE b MEMBER OF o.items
)
to find books for which there is no order such that the book is a member of the order's items list.
(I should note that this is probably not very efficient due to the negations. Flagging Books once they occur in an Order is actually more efficient.)

Deleting Objects in Many to Many association with Hibernate and Java

I've been searching for days but can't seem to find the answer.
Given this many to many (employee/meeting)
#Entity
#Table(name="EMPLOYEE")
public class Employee {
#Id
#Column(name="EMPLOYEE_ID")
#GeneratedValue
private Long employeeId;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="EMPLOYEE_MEETING",
joinColumns={#JoinColumn(name="EMPLOYEE_ID")},
inverseJoinColumns={#JoinColumn(name="MEETING_ID")})
private Set<Meeting> meetings = new HashSet<Meeting>();
}
#Entity
#Table(name="MEETING")
public class Meeting {
#Id
#Column(name="MEETING_ID")
#GeneratedValue
private Long meetingId;
#ManyToMany(mappedBy="meetings")
private Set<Employee> employees = new HashSet<Employee>();
}
I can add employees to a meeting and it shows up in the employee_meeting table.
When I do get a meeting object and delete it, it's also gone from the join table but remains in the employee set... Is this the expected behaviour?
Here's how I would remove a meeting object
session.delete(meeting);
transaction.commit();
At this point it's gone from the table.
Thanks!
Yes this is correct behaviour. If you have many-to-many relationship, then you need to delete it manually. Please refer this link for hibernate collection mapping strategy

How to create Many-One Mapping in hibernate?

I want to create Many-One Mapping between two tabels, Expense(ID, NAME, CATEGORY) and
Category(ID, NAME).
In my class i have created a field 'Category category' and its setters and getters.
I did them after seeing some stuff from internet. What are all the changes i have to do in my Category.java class. For now, its looks like,
public class Category{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int catId;
private String catName;
public Category() {
}
public int getCatId() {
return this.catId;
}
public void setCatId(int catId) {
this.catId = catId;
}
public String getCatName() {
return this.catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
}
I dont want to do mappings with xml config. I think, annotations is good for a beginner like me.
And my Old! SQL query looks like,
SELECT EXPENSES.EXPNS_ID, EXPENSES.CAT_ID, EXPENSES.NAME, CATEGORY.CAT_NAME FROM EXPENSES INNER JOIN CATEGORY ON EXPENSES.CAT_ID = CATEGORY.CAT_ID WHERE USER_NAME="+currentUserName
How to use inner join in Hibernate?
Any Suggestions!!
Thanks!
Update
Thanks for all answerers,
I tried what you told and it returns a empty list.
To, test i set the 'userName=Tamil' which is in the table.
The query generated by Hibernate is looks like below,
select expens0_.expnsId as expnsId1_, expens0_.catId as catId1_, expens0_.category_catId as category7_1_, expens0_.userName as userName1_ from Expens expens0_ inner join Category category1_ on expens0_.category_catId=category1_.catId where expens0_.userName=?
As a beginner, i have some doubts in JPQL, I want catName from Category[catId, catName] table. And the catId is also available in Expens[expnsId, catId, userName].
By adding the below lines in Expens.java class, how it will give me catName along with the other variables in the Expens table.
#ManyToOne
private Category category
// getters, setters
I cant able to understand it. Without understanding this i cant move further, i have to give more mappings in my project. If clear with this mapping, i can move to the rest with confidence.
The query i used is pascal's version: Query query = hSession.createQuery("SELECT e FROM Expens e JOIN e.category c WHERE e.userName = :userName").setParameter("userName", userName);
For me, the query generated by hibernate is looks like same as my Old SQl query. I cant able to find problem here.
Actually, a big part of the documentation that would be useful in your case is located in the Hibernate Annotations Reference Guides (links provided below). Reading it would be very worth it.
That being said, regarding your specific question, the simplest possible mapping would be:
#Entity
public class Expense {
#Id #GeneratedValue
private Long;
#ManyToOne
private Category category
// getters, setters
...
}
That's all.
If you want to make it bi-directional, you'll have to add a OneToMany on the other side (and don't forget the mappedBy element since the association is bidirectional):
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
#OneToMany(mappedBy="category")
private Set<Expense> expenses = new HashSet<Expense>();
....
}
And a possible JPQL query would be:
SELECT e FROM Expense e JOIN e.category c WHERE e.username = :username
Update: Hibernate and JDBC are different. With Hibernate, you need to think objects and the above HQL query (which was more an example) will actually return a List<Expense>. To get a category name, iterate over the results and navigate through the association. For example:
List<Expense> expenses = ... // some code to retrieve a list by username
for (Expense expense : expenses) {
System.out.println(expense.getCategory().getName());
}
References
2.2. Mapping with JPA (Java Persistence Annotations)
2.2.5.2. Many-to-one
As Bozho suggested,
#ManyToOne(fetch=FetchType.EAGER) // Gonna be eager by default anyway
#JoinColumn(name="CATEGORY_ID")
private Category category;
Plus this in your Category class to make it bidirectional,
#OneToMany(mappedBy="category")
private List<Expense> expense;
You need not do an inner join like that. When you query the expense, the related category will automatically get loaded eagerly, most likely using join.
In your Expense class have:
#ManyToOne
#JoinColumn(name="CATEGORY_ID")
private Category category
As pointed in the comments, if you need to access all expenses in a given category, i.e. have the one-to-many relationship, you can have:
#OneToMany
private List<Expense> expenses;
I, for example, prefer to use as little #OneToMany mappings as possible - you'd have to manager eager/lazy loading, at some point limiting the number of results, etc. For them I tend to use HQL queries that fetch the subset of objects (expenses in your case) that I need.

Categories