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);
Related
I have two entities mapped Board and Tag by #ManyToMany to a join table board_tag_table.
How would I return the top 5 most common tag_id in the board_tag_table?
enter image description here
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinTable(name = "board_tag_table",
joinColumns = {
//primary key of Board
#JoinColumn(name = "id", referencedColumnName = "id")
},
inverseJoinColumns = {
//primary key of Tag
#JoinColumn(name = "tag_id", referencedColumnName = "tag_id")
})
private Set<Tag> tags = new HashSet<>();
}
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer tag_id;
private String tagname;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "tags")
private Set<Board> boards = new HashSet<>();
}
Unable to find how to query within a many to many table
you can pass through foreach and write your query in Tag repository, but I think you can't write query, because they are have list from two sides
Consider using a native query instead.
If you want to use the JPA, you can add a field (Eg. usedCount) in the Tag entity and follow the instructions here https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result.
The query should look like this:
List<Tag> findByUsedCount(Sort sort, Pageable pageable);
Don't look at it as trying to access the board_tag_table, and instead look at how you would do this with the java entities themselves. This would be just selecting the 5 top Tags based on the number of boards they have. "select t.tag_id, count(b) as boardCount from Tag t join t.boards b group by t.tag_id order by boardCount", then use maxResults to limit the returned values to 5
I am building a spring boot backend and trying to solve my Lazy Loading Problems with a Dto or the join fetch solution. I've got the following entity:
#Entity
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//Some other stuff
#OneToMany(cascade = CascadeType.REMOVE)
#JoinTable(name = "project_categories",
joinColumns = #JoinColumn(name = "project_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "categories_id", referencedColumnName = "id"))
private Set<Categories> categories;
#OneToMany(cascade = CascadeType.REMOVE)
#JoinTable(name = "project_services",
joinColumns = #JoinColumn(name = "project_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "services_id", referencedColumnName = "id"))
private Set<Services> services;
//Constructors, Getters, Setter, ...
And in addition to that the Categories/Services Entity.
#Entity
#Table(name = "categories_table")
public class Categories {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long catId;
#ManyToOne
#JsonIgnore
private Project project;
//Construcor, Getter, Setter, ...
Now as I mentioned before I've tried to use a dto or the Join fetch method.
With my Join Fetch Method I've got the following Repo setup:
#Query("SELECT p FROM Project p JOIN FETCH p.categories " +
"JOIN FETCH p.services WHERE p.id = :projectId")
Optional<Project> findFullProjectById(#Param("projectId") Long id);
Nothing special here, but when I try to run it I get a ObjectNotFound Exception. But I know for sure it exists.
With the dto approach I've got the following setup:
#Query("SELECT new path.ProjectDto(p, p.categories, " +
"p.services) FROM Project p WHERE p.id = :projectId")
Optional<Project> findFullProjectById(#Param("projectId") Long id);
Here I've got a SQL Syntax Error when executing the above method.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '. as col_1_0_, . as col_2_0_ from Project project0_ inner join project_categorie' at line 1
Which makes sense, because spring boot tries to execute the following query:
select project0_.id as col_0_0_, . as col_1_0_, . as col_2_0_ from Project project0_ inner join project_categories categories1_ on project0_.id=categories1_.project_id inner join categories_table categories2_ on categories1_.categories_id=categories2_.id inner join project_services services3_ on project0_.id=services3_.project_id inner join services_table services4_ on services3_.services_id=services4_.id where project0_.id=?
I really hope some of you guys could help me here.
Cheers, Nicklas
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?
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.
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;
}