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

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!

Related

How to retrieve data from parent-child tables using Spring Data JPA?

In my Spring Boot app, I use Hibernate and applied the necessary relations to the following entities properly.
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false, length=50)
private String title;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
#Entity
public class RecipeIngredient {
#EmbeddedId
private RecipeIngredientId recipeIngredientId = new RecipeIngredientId();
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#MapsId("recipeId")
#JoinColumn(name = "recipe_id", referencedColumnName = "id")
private Recipe recipe;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#MapsId("ingredientId")
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")
private Ingredient ingredient;
}
#Entity
public class Ingredient
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique=true, nullable=false, length=50)
#EqualsAndHashCode.Include
private String name;
#OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
}
Now I am trying to retrieve data by merging related entities. For example, when retrieving a Recipe, I also need to retrieve all Ingredients belonging to this Recipe.
As far as I know, I can use Projection and maybe it is better to only use Hibernate features and retrieve related table data via Java Stream. I have no idea how should I retrieve data via Hibernate.
Suppose that I just need an Optional<Recipe> that has List<Ingredient>. Then, I probably need a DTO class something like that:
#Data
public class ResponseDTO {
private Long id;
private String title;
List<RecipeIngredient> ingredients;
// getter, setter, constructor
}
So, how should I populate this DTO with the requested Recipe and corresponding Ingredient data (getting Ingredient names besides id values) using Java Stream?
Or if you suggest Projection way, I tried it but the data is multiplied by the ingredient count belonging to the searched recipe.
Update:
#Getter
#Setter
#NoArgsConstructor
public class ResponseDTO {
private Long id;
private String title;
List<IngredientDTO> ingredientDTOList;
public ResponseDTO(Recipe recipe) {
this.id = recipe.getId();
this.title = recipe.getTitle();
this.ingredientDTOList = recipe.getRecipeIngredients().stream()
.map(ri -> new IngredientDTO(ri.getIngredient().getName()))
.toList();
}
}
#Getter
#Setter
public class IngredientDTO {
private Long id;
private String name;
public IngredientDTO(String name) {
this.name = name;
}
}
First, in the ResponseDTO you will need you change the type of ingredients from List<RecipeIngredient> to List<Ingredient>.
To manually perform the mapping, you should use (to map from a suppose Recipe recipe to a RespondeDTO response):
ResponseDTO recipeToResponseDTO(Recipe recipe) {
ResponseDTO response = new ResponseDTO();
response.setId(recipe.getId());
response.setTitle(recipe.getTitle());
response.setIngredients(recipe.recipeIngredients.stream()
.map(RecipeIngredient::getIngredient()
.collect(Collectors.toList());
return response;
}
On the other hand, to model a n-n relation, I encourage you to use the approach proposed by E-Riz in the comment.

JPA StackOverflow -- Many to Many and one to one mapping

I am trying to save a JPA entity which has ManytoMany Relationship (Consumer and Product table) and OnetoOne relation with ConsumerDetailstable.Below are my entities
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity
public class Consumer {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
#JsonManagedReference
#OnToMany(mappedBy = "consumer")
private Set<ConsumerProduct> consumerProducts;
#OneToOne
private CustomerDetails consumerDetails;
}
#Entity
public class Product {
#Id
#GeneratedValue
private Long productId;
private String productCode;
#OneToMany(mappedBy = "product")
private Set<ConsumerProduct> consumerProducts;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity(the join table)
public class ConsumerProduct {
#EmbeddedId
ConsumerProductKey id;
#JsonBackReference
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Consumer consumer;
#ManyToOne
#MapsId("productId")
#JoinColumn(name = "product_id")
private Product product;
}
#Embeddable (forgein keys combined as embeded id)
public class ConsumerProductKey implements Serializable {
#Column(name="id")
private Long id;
#Column(name = "product_id")
private Long productId;
}
#Enitity (one to one relation table)
public class CustomerDetails {
#Id
#GeneratedValue
private Long consumerDtlId;
#OneToOne
private Consumer consumer;
private String city;
private String state;
private String country;
}
To save the entity am have just extended JPARepository and called save method
public class ConsumerRepository<Consumer> Implements JPARepository<Consumer, Long> {
#Override
public Consumer S save(Consumer entity) {
return save(entity);
};
}
I get java.lang.StackOverFlowError at save method.
Anything wrong with my Mappings ?
Question: Since this will be save operation and since Consumer Id is yet to be generated how do I assign to below Entities
ConsumerProduct.ConsumerProductKey (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
CustomerDetails (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
EDIT: I have updated the entity with JsonManagedReference and JsonBackedReference but still i have am facing stackoverflow error
It is due to Consumer trying to access ConsumerProduct and ConsumerProduct trying to access consumer entity and end up with StackOverflow error.
You should use #JsonManagedReference and #JsonBackReference annotation in consumer and ConsumerProduct respectivly.

JPA: Deletion with Foreign Keys in ManyToMany and ManyToOne relations?

I am using spring boot to model my backend and I have several foreign keys in my model structure.
I am unable to run delete operations in general and update operations when foreign keys are affected. I have tried several methods found on the internet but none seem to work.
Via SQL (SQL commands) can delete entities from book_queue_entry, copy and author_write but not author, book and book_store user due to foreign key constraints. Therefore I'd like to know what I am doing wrong in each of my models/relationships that don't work on SQL level.
Since it does not work on SQL level something with my modelling, at least as far as configuring on delete operations is fundamentally wrong. I can't point my finger at what.
Problem 1: Cannot delete a book: When a book is deleted, it should be deleted from the list of works of an author - in other words, it should be deleted from authors_write:
Author.java:
#Entity
#Setter
#Getter
#NoArgsConstructor
public class Author extends BaseEntity implements Serializable {
//stuff
#ManyToMany(cascade = CascadeType.ALL)
#JsonIgnore
#JoinTable(
name = "authors_write",
joinColumns = #JoinColumn(name = "author_id"),
inverseJoinColumns = #JoinColumn(name = "book_id"))
Set<Book> works;
}
Book.java
#Entity
#Setter
#Getter
#NoArgsConstructor
public class Book extends BaseEntity implements Serializable {
//stuff
#ManyToMany(mappedBy = "works", cascade = CascadeType.ALL)
#EqualsAndHashCode.Exclude
Set<Author> authors;
#JsonIgnore
#OneToMany(mappedBy = "book")
private List<BookQueueEntry> bookQueue;
}
Problem 2: Cannot delete Authors. When an author is deleted then all entries in authors_write with the corresponding author_id should be deleted. The classes are the same as above. Cascading does not work in this case either.
Problem 3: Cannot delete Users. When users are deleted borrower_id in copy should be nulled (I've read this does not work in JPA at all) and book_queue_entries with corresponding user_id should be deleted
Copy.java
#Entity
#Setter
#Getter
#NoArgsConstructor
public class Copy extends BaseEntity implements Serializable {
//stuff
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn
#EqualsAndHashCode.Exclude
private Book reference;
#ManyToOne
#JoinColumn
#EqualsAndHashCode.Exclude
private BookStoreUser borrower;
}
User.java:
public class BookStoreUser extends BaseEntity implements Serializable {
//more stuff here
#JsonIgnore
#OneToMany(mappedBy = "borrower")
private Set<Copy> booksBorrowed;
}
BookQueueEntry.java
#Entity
#Getter
#Setter
#NoArgsConstructor
#EqualsAndHashCode
public class BookQueueEntry extends BaseEntity implements Serializable {
//more stuff here
#ManyToOne(cascade = CascadeType.ALL)
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "user_id", nullable = false)
#EqualsAndHashCode.Exclude
private BookStoreUser user;
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "book_id", nullable = false)
#EqualsAndHashCode.Exclude
private Book book;
}
Once again, I believe something is wrong with how I've mapped the cascading operations. Something is wrong in a more general sense and I cant figure out what.
EDIT: I should note that #OnDelete(action = OnDeleteAction.CASCADE) did not work for me either.
EDIT 2: With a clear head, I've been able to fix Problems 1 and 2 using this: https://www.youtube.com/watch?v=vYNdjtf7iAQ
As noted in the comments, follow this video: https://www.youtube.com/watch?v=vYNdjtf7iAQ
My problems are solved but when deleting Users it throws a ConcurrentModificationException which is not really related to this problem.
My classes now look like this:
Book.java
public class Book extends BaseEntity implements Serializable {
#Column(unique = true)
private String isbn;
#NotBlank
private String title;
private Integer year;
private String imageUrl;
#ManyToMany(mappedBy = "works")
#EqualsAndHashCode.Exclude
Set<Author> authors;
private Boolean isAvailable;
private Integer version;
#Enumerated(EnumType.STRING)
private Language language;
#JsonIgnore
#OneToMany(mappedBy = "reference", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<Copy> copies;
#JsonIgnore
#OneToMany(mappedBy = "book", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<BookQueueEntry> bookQueue;
// see here why: https://www.youtube.com/watch?v=vYNdjtf7iAQ
public void addAuthor(Author author) {
this.authors.add(author);
author.getWorks().add(this);
}
public void removeAuthor(Author author) {
this.authors.remove(author);
author.getWorks().remove(this);
}
}
Author.java:
public class Author extends BaseEntity implements Serializable {
private String name;
#ManyToMany
#JsonIgnore
#JoinTable(
name = "authors_write",
joinColumns = #JoinColumn(name = "author_id"),
inverseJoinColumns = #JoinColumn(name = "book_id"))
Set<Book> works;
public void addBook(Book book) {
this.works.add(book);
book.addAuthor(this);
}
public void removeBook(Book book) {
this.works.remove(book);
book.removeAuthor(this);
}
}
Copy.java:
public class Copy extends BaseEntity implements Serializable {
#ManyToOne
#JoinColumn
#EqualsAndHashCode.Exclude
private Book reference;
private LocalDate borrowedAt;
private LocalDate dueDate;
#ManyToOne
#JoinColumn
#EqualsAndHashCode.Exclude
private BookStoreUser borrower;
#NotNull
#Enumerated(EnumType.STRING)
private Location location;
public void addBorrower(BookStoreUser user) {
user.addBorrowedCopy(this);
}
public void removeBorrower(BookStoreUser user) {
user.removeBorrowedCopy(this);
}
}
BookStoreUser.java
public class BookStoreUser extends BaseEntity implements Serializable {
#NotNull
#Column(unique = true)
private String email;
private String firstName;
#NotNull
private String lastName;
private boolean isAdmin;
#JsonIgnore
#OneToMany(mappedBy = "borrower", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Copy> booksBorrowed;
#NotNull
private String password;
public void addBorrowedCopy(Copy copy) {
this.booksBorrowed.add(copy);
copy.setBorrower(this);
copy.setBorrowedAt(LocalDate.now());
copy.setDueDate(LocalDate.now().plusMonths(1));
}
public void removeBorrowedCopy(Copy copy) {
this.booksBorrowed.remove(copy);
copy.setBorrower(null);
copy.setBorrowedAt(null);
copy.setDueDate(null);
}
}
And also make sure to make use of the utility methods in the service layer like this:
public Long deleteById(Long id) {
this.validator.checkIDNotNull(id);
Book book = em.find(Book.class, id);
for (Author author : book.getAuthors()) {
book.removeAuthor(author);
}
bookRepository.deleteById(id);
validator.checkEntityNotExists(id);
return id;
}
public Book create(Book book) {
this.checkISBNIsValid(book.getIsbn());
this.checkISBNExists(book.getIsbn());
this.checkEntityHasValues(book);
for (Author author : book.getAuthors()) {
author.addBook(book);
}
log.info("Book with ISBN {} created successfully", book.getId());
return bookRepository.save(book);
}
EDIT: ConcurrentModificationException: Deleting from a list/Set will throw this problem. This can be found on SO - to fix ConcurrentModificationExceptions you have to create a new HashSet with whatever is throwing that problem and iterate through that:
new HashSet<Author>(book.getAuthors())
.forEach(author -> book.removeAuthor(author));

Hibernate delete row and foreign key row ManyToOne

I have the following two classes, one ReqCandAssociation can have many Comments and it is mapped like so. I need to figure out a way that when I delete a ReqCandAssociation it deletes all of its associated comments. Thanks
#Entity
#Table(name = "candidate_jobReq")
public class ReqCandAssociation implements Serializable {
#Id
private Integer candidateId;
#Id
private Integer jobId;
#Column(name = "reqStatus")
private String reqStatus;
#ManyToOne
#PrimaryKeyJoinColumn(name="candidateId", referencedColumnName="id")
private Candidate candidate;
#ManyToOne
#PrimaryKeyJoinColumn(name="jobId", referencedColumnName="id")
private JobReq jobReq;
public ReqCandAssociation(){
}
Second class
#Entity
#Table(name="comment")
public class Comment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="commentText")
private String commentText;
#Column(name="commentDate")
private Date commentDate;
#ManyToOne
#PrimaryKeyJoinColumn(name="reqCandAssociationId", referencedColumnName="id")
private ReqCandAssociation reqCandAssociation;
#ManyToOne
#PrimaryKeyJoinColumn(name="userId", referencedColumnName="id")
private User user;
Change this to the following, i'm making it bidirectional mapping.
#Entity
#Table(name = "candidate_jobReq")
public class ReqCandAssociation implements Serializable {
#Id
private Integer candidateId;
#Id
private Integer jobId;
#Column(name = "reqStatus")
private String reqStatus;
#OneToMany(cascade = { CascadeType.ALL }) //this is added here.
#JoinColumn(name ="reqCandAssociationId")
private Set<Comment> comments;
-----
Readup more on the cascade options. All cascade types are all|none|save-update|delete|all-delete-orphan|delete-orphan
The cascade all will delete all the comments associated to this class.

one to many for same entity class in play framework

Good day!
I am triing to save my Entity model by always get an error
Error inserting bean [class models.CategoryEntity] with unidirectional relationship. For inserts you must use cascade save on the master bean [class models.CategoryEntity].]
Here my class
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String Name;
private Integer level;
#OneToMany(targetEntity = CategoryEntity.class, cascade = CascadeType.ALL)
private List<CategoryEntity> categories;
//GETERS SETRES
}
I tried to save the header category, but error is the same
If I understand the question correctly and what you want is that each CategoryEntity contains a list of other CategoryEntities, 2 possible approaches come to mind (although none of them use #OneToMany):
Approach 1:
You could create a #ManyToMany relationship and define a #JoinTable whilst naming its keys:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable( name = "category_category",
joinColumns = #JoinColumn(name = "source_category_id"),
inverseJoinColumns = #JoinColumn(name = "target_category_id"))
public List<CategoryEntity> category_entity_lists = new ArrayList<CategoryEntity>();
}
Approach 2:
Or you could create a new entity for the list of category entities and create a #ManyToMany relationship, e.g.:
#Entity
public class CategoryList extends Model
{
#Id
public Long id;
#ManyToMany
#JoinTable(name="categorylist_category")
public List<CategoryEntity> category_list = new ArrayList<CategoryEntity>();
}
And then in your model:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#OneToOne
public CategoryList this_category_list;
#ManyToMany(mappedBy="category_list")
public List<CategoryList> in_other_category_lists = new ArrayList<CategoryList>();
}
Didn't test the code, but what this should do is that each CategoryEntity can be part of several CategoryLists. Each CategoryList contains a list of CategoryEntities.
You would have to initialize this_category_list and add CategoryEntities to its category_list field.
Your model doesnt make sense.
why do you have as class attribute the class itself?
the List should not be in the same class

Categories