I want to search book(Many) entity from library(One) entity. (A->B, B->A) - spring

It's so working well when I was trying to search "nice_library" from "book". I guess because the nice_library FK is saved on the book.
book = bookRepository.findById(book.getId()).orElseThrow(() -> new RuntimeException());
Long id = book.getNiceLibrary().getId();
assertEquals(id, niceLibrary.getId());
but I can't get anything when I tried to search "book" from "nice_library". I guess because nice_library doesn't have FK of book
niceLibrary = niceLibraryRepository.findById(niceLibrary.getId()).orElseThrow(() -> new RuntimeException());
List<Book> books = niceLibrary.getBooks(); // WHAT THE
I was expecting the "Book.id" to be saved on the "nice_library" table for search to "book" from "nice_library".
Book.java
package com.example.jpa;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
#Getter
#Entity
public class Book {
#Id
#GeneratedValue
#Column
private Long id;
#Setter
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "LIBRARY_ID") // Foreign Key
private NiceLibrary niceLibrary;
}
NiceLibrary.java
package com.example.jpa;
import lombok.Getter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Getter
#Entity
public class NiceLibrary {
#Id
#GeneratedValue
#Column
private Long id;
#OneToMany(mappedBy = "niceLibrary")
private List<Book> books = new ArrayList<>();
}
book table
id|library_id|
--+----------+
77| 78|
nice_library
id|
--+
78|
Test Code
#SpringBootTest
class BookServiceTest {
#Autowired
private BookRepository bookRepository;
#Autowired
private NiceLibraryRepository niceLibraryRepository;
#Test
public void book() {
Book book = new Book();
NiceLibrary niceLibrary = new NiceLibrary();
niceLibrary.getBooks().add(book);
book.setNiceLibrary(niceLibrary);
bookRepository.save(book);
book = bookRepository.findById(book.getId()).orElseThrow(() -> new RuntimeException());
Long id = book.getNiceLibrary().getId();
assertEquals(id, niceLibrary.getId());
niceLibrary = niceLibraryRepository.findById(niceLibrary.getId()).orElseThrow(() -> new RuntimeException());
List<Book> books = niceLibrary.getBooks(); // ???
}
How can I search book from nice_library?

From what I understand you want to be able to get a specific book from the library.
The one-to-many relationship is setup by having a reference to the library in the book table.
There is no reason to store the book's id in the library table.
This is all you need to setup a one-to-many relationship and you have this correct.
+-----------------+
| Book |
+-----------------+
| id |
| LIBRARY_ID (fk) |
+-----------------+
+------------+
| Library |
+------------+
| id |
+------------+
The way you are describing the problem is that you are expecting a library to only have one book, which is about as big as my library.
In a one-to-many relationship the one side (library in this case) will always have a collection of entities and the other side (book in this case) will only have a reference to a single entity.

Related

why employee_id and department_id not update automatic in database Hibernate jpa

few days back i starting learning hibernate JPA but i am unable to find solution of given problem below
My Project consist three class employee ,phone ,department by seeing code you can easily understand what i am doing .
Main problem raise when i try to save this data into database using spring boot controller it showing null value in column . In employee table department_id is null(not automatic update using cascade.All) same in phone table employee_id is null.
I do not want update manually .is their any way so dep_id and emp_id automatic update to foreign key table .
{
"name":"CSE",
"employees":[
{
"name":"Welcome",
"age":23,
"phones":[{"number":1234567890},{"number":1234567890}]
},
{
"name":"back",
"age":25,
"phones":[{"number":1234567890},{"number":1234567890}]
}
]
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int age;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
#OneToMany(mappedBy = "employee", cascade=CascadeType.ALL)
private List<Phone> phones;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*
#Entity
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String number;
#ManyToOne
#JoinColumn(name = "employee_id")
private Employee employee;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
}
dept table in database
id name
1 CSE
employee table
id name age department_id
1. welcome 23. null
2. back. 25. null
phone table
id number employee_id
1. 1234567890. null
2. 1234567890. null
3. 1234567890 null
4. 1234567890. null
Why employee_id and department_id not updating automatic in cascade All
Controller class
package com.example.controller;
import com.example.dao.DepRepo;
import com.example.dao.EmployeeRepo;
import com.example.dao.PhoneRepo;
import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.service.FakeService;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Inject;
#Controller("/dummy")
public class DummyController {
#Inject
FakeService fakeService;
#Inject
PhoneRepo phoneRepo;
#Inject
EmployeeRepo employeeRepo;
#Inject
DepRepo depRepo;
#Get ("/")
public String fun(){
fakeService.fun();
return "welcome back";
}
#Post("/add")
public HttpResponse<?> fun(#Body Department dep){
System.out.println(dep);
depRepo.save(dep);
return HttpResponse.status(HttpStatus.ACCEPTED).body("data add successfully");
}
}
Your Hibernate mapping says that the relationships are mapped by the 'many' side of the association:
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
So Hibernate looks for the value of the 'department' in the employee entity and it is null (because there's no value in the JSON data)
So try removing mappedBy to tell Hibernate that the relationship is mapped on the 'one' side

Entity Design using JPA

I have 3 entities -
Course
Module
Timeline
Course is an independent entity with following attributes:
Course - (id Integer Primary Key, course_name)
#Id
#Column(name = "id")
Integer courseId;
#Column(name = "course_name")
String course_name;
Next up is another entity Module,
Every row in module is related to one course, and hence there is a one to one relationship between Module and Course.
Module - (module_id, module_name, module_type, duration)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "module_id")
Integer module_id;
#Column(name = "module_name")
String module_name;
#Column(name = "duration")
Integer duration;
#ManyToOne
#JoinColumn(name="timeline_id", nullable=false)
private Timeline timeline;
Now, next is a timeline entity, which is also related to course i.e every timeline id belongs to one course id, but one timeline id can belong to multiple module_ids, and hence below code:
#Id
#Column(name = "timeline_id")
Integer timelineId;
#OneToMany( mappedBy = "timeline" )
private List<Module> module;
#OneToOne( cascade = CascadeType.ALL)
private Course course;
Can you please tell me what is the error over here.
ModuleRepository:
#Repository
public interface ModuleRepository extends JpaRepository<Module, Integer>{
public List<Module> findAllByTimelineTimelineId(Integer timelineId);
}
IModuleService
public interface IModuleService {
public List<Module> findByTimelineId(Integer timelineId);
}
ModuleServiceImpl
public List<Module> findByTimelineId(Integer timelineId) {
// TODO Auto-generated method stub
return moduleRepo.findAllByTimelineTimelineId(timelineId);
}
Controller
#RequestMapping("/gettimeline/{timeline_id}")
public List<Module> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id);
}
Now when I run this url in Postman: http://localhost:8083/gettimeline/1
I get an infinite loop, I am unable to decode the error, also is there any problem with OneToMany mapping, I am new to JPA:
[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[
Please help, thank you in advance :)
The infinite loop issue is caused by the one-to-many relation. There are several ways of fixing this, but I find view model classes like shown below as the cleanest approach.
Please note that the owning side of the one-to-many relation is not included in the code below, only the many-to-one. This can be done the other way around, but from your code, I guess this is what you want.
TimelineVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class TimelineVM {
private Integer timelineId;
public TimelineVM(Timeline timeline) {
this.timelineId = timeline.getTimelineId();
}
}
ModuleVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class ModuleVM {
private Integer module_id;
private String module_name;
private Integer duration;
private TimelineVM timeline;
public ModuleVM(Module module) {
this.module_id = module.getModule_id();
this.module_name = module.getModule_name();
this.duration = module.getDuration();
this.timeline = new TimelineVM(module.getTimeline());
}
}
Controller method
#RequestMapping("/gettimeline/{timeline_id}")
public List<ModuleVM> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id).stream().map(ModuleVM::new).collect(Collectors.toList());
}

