spring data jpa findAll generated sql do not use join [duplicate] - spring

I have created two entities Book and Book_Category with one-to-many relationship. When I issued BookCategoryRepository.findAll(), I expected hibernate to use 'INNER JOIN' query. But it just issued query to take data from Book_Category.
What I am missing? What should I do to make hibernate issue JOIN query?
Book.java
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "book_category_id")
private BookCategory bookCategory;
}
BookCategory.java
#Entity
#Table(name = "book_category")
public class BookCategory {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#OneToMany(mappedBy = "bookCategory", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Book> books;
}
BookCategoryRepository.java
public interface BookCategoryRepository extends JpaRepository<BookCategory, Integer> {
}
bookCategoryRepository.findAll()

Hibernate uses by default a second query to retriev the child collection. One reason for this is a proper limit query. Otherwise, there would be more rows in the result set, than entities for the 1 side, if at least one has more than 1 child.
There exists an annotation to change this behaviour in hibernate which is ignored by the Spring Data Jpa Repositories. The annotation is #Fetch(FetchMode.JOIN). You might consider How does the FetchMode work in Spring Data JPA if you really need this behaviour.

Related

How to generate an entity from another entity JPA - Spring boot

I have a spring boot JPA project with an entity called Customers and another one CustomerReports
#Entity
public class Customers {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String Name;
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Reports reports;
//getter and setters..etc
}
#Entity
public class CustomerReports {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private BigDecimal monthlyPayment;
//done
#JsonIgnore
#OneToOne(mappedBy = "reports")
private Customers customers;
//constructors, getters...etc.
}
I want whenever I insert a Customer, a report to also be generated for that customer. The column "monthlyPayment" in reports is also generated through a reference from another table so I don't want to insert those columns manually if that makes sense.
Is there a way to do that? I'm not sure what to google so it would be great if anyone can give me an idea
If I understand your question properly, you can derive CustomerReports entity based on Customers via simple java utility method & then call save if you are using jparepository :
CustomerReports customerReports=reportUtil(customerEntity);
jpaRepository.save(customerEntity);
jpaRepository.save(customerReports);
...
private CustomerReports reportUtil(Customers customerEntity){
/*Derive values for CustomerReports based on Customers & return*/
}
Or if you don't want to do by this way then check if your underlying database support triggers which you can use for inserting data into CustomerReports while doing insert to Customers

Is that possible in spring boot that join column (foreign key) with id

