Query dsl with predicate and fetch join - java

is there a chance to fetch join entity with using predicate?
#Entity
public class Student {
#Id
private int id;
private String hobby;
private String favouriteColor;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "CLASS_ID", referencedColumnName = "ID")
private Classroom classroom;
}
#Entity
public class Classroom {
#Id
private int id;
private String classRoom;
private List<Student> students;
}
I have some predicate
public class StudentPredicate {
private StudentPredicate() {}
public static Predicate createPredicate(final Student filter) {
QStudent student = QStudent.student;
BooleanBuilder builder = new BooleanBuilder();
if (isNotBlank(filter.getHobby())) {
builder.and(student.hobby.eq(filter.getHobby());
}
if (isNotBlank(filter.getFavouriteColor())) {
builder.and(student.favouriteColor.eq(filter.getFavouriteColor()));
}
return builder.getValue();
}
}
and repository
#Repository
public interface StudentRepository extends CrudRepository<Student, Integer>, QueryDslPredicateExecutor<Student> {
}
and now how can I find all students with fetched classrooms?
studentRepository.findAll(predicate)
How to say to query dsl that these findAll should fetch classroom?

As there's FetchType.LAZY for classroom field in Student class, so here while you call getClassRoom() will actually fetch the related entity from db or either you can use FetchType.EAGER.

Related

Spring boot hibernate bidirectional mapping many to one can not established relationship

I have two entity table one category and other is subject
My category entity
#Entity
public class Category extends AuditableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "description_id")
private CategoryDescription description;
#OneToMany( mappedBy = "category", cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Subject> subjects;
//getter and setter
}
And my Subject entity
#Entity
public class Subject extends AuditableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "description_id")
private SubjectDescription description;
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
//Getter and Setter
}
Category Repository
#Repository
#Transactional
public interface CategoryRepository extends
JpaRepository<Category, Integer> {
}
Subject Repository
#Repository
#Transactional
public interface SubjectRepository extends JpaRepository<Subject,
Integer> {
}
Category Controller
#RestController
#RequestMapping("/category/api/")
public class CategoryResource {
private final CategoryService categoryService;
private final SubjectService subjectService;
private final TopicService topicService;
public CategoryResource(CategoryService categoryService,
SubjectService subjectService, TopicService topicService) {
this.categoryService = categoryService;
this.subjectService = subjectService;
this.topicService = topicService;
}
#PostMapping("save")
public void saveCategory(#RequestBody Category category) {
categoryService.save(category);
}
I am using postman to save data. Problem is that after saving data to the category and subject table my subject table column category_id is null i can not established a relationship between them my sql structure and data is after saving data it shows like
Category table
Subject Table
category_id is NULL how to set category id i am trying many ways but couldn't find a solution.Please help me to solve this issue
It's great that you are learning spring boot!
To answer your question since the answer is pretty simple, your code is missing category in subject.
subject.setCategory(category);
Now this might cause you an exception, so make sure you save category before you persist subject.
Cheers!

Create Spring Data Jpa Specification method without JOINS

I have a Spring-MVC online-store project where I use Spring Boot and Hibernate. I decided to use the Specification for filtering. Thus, I wrote a method for the specification using JOINS. Please, tell me how you can write the same method without JOIN.
TeaSpecification class:
public static Specification<Tea> getTeasByFilter(Long colorId, Long typeId, Long countryId) {
return (root, query, criteriaBuilder) -> {
Join<Object, Object> colorJoin = root.join(Tea_.TEA_COLOR);
Join<Object, Object> typeJoin = root.join(Tea_.TEA_TYPE);
Join<Object, Object> countryJoin = root.join(Tea_.COUNTRIES);
Predicate countryPredicate = criteriaBuilder.equal(countryJoin.get(Countries_.ID), countryId);
Predicate colorPredicate = criteriaBuilder.equal(colorJoin.get(TeaColor_.ID), colorId);
Predicate typePredicate = criteriaBuilder.equal(typeJoin.get(TeaColor_.ID), typeId);
return criteriaBuilder.and(colorPredicate, typePredicate, countryPredicate);
};
}
Drink class(Tea extends Drink):
#Inheritance(strategy = InheritanceType.JOINED)
public class Drink {
// Fields
//
private #Id
#GeneratedValue
Long id;
private String name;
private BigDecimal price;
private String about;
#Column(name = "is_deleted")
private boolean isDeleted;
// Relationships
//
#ManyToOne
#JoinColumn(name = "packaging_id")
private Packaging packaging;
#ManyToOne
#JoinColumn(name = "manufacturer_id")
private Manufacturer manufacturer;
#ManyToOne
#JoinColumn(name = "country_id")
private Countries countries;
}
public class Tea extends Drink {
// Relationships
//
#ManyToOne
#JoinColumn(name = "type_id")
private TeaType teaType;
#ManyToOne
#JoinColumn(name = "color_id")
private TeaColor teaColor;
}
public class TeaSpecification {
public static Specification<Tea> getTeasByFilter(Long colorId, Long typeId, Long countryId) {
return (root, query, criteriaBuilder) -> {
Predicate colorPredicate = criteriaBuilder
.equal(root.get(Tea_.teaColor).get(TeaColor_.id), colorId);
Predicate typePredicate = criteriaBuilder
.equal(root.get(Tea_.teaType).get(TeaType_.id), typeId);
Predicate countryPredicate = criteriaBuilder
.equal(root.get(Tea_.countries).get(Countries_.id), countryId);
return criteriaBuilder.and(colorPredicate, typePredicate, countryPredicate);
};
}
}

Unable to save Entity with OneToMany mapping using JPA

I have Order and OrderLineItem with One-to-Many relationship. When I am saving the Order, I am getting ConstraintViolationException. Below is the code snippet.
#Entity
#Table(name = "orders")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Order extends AuditModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
#JsonProperty("order_id")
private long orderId;
private double price;
#OneToMany(mappedBy = "order",
fetch = EAGER,
cascade = CascadeType.ALL )
private Set<OrderLineItem> orderLineItems = new HashSet<>();
// scaffolding code
public void addOrderLineItem(OrderLineItem orderLineItem){
this.orderLineItems.add(orderLineItem);
orderLineItem.setOrder(this);
}
... setters, getters, toString, equals and hashcode methods
}
In OrderLineItem class
#Entity
#Table(name = "order_line_item")
#JsonIgnoreProperties(ignoreUnknown = true)
public class OrderLineItem extends AuditModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private double price;
#ManyToOne()
#JoinColumn(name = "order_id", nullable = false)
#JsonIgnore
private Order order;
public OrderLineItem(String name, double price){
this.name = name;
this.price = price;
}
public OrderLineItem(){}
... setters, getters, toString, equals and hashcode methods
}
OrderServiceImple class
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderRepository orderRepository;
#Override
public Order createOrder(Order order) {
System.out.println("Inside the save method of Order service .... :: ");
System.out.println(order);
return this.orderRepository.save(order);
}
...
}
OrderRepository
#Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findAll();
}
I am using OrderRepository and OrderItemRepository JPA interfaces.
The post request
{
"price": 4500,
"orderLineItems": [
{
"name": "new Order Item",
"price": 4000
}
]
}
Error:
java.sql.SQLIntegrityConstraintViolationException: Column 'order_id' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117) ~[mysql-connector-java-8.0.19.jar:8.0.19]
Where am I going wrong?
JPA didn't find Order inside orderLineItems thats why order_id set as null. To save child with parent in the bidirectional relationship set parent in child entity also to sync both side.
public Order createOrder(Order order) {
for(OrderLineItem orderLineItem : order.getOrderLineItems()) {
orderLineItem.setOrder(order);
}
return this.orderRepository.save(order);
}