How deal with child entites with unique attributes in JPA+Hibernate+Spring?

Saving a many-to-one association only works the first time.
How save father with appended child if child entries already exist and prevent the "Unique index or primary key violated" error message ?
Note: that the name attribute is unique!
The main program.
// This works
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
Model model1 = new Model();
Model model2 = new Model();
model1.setName("911");
model2.setName("Vito");
car1.setLicense("S-TU-123");
car2.setLicense("S-GH-124");
car3.setLicense("S-BN-123");
car1.setModel(model1);
car2.setModel(model2);
car3.setModel(model1);
carRepository.saveAll(Arrays.asList(car1, car2, car3));
//// wont work because of "Unique index or primary key violated"
Car car4 = new Car();
Model model4 = new Model();
car4.setModel(model4);
model4.setName("911"); // <== exists in database already
car4.setLicense("L-QQ-001");
carService.save(car4);
model code:
import javax.persistence.*;
import lombok.*;
#Entity
#Table(name = "cars")
#Getter
#Setter
#EqualsAndHashCode
#NoArgsConstructor
public class Car {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String license;
#EqualsAndHashCode.Exclude
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "model_id", referencedColumnName = "id")
private Model model;
public void setModel(Model model) {
this.model = model;
model.getCars().add(this);
}
public void removeModel(Model model) {
this.model = model;
model.getCars().add(null);
}
}
car code:
import javax.persistence.*;
import lombok.*;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table(name = "models")
#Getter
#Setter
#EqualsAndHashCode
#NoArgsConstructor
public class Model {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String name;
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "model", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Car> cars = new HashSet<>();
public void setCars(Set<Car> cars) {
this.cars = cars;
for (Car car : cars) {
car.setModel(this);
}
}
}
car repository:
public interface CarRepository extends JpaRepository<Car, Long> {
}
model repository:
public interface ModelRepository extends JpaRepository<Model, Long> {
Optional<Model> findByLicense(String license);
}