I want to join column without object reference. is that possible?
I want to do foreign key without object reference like that
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "REFERENCE_NO")
private String referenceNo;
#ManyToOne
#JoinColumn(name = "AUTHORITY", referencedColumnName = "id")
private Integer authority;
#ManyToOne
#JoinColumn(name = "PART_TWO_REGISTER_ID")
private Integer partTwoRegisterId;
#Column(name = "PART_TWO_ORDER_NO")
private String partTwoOrderNo;
#Column(name = "REMARKS")
private String remarks;
#Column(name = "HRM_TYPE")
private Integer hrmType;
}
If I found solve this problem, it will helpful for me.
Joining is not needed in this case. If you only need the foreign key value, then simply add the column as a #Column like any other:
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "AUTHORITY")
private Integer authority;
// other fields
// ...
}
No, I don't think that you can join columns between two entities without adding the reference of one to the related entity. You will have to create one entity class corresponding to each of your relational database table and add the reference of one to the other to establish relation.
However, I understand that you may not need all the attributes from your related table based upon your use case, and only wish to select one column from it. You can do that either by only adding required attributes in your joined table entity class (if you are sure you won't need other attributes for that table anywhere else).
Or you can use custom queries using JPQL in your repository class which selects only the required attributes from the tables that you have joined.
I will show you an example of the second way:
//Say, this is your entity class where you wish to join other table to fetch only one attribute from the joined table-
#Entity
#Table(name = "TABLE1", schema = "SCHEMA1")
public class Table1 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN")
private String table2Column;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TABLE2_COLUMN1")
private Table2 table2; //refrence of the joined table entity object
}
// And this is the joined table entity class
#Entity
#Table(name = "TABLE2", schema = "SCHEMA1")
public class Table2 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN1")
private String table2Column1;
#Column(name = "TABLE2_COLUMN2")
private String table2Column2; // The column which we want to select from the joined table
}
In your repository class -
#Repository
public interface Table1Repository extends JpaRepository<Table1, String> {
#Query("SELECT t1 FROM Table1 t1 WHERE t1.id = :id")
public List<Table1> getTable1Rows(#Param("id") String id);
#Query("SELECT t1.table2.table2Column2 FROM Table1 t1 WHERE t1.id = :id")
public String getTable2Column2(#Param("id") String id);
}
Based upon the response from Markus Pscheidt below, I agree when he said there's no need to join the entities if you only need the attribute which is a foreign key. As foreign key is already present as an attribute in your entity (or table) you are working with.
If you need to fetch any other column apart from foreign key, then you may use JPQL to fetch the exact column that you wish to select.

Spring Boot Jpa Update- Avoid unnecessary updates of columns & child entities

I have a jpa entity with parent-child relationship(1:n).
Any sort of update on the entity results in jpa trying to update each and every column, even if there is no change in the value. I'm confused about the update behaviour for child enitities, I guess same would be happening with those also.
I went through #DynamicUpdate over entities but I do not know how they behave in case of child-entities.
public class Tutorial extends Auditable<String> implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private string description;
#OneToMany(
fetch = FetchType.LAZY,
mappedBy = "tutorial",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Collection<Modules> Modules = new HashSet<>();
// Getter/Setters
}
How to come up with an approach so that only necessary fields/columns and child entities and their columns are update on each save.

How to load insert scripts for multiple tables which are linked with foreign key in spring boot

I am using spring boot. I am loading test data through yml by defining spring.datasource.data=classpath:/test-data/sql_file_EntityOne.sql, classpath:/test-data/sql_file_EntityTwo.sql,...
For every single entity it works seamlessly but problem comes when EntityOne and EntityTwo have foreign key relationship and the corresponding insert statements are written in 2 different SQL files as depicted above.
I am using in memory h2 dB for local.
sql_file_EntityOne.sql
(Id_One, data_1,data_2) values(101, 'dat', 5)
sql_file_EntityTwo.sql
(Id_two, Id_Onethis is fk, data_3,data_4)
values(1,101, 'dat2', null, 5)
EntityOne
#Id
IdOne
....
#OneToMany(Cascade.All, mappedBy="entityOneRef")
List entityTwoRef
EntityTwo
#Id
IdTwo
....
#ManyToOne(Cascade.All)
#JoinColumn("entityTwoRef")
EntityOne entityOneRef
Can you please mention the error you are getting here?
You can use the following hibernate annotations for bidirectional relationship:
#OneToMany(mappedBy = ) on parent enity
#ManyToOne #JoinColumn(name = , nullable = false) on child entity
for example, let's take an example of Cart and Item as two entities with a Cart related as one to many with item:
//Cart
#Entity
#Data
public class Cart {
#Id
private Integer cartId;
#Column
private String data;
#OneToMany(mappedBy = "cart")
private Set<Item> items;
}
//Item
#Entity
#Data
public class Item {
#Id
private Integer itemId;
#Column
private String data;
#ManyToOne
#JoinColumn(name = "cart_id", nullable = false)
private Cart cart;
}
#Data is just lombok annotation for getters and setters. Scripts as below:
//Script1
INSERT INTO CART(cart_id,data) VALUES (101,'data1')
//Script2
INSERT INTO ITEM(item_id,cart_id,data) VALUES (1,101,'data2')
Then load the scripts in spring-boot in order:
spring.datasource.data=classpath:sql_script1.sql,classpath:sql_script2.sql
Hope it helps :)

Spring JPA - Get all Child Elements

I have the following POJOs'
Filter ----> Filter Components
#Entity
public class Filter {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
//Setters and getters are not shown
And I have the following child class
#Entity
public class FilterComponents {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private int component_id;
private int component_type;
#ManyToOne
#JoinColumn(name = "filter_id")
private Filter filter;
//Setters and getters are not shown
I created a repository to query for Filters
public interface FilterRepository extends JpaRepository <Filter, Long> {}
I am calling the findAll() function to get all Filters. The function is working fine; however, it only returns the name and id of each filter.
Is there a way to return the corresponding Filter Components as well? I assume I can write a join query, but I have a feeling that there is a cleaner way to do it!
Thank you
Make Filter to FilterComponents a OneToMany unidirectional mapping. So, you will be managing child FilterComponents inside its parent.
#Entity
public class Filter {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#OneToMany
#JoinColumn(name = "filter_id")
private List<FilterComponents> filterComponenets;
}
#Entity
public class FilterComponents {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private int component_id;
private int component_type;
// Removed mapping
}
What you probably missing is a filterComponents field for a Filter e.g.
public class Filter {
...
#OneToMany(mappedBy="cart")
private List< FilterComponents> filterComponents;
You can define eager or lazy fetching depending on your needs here
Your Filter entity does not have any explicit association with FilterComponent entity. In this case you would need to do another request to select all filter components with the given filter id.
On the other hand you can declare the following field in the Filter entity:
#OneToMany(mappedBy = "filter", fetch = FetchType.EAGER)
private List<FilterComponent> filterComponents;
and always load filter components eagerly.
If eager loading does not fit you you can use custom query with left join fetch:
select distinct f from Filter f left join fetch f.filterComponents c

Resources