Hibernate one-to-many relationship java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null

I'm new to hibernate, learn doc save persistent object
followed hibernate doc this is person and phone relationship one-to-many
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
I'm persistent person and add one phone the error be throw
#Test
public void say() {
Person person = new Person();
person.setUsername("aaaa");
Phone phone = new Phone();
phone.setNumber("111");
person.getPhones().add(phone);
personService.save(person);
}
this is Dao persistent
public class PersonDaoImpl implements PersonDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void save(Person person) {
entityManager.persist(person);
}
Update service code, service just save person
#Service(value = "personService")
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
#Override
public void save(Person person) {
personDao.save(person);
}
}
error info:
23:35:47.059 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
23:35:47.062 [main] DEBUG org.hibernate.SQL -
insert
into
phone
(number, person_id)
values
(?, ?)
23:35:47.297 [main] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null
Add the #GeneratedValue annotation to specify that the primary key for both entities will be populated outside of your code.
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#JoinColumn("person_id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
Additionally, you need to persist the Person object instead of the Phone object because there is no cascade configured from Phone to Person. If you can't do that, switch the CascadeType on Person to none and put the cascade on the Phone as shown above.
You should also add a #JoinColumn annotation on the Phone entity so hibernate is aware of the foreign key column.
You Missed something. You can try with this.
Person Entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
//omit getter and setter
}
Phone Entity
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(cascade = CascadeType.PERSIST)
private Person person;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
//ommit setter and getter
}
Phone Dao
public interface PhoneDao {
public Phone save(Phone phone);
}
PhoneDaoImpl
#Repository
public class PhoneDaoImpl implements PhoneDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Phone save(Phone phone) {
return entityManager.merge(phone);
}
}
PersonDaoImpl
#Repository
public class PersonDaoImpl implements PersonDao{
#PersistenceContext
private EntityManager entityManager;
#Override
public Person save(Person person) {
return entityManager.merge(person);
}
}
Test Method
#Test
#Transactional
#Commit
public void say()
{
Phone phone = new Phone();
phone.setNumber("jghjkhk");
Person person = new Person();
person.setUsername("7576");
phone.setPerson(person);
Phone pers = phoneDao.save(phone);
Assert.assertNotNull(pers);
}
Try now. It will work.
I think that you need to set the value of the person->id and then also use an getter method to pass the id to your phone object instead of passing the person object
Normally people have hibernate set the id of an entity automatically with a surrogate key.
public class Person {
#Id #GeneratedValue // should pick an appropriate strategy here
private long id;
Since you don't have that you must either add it or set it yourself.
Person p = new Person();
p.setId(1); // hopefully unique
The same goes for phone.
As you are not having any generation type on your #Id and id is the primary key which can not be null so either you have to set value of id or have #GeneratedValue annotation on your id field and set strategy either as Auto or Identity.
You can also have your own sequence generation.
Also, you need to do same for the Phone class.

Hibernate. Delete object

I have a relationship between Student entity and Publication as One to Many. I need to delete publication from sutdent. When I try to delete publication object but always get the exception:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.irs.entities.GroupStudent#1]
I do not know why it happens. I use Spring MVC 3.2 and Hibernate.
Student entity
#Entity
#org.hibernate.annotations.DynamicUpdate(value = true)
#Table(name = "Student")
public class Student implements Serializable {
public Student() {}
public Student(String studentFullName, String studentBook,
int studentEnter, String studentOKR) {
this.studentFullName = studentFullName;
this.studentBook = studentBook;
this.studentEnter =studentEnter;
this.studentOKR = studentOKR;
}
// create connectivity with table GroupStudent
private GroupStudent groupStudent;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GroupStudentId")
public GroupStudent getGroupStudent() {
return this.groupStudent;
}
public void setGroupStudent(GroupStudent groupStudent) {
this.groupStudent = groupStudent;
}
// create connectivity with table Publication
private Set<Publication> publications = new HashSet<Publication>();
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Publication> getPublications() {
return publications;
}
public void setPublications(Set<Publication> publications) {
this.publications = publications;
}
// other methods
}
Pulication entity
#Entity
#Table(name = "Publication")
public class Publication implements Serializable {
public Publication() {}
public Publication(String publicationTitle, String publicationType,
String publicationPlace, Date publicationDate) {
this.publicationTitle = publicationTitle;
this.publicationType = publicationType;
this.publicationPlace = publicationPlace;
this.publicationDate = publicationDate;
}
// create connectivity with table Student
private Student student;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "StudentId")
public Student getStudent() {
return this.student;
}
public void setStudent(Student student) {
this.student = student;
}
}
GroupStudent entity
#Entity
#Table(name = "GroupStudent")
public class GroupStudent implements Serializable {
public GroupStudent() {}
public GroupStudent(String groupStudentNumber) {
this.groupStudentNumber = groupStudentNumber;
}
// create connectivity with table Student
private Set<Student> students = new HashSet<Student>();
#OneToMany(mappedBy = "groupStudent", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Student> getStudents() {
return this.students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
Controller
#RequestMapping(value = "/deletePublication.html", method = RequestMethod.GET)
public ModelAndView deletePublication(#RequestParam("studentId") Long studentId) {
....
ps.deletePublication(ps.selectPublicationsById(2L));
....
return modelandview;
}
This error happens when you try to fetch entity that was already in the hibernate context. You can't have two attached entities at the same time.
In your controller you firts call ps.selectPublicationsById(2L), that probably causes the error.
Try to replace the delete logicc with HQL or native SQL delete.
String hql = "delete from Publication where Id= :id";
session.createQuery(hql).setString("id", id).executeUpdate();

Categories