Spring Data/SQL Query to find all matches by collection - java

Could you please advise how to write query in Spring Data JPA repository? Here is the situation:
I have 2 Entities: Customer and Product with relationship 'OneToMany' - means one Customer may have many products. In code it looks like Customer entity has Set products and Product has reference to Customer customer, very simple. If I retrieve Customer from DB JSON would look like this: {"id":10, "name":'John Smith',"personalCode":12345678,"products":[ {"id":15,"type":"productType1"}, {"id":20,"type":"productType2"}] }
The question is how can I write query to DB to find all customers whose products match passed products collection? For example I want to find all customers who owns products with type1 and type2. THANKS!
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private String personalCode;
#Enumerated(EnumType.STRING)
private Country country;
private String internetBankUserId;
#Enumerated(EnumType.STRING)
private CustomerType type;
#JsonManagedReference
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
#SortNatural
private SortedSet<Product> products = new TreeSet<>();
#Entity
#Table(name = "product")
public class Product implements Comparable<Product>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private ProductType type;
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id")
private Customer customer;

This solution worked for me:
#Query("SELECT c FROM Customer c join c.products p where p.type in :products
and SIZE(c.products) >= :count")
Set<Customer> findAllByProductType (#Param("products")Set<ProductType> products,
#Param("count") Integer count );

Related

Update object in relation by Hibernate ORM in Java

I have question about access to data.
I have that DB:
[country: id, country_name],
[city: id, country_id, city_name],
[address: id, shop_data_id, city_id, address_data],
[shop_data: id, data]
My relations country-city one to many, city-address one to many, address-shop_data one to one.
I'm looking for information that can I do that SQL query with ORM, or what is the best way do do it in ORM.
UPDATE shop_data
INNER JOIN country ON country.id=1
INNER JOIN city ON country.id=city.country_id
INNER JOIN address ON city.id= address.city_id
INNER JOIN shop_data ON address.shop_data_id=shop_data.id
SET shop_data.data="shop data string"
WHERE shop_data.id=address.shop_data_id
I know that in SQL I should start by shop_data, but by doing this I want to show that I want start in ORM by country entity.
I wrote entities in Hibernate with annotation
#Entity
#Table(name="country")
public class Country{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "country")
private String country;
#OneToMany(mappedBy = "country", fetch=FetchType.LAZY)
#JsonBackReference
private List<City> cities = new ArrayList<>();
// getters/setters ..
}
#Entity
#Table(name="city")
public class City{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "city")
private String city;
#OneToMany(mappedBy = "city", fetch=FetchType.LAZY)
private List<Address> adresses = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="country_id")
#JsonIgnore
#JsonManagedReference
private Country country;
// getters/setters ..
}
#Entity
#Table(name="address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "address")
private String address;
#Column(name = "district")
private String district;
#Column(name = "post_code")
private String postCode;
#OneToOne
#JoinColumn(name="shop_data_id")
private ShopData shopData;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="city_id")
private City city;
// getters/setters ..
}
#Entity
#Table(name="shop_data")
public class shopData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "data")
private String data;
// getters/setters ..
}
I know that I can use getters starting from country that returns List<ObjectType> and from there get my object. Next run function update and update right row. But in this way are done some number of queries.
Is it possible to do by Java Hiberante ORM by one query? Or which way is the best to minimize query amount? By this method I also want to update next also address data.
You should definitely read a book about JPA/Hibernate to understand what JPQL or HQL supports. You can just do joins as you do them with SQL, except for DML statement, but you don't need that. In your case a simple subquery is enough to model what you need. A possible query could look like the following:
UPDATE ShopData s
SET s.data="shop data string"
WHERE EXISTS (
SELECT 1
FROM Country c
WHERE c.id = 1 AND c.city.address.shopData.id = s.id
)

Are these Spring/Hibernate entities good (performance-wise) for culinary app?