OneToMany bidirectional relationship JoinColumn value is null in Spring Data JPA

I have OneToMany bidirectional mapping for two entities Cart and CartProduct. Whenever we insert a Cart object with cart products, CartProduct table should fill with cart_id. Here is the problem, when I insert cart object, everything seems to be fine except, JoinColumn(card_id) which results in a null value in CartProduct table. Am I doing this right?
Cart.Java
package com.springtesting.model.cart;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.user.UserProfile;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#EqualsAndHashCode(callSuper = true)
#Entity
#Data
#Table(name = "cart")
public class Cart extends AbstractAuditingEntity
{
private static final long serialVersionUID = 6294902210705780249L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "user_profile_id")
#JsonIgnoreProperties(value = {"addresses"})
private UserProfile userProfile;
#ManyToOne
#JoinColumn(name = "cart_status")
private CartStatus cartStatus;
#OneToMany(mappedBy = "cart", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
//#ElementCollection(targetClass = CartProduct.class)
private List<CartProduct> cartProducts=new ArrayList<>();
}
CartProduct.Java
package com.springtesting.model.cart;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.product.Product;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
#EqualsAndHashCode(callSuper = true)
#Entity
#Data
#Table(name = "cart_product")
public class CartProduct extends AbstractAuditingEntity
{
private static final long serialVersionUID = 6498067041321289048L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "product_id")
private Product product;
#Column(name = "quantity")
private Integer quantity;
#ManyToOne
#JoinColumn(name = "cart_id",referencedColumnName = "id")
#JsonIgnoreProperties(value = {"userProfile","cartStatus","cartProducts"})
private Cart cart;
}
TestCase.java
#Test
public void insertCart()
{
Cart cart=new Cart();
cart.setUserProfile(userProfileRepository.findAllByUserId(1L).get());
cart.setCartStatus(cartStatusRepository.findById(1L).get());
List<CartProduct> cartProducts=new ArrayList<>();
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProducts.add(cartProduct);
cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(2L).get());
cartProduct.setQuantity(1);
cartProducts.add(cartProduct);
cart.setCartProducts(cartProducts);
cartRepository.saveAndFlush(cart);
}
Yes, your fix is the addition of cartProduct.setCart(cart); This is because the CartProduct is the owning entity and is the keeper of the foreignKey. The above statement sets the FK.
The way to think about this is the concept of owning entity. When you have mappedBy="cart" you are saying that the CartProduct class owns the relationship. This means that only the CartProduct class is doing the persisting. This tells JPA to create a FK in the CartProduct table. However, we notice that save is not being called on CartProduct but rather on Cart and yet cartProducts is still being saved. This is because you have the cascade = CascadeType.ALL annotation. This tells JPA to cascade certain operations when they are done to Cart, in this case the save operation.
You should have SQL statements printed and examine the differences with different configurations and test cases. This will help you understand better.
You also have FetchType.EAGER. This is generally a bad habit and usually leads to endless problems.
A good way to think about a bidirectional mapping is that the List<CartProducts> cartProducts is a query only field. In order to save a CartProduct you would call save on the cartProductRepository directly. E.g.
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProduct.setCart(cart);
cartProductRepository.save(cartProduct);
and then
cart.getCartProducts().add(cartProduct);
and remove all the cascade and eager fetch annotations. When hibernate says that you must management both sides of the relationship this is what is meant.
Doing it this way will result in one query for the save. By using a cascade annotation you will find that as you add items to the cart and call save on it the sql generated will first delete all the existing cartProducts items from the database and re-add them along with the new one every time you call save. For a cart with 10 items instead of a single save you will have a delete and 10 new saves. Definitely less desirable. If you have to reload the cart from scratch the most efficient method is to get the cart and then cart.setCartProducts(cartProductRepository.findAllByCart(cart)); which is what FetchType.EAGER is doing anyway. When you understand all this then you realize that you don't need a = new ArrayList<>(); for your cartProducts.
I think I figured out the solution. Based Hibernate docs
Whenever a bidirectional association is formed, the application
developer must make sure both sides are in-sync at all times.
So I manually added the cart object to cartProduct object, which saves cart_id in CartProduct table
CartController.java
import com.pj.springsecurity.model.cart.Cart;
import com.pj.springsecurity.model.cart.CartProduct;
import com.pj.springsecurity.repo.CartRepository;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
#RestController
#RequestMapping("/api/v1/cart")
public class CartController
{
private final CartRepository cartRepository;
public CartController(CartRepository cartRepository)
{
this.cartRepository = cartRepository;
}
#GetMapping(path = "/list")
public List<Cart> getAllCarts()
{
return cartRepository.findAll();
}
#GetMapping(path = "/find/user/{id}")
public Optional<Cart> getCartBasedOnUserId(#PathVariable Long id)
{
return cartRepository.findAllByUserProfileUserId(id);
}
#PostMapping(path = "/product/add")
public Cart addProductToCart(#RequestBody Cart cart)
{
List<CartProduct> cartProducts=cart.getCartProducts();
for(CartProduct cartProduct: cartProducts)
{
cartProduct.setCart(cart);
}
return cartRepository.saveAndFlush(cart);
}
#PutMapping(path = "/update")
public Cart updateCart(#RequestBody Cart cart)
{
return cartRepository.saveAndFlush(cart);
}
#DeleteMapping(path = "/delete")
public Cart createEmptyCart(#RequestBody Cart cart)
{
return cartRepository.saveAndFlush(cart);
}
#DeleteMapping(path = "/product/delete")
public void deleteProductFromCart(#RequestBody Cart cart)
{
List<CartProduct> cartProducts=cart.getCartProducts();
for(CartProduct cartProduct: cartProducts)
{
cartProduct.setCart(null);
}
cartRepository.delete(cart);
}
}
and Test case updated with the same
#Test
public void insertCart()
{
Cart cart=new Cart();
cart.setUserProfile(userProfileRepository.findAllByUserId(1L).get());
cart.setCartStatus(cartStatusRepository.findById(1L).get());
List<CartProduct> cartProducts=new ArrayList<>();
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProduct.setCart(cart);
cartProducts.add(cartProduct);
cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(2L).get());
cartProduct.setQuantity(1);
cartProduct.setCart(cart);
cartProducts.add(cartProduct);
cart.setCartProducts(cartProducts);
cartRepository.saveAndFlush(cart);
}

hibernate #ManyToOne no table relationship

Currently Using:
Hibernate 4.0.1.Final
Spring-data-jpa: 1.0.3.RELEASE
QueryDSL: 2.3.0
MySQL 5.x
I have an interesting problem that I have not found the answer, or clue for yet. I have two tables that did not have foreign key or other relationship. But to try and solve this issue I added one. I want my User entity to hold it's UserRole. This pattern is repeated throughout the database, but this is the easiest to describe.
Here are my tables:
User
userId bigint(20) PK
password varchar(255)
status int(11)
userName varchar(255)
userRoleId long
CONSTRAINT `FK_USERROLE` FOREIGN KEY (`userRoleId`) REFERENCES `UserRole` (`userRoleId`)
UserRole
userRoleId bigint(20) PK
userRoleDescription varchar(255)
userRoleDescriptionShort varchar(255)
Here are my classes:
User.java
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#XmlRootElement(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
private String password;
private int status;
private String userName;
#ManyToOne
#JoinColumn(name = "userRoleId")
private UserRole userRole;
public UserRole getUserRole() {
return userRole;
}
public void setUserRole(UserRole userRole) {
this.userRole = userRole;
}
UserRole.java
#Entity
#XmlRootElement(name = "userRole")
public class UserRole {
private Long userRoleId;
private String userRoleDescription;
private String userRoleDescriptionShort;
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
public UserRole() {...}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getUserRoleId() {... }
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
So you can see where I am trying to associate the UserRole.userRoleId with the User. I thought perhaps Hibernate would build the mapping and retrieve/associate the UserRole whenever the User was updated.
I have gone back and edited this post to use a foreign key between the tables, but on app server startup I get this:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: UserRole, for columns: [org.hibernate.mapping.Column(users)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:304)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:288)
at org.hibernate.mapping.Property.isValid(Property.java:216)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:467)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
I looked for that error, which appears related to JPA and transient variables, which is not the case here.
If i were you i would first clean the annotations, it is forbidden to annotate BOTH getter AND fields in the same entity, it could end up in unexpected results ...
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
should be simplified in :
#ElementCollection
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}

Resources