I want to store recipes and ingredients in db. I came up with these entities:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String notes;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<IngredientQuantity> ingredientQuantities = new ArrayList<>();
}
#Entity
public class Ingredient {
#Id
private String name;
#OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private List<IngredientQuantity> ingredientQuantities = new ArrayList<>();
}
#Entity
public class IngredientQuantity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "fk_recipe")
private Recipe recipe;
#ManyToOne
#JoinColumn(name = "fk_ingredient")
private Ingredient ingredient;
private double quantity;
}
Now when I want to create a new recipe, I would have to check every ingredient if they already exist in db or not. Is this approach any good? And if it is fine, do I make a one call to db to fetch all ingredients using "WHERE (...) IN ()" clause or hit db with multiple single queries?

Spring JPA stop creating a new table with relationship

I am trying to stop my relationship making new tables. I have tried multiple approaches to this problem, but there seems to be an error every way I turn. For instance when I try the following code:
//other variables
#OneToMany(fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private List<User> users= new ArrayList<>();
I get the following error:
Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`eb322`.`#sql-3140_2e7`, CONSTRAINT `FK20sqpkpotyyf5wx4jfmp519lu` FOREIGN KEY (`user_id`) REFERENCES `year` (`year_id`))
I have checked all my tables and indexes in the database and I cannot find this constraint anywhere. How do I go about removing it. I basically want to have my schema be like this:
Year will have a list of all students, teachers. When a student is enrolled they will be added to that year etc.
If I don't add the join Column I simply get another table saying
Year.students
How do I combine these together.
This is my student class just incase there's something wrong here:
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int User_id;
}
How I am adding data to year table
//get data about student
Student s = ssrepo.findByName(name);
Year y = yyrepo.findByYear(year);
List<Student> students = y.getStudents();
students.add(s);
yyrepo.save(y)
You seem to be using Unidirectional OneToMany relationship
Hibernate uses an association table to map the relationship so when you remove #JoinColumn annotation an association table is created.
As Year has one to many relationship with student, the type of the List should be List<Student> instead of List<User>
#OneToMany(fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private List<Student> users= new ArrayList<>();
And using OneToMany Unidirectional association is normally not recommended because of its performance issues. You can consider using bidirectional association. It would be something as follows
public class Year {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "YEAR_ID")
private Long id;
#Column(name = "TYPE_ID")
private Long typeId
#Column(name = "TYPE")
private Boolean type // 1 or 0 to know if typeId is of student or teacher
#Column(name = "YEAR")
private Date year
#OneToMany(mappedBy="typeId", fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
private List<Student> students;
#OneToMany(mappedBy="typeId", fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
private List<Teacher> teachers;
}
public class Teacher{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TEACHER_ID")
private Long id;
#ManyToOne
#JoinColumn(name="TYPE_ID", nullable=false)
private Year typeId;
}
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "STUDENT_ID")
private Long id;
#ManyToOne
#JoinColumn(name="TYPE_ID", nullable=false)
private Year typeId;
}
There are two ways to do this. The first is bidirectional. Where you do the mapping in the two entities. here in this link.(https://dzone.com/articles/introduction-to-spring-data-jpa-part-4-bidirection)
hava exemples.
public class MyClass {
#OneToMany(mappedBy = "myClass", fetch = FetchType.LAZY,
cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "user_id")
private List<User> users;
}
mappedBy is to say who is the dominate in the relationship. In this case, MyClass has the strongest relationship.
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private MyClass myClass;
}
I believe that this is the best way, because her realities are apparent in both entities. There is a way to do it in a unidirectional way. Exemple in link (How to define unidirectional OneToMany relationship in JPA)

OneToOne mapping JPA - Cascade.All not copying the id column

I tried searching for this specific issue but could not get any help
While creating Employee --> Account should get created.
My entities are --> Employee entity
#Entity
#Table(name = "EMPLOYEE")
public class EMPLOYEE implements Serializable {
private static final long serialVersionUID = -1798070786993154676L;
#Id
#Column(name = "EMPLOYEE_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int EMPLOYEE_id;
#Column(name = "EMPLOYEE_name")
private String EMPLOYEE_name;
#Column(name = "EMPLOYEE_desc")
private String EMPLOYEE_desc;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "EMPLOYEE_id")
#MapsId
private ACCOUNT account;
// getters and setters
}
account entity -->
#Entity
#Table(name = "ACCOUNT")
public class ACCOUNT implements Serializable {
private static final long serialVersionUID = -6790693372846798580L;
#OneToOne( fetch = FetchType.LAZY)
#JoinColumn(name = "EMPLOYEE_id")
private EMPLOYEE employee
#Id
#Column(name = "account_id")
private int account_id;
#Column(name = "account_desc")
private String account_desc;
// getters setters
}
While creating Employee --> Account should get created.
My pojo is getting mapped correctly as shown below -->
employee = { EMPLOYEE_id = 0 ,
EMPLOYEE_name = 'abc' ,
EMPLOYEE_desc = 'new employee' ,
account = { account_id = 1 ,
account_desc = 'my account' ,
EMPLOYEE_id = 0 }
}
so when i same employee object - it generates employee_id , but the same employee_id is not getting populated in account table
It is partly working --> in the Account table Employee_id is not getting populated.
I tried searching so many questions on stackOverflw - but no luck so far.
Any help is appreciated.
Thank you in advance
If the #OneToOne is defined on both sides of the relationship, one side should be the owner. That is the entity that has the foreign key. The other side should have the mappedBy attribute. So the following works for me
#Entity
#Table(name = "EMPLOYEE")
public class EMPLOYEE implements Serializable {
#OneToOne(cascade= CascadeType.ALL, mappedBy = "employee") // non-owner side
private Account account;
....
}
#Entity
#Table(name = "ACCOUNT")
public class Account implements Serializable {
#OneToOne( fetch = FetchType.LAZY)
#JoinColumn(name = "EMPLOYEE_id") // owner-side
private Employee employee;
}

Hibernate updating child entries in one to many Mapping

I have a
company table
department table
employee table.
I am using hibernate to persist data in the database.
1. One to Many Relationship between Company and Department .
A company can have multiple departments and a department can have multiple employees.
I have done corresponding one to many mapping of entities as mentioned in below code. Request to update these entities comes from UI in the JSON format.
I have provided company Id, department Id, and employee Id in the request.
Now Suppose If for a particular company , there is one department in the database with dept_id 3 . . In the Json request, I get a request to update that particular company with one more department. So after updation, previous entry should remain as it is, i.e department with ID 3 ,should remain untouched and new entry should be added with some department Id, say 4,.
Now that company would have two departments one with Id 3 and other with id 4.
How could this be achieved??..Also department entries , which are not there in the request, should be deleted from the database... Same goes for the relation between employee and department,.request may ask to add new employee for a particular department,keeping the existing one.
Please help me with this, what configuration/approach has to be done in my code, to achieve this.
Here is the code for these three tables:
#Entity
#Table(name = "COMPANY")
#Getter
#Setter
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "company_id")
private long companyId;
#Column(name = "company_region")
private String companayRegion;
#Column(name = "company_code")
private String companyCode;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
private List<department> departments;
public Company() {
}
}
#Entity
#Table(name = "Department")
#Getter
#Setter
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "dept_id")
private long departmentID;
#Column(name = "dep_code")
private String departmentCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;
#OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees;
}
#Entity
#Table(name = "Employee")
#Getter
#Setter
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private long employeeId;
#Column(name = "emp_code")
private String empCode;
#Column(name = "emp_name")
private String empname;
#Column(name = "employee_city")
private String employeeCity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "dept_id")
private Department department;
}
the mappings seem correct, what you need is to use merge to obtain an Hibernate-managed entity and copy the fields from the detached instance (coming from the REST api):
Company c = (Company) session.merge(companyFromDto);
Merge should take care to create a new Company, or update an existing one (depending if it is already in the DB) as well as cascade the associations.
Make sure your JSON contains companyId / departmentID / employeeId
Load the company by companyId
INSERT all department entries where departmentID == 0, and fetch the generated ID (Hibernate will update the POJO, or return a new one with the ID set)
UPDATE all department entries where departmentID > 0, and remember the used departmentID
Iterate over company.getDepartments() and iterator.remove() entries with departmentID not contained in the collection built from (3) and (4)
By persisting the company Hibernate will detect which departments were removed, and delete those
Do basically the same for the department -> employee relationship

Categories

Resources