I have 2 entities - Book and Publisher which have a many-to-many relationship. Besides the ids of these entities in the mapping entity, I want to store additional attribute such as bookFormat. So I created a mapping entity BookPublisher and created a composite key entity BookPublisherId. Now, when I want to persist the data using JSON data from my rest controller, I am not able to persist the data correctly. In my JSON data, I am passing the following:
{
"name": "Book14",
"authors": [
{
"firstName": "book14FirstName",
"lastName": "book14LastName"
}
],
"publishers": [
{
"publisher": {
"name": "Pearson1"
},
"format": "Paperback"
}
]
}
Here's the Book entity:
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
private String name;
#ManyToMany(cascade = CascadeType.ALL)
#JsonBackReference
private Set<Author> authors;
#OneToMany(mappedBy = "book")
private List<Review> reviews;
#OneToMany(mappedBy = "book")
private Set<BookPublisher> publishers;
public Book() {
}
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Review> getReviews() {
return reviews;
}
public void setReviews(List<Review> reviews) {
this.reviews = reviews;
}
public Set<Author> getAuthors() {
return authors;
}
public void setAuthors(Set<Author> authors) {
this.authors = authors;
}
public Set<BookPublisher> getPublishers() {
return publishers;
}
public void setPublishers(Set<BookPublisher> publishers) {
this.publishers = publishers;
}
}
Here's the Publisher entity:
#Entity
public class Publisher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#OneToMany(mappedBy = "publisher")
private Set<BookPublisher> publishedBooks;
public Publisher() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<BookPublisher> getPublishedBooks() {
return publishedBooks;
}
public void setPublishedBooks(Set<BookPublisher> publishedBooks) {
this.publishedBooks = publishedBooks;
}
}
Here's the BookPublisher entity:
#Entity
public class BookPublisher {
#EmbeddedId
private BookPublisherId bookPublisherId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("bookId")
private Book book;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("publisherId")
private Publisher publisher;
private String format;
public BookPublisher() {
}
public BookPublisher(Book book, Publisher publisher, String format) {
this.book = book;
this.publisher = publisher;
this.format = format;
}
public BookPublisherId getBookPublisherId() {
return bookPublisherId;
}
public void setBookPublisherId(BookPublisherId bookPublisherId) {
this.bookPublisherId = bookPublisherId;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
Here's the code where I am trying to save the Book entity I receive in the rest controller:
public Book addBook(Book book) {
Set<BookPublisher> toAddPublishers = new HashSet<>();
for(BookPublisher publisher : publishers) {
Publisher addedPublisher = publisherRepository.save(publisher.getPublisher());
BookPublisher toAddBookPublisher = bookPublisherRepository.save(new BookPublisher(book, addedPublisher, publisher.getFormat()));
toAddPublishers.add(toAddBookPublisher);
}
book.setPublishers(toAddPublishers);
Book addedBook = bookRepository.save(book);
return addedBook;
}
When I try to call this method, I see that the BookPublisher entity is not being persisted in the database. What is the correct way to add such data?
I can not say anything definite by the looks of your code but is your addBook inside a transactional scope? Also, why have you not set any cascades for your relationships?
Related
I Have a rest controller that is not de-serializing the array type in json..
#PostMapping()
#ResponseBody
public ResponseEntity<Team> createteam(#RequestBody Team team) throws JsonProcessingException {
Team savedTeam = teamService.createTeam(team);
return new ResponseEntity<Team>(savedTeam, HttpStatus.CREATED);
}
below is my entity class.
#Entity
#JsonIdentityInfo(generator = IntSequenceGenerator.class)
public class Team {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Developer> developers;
public Team(String name, List<Developer> developer) {
super();
this.name = name;
this.developers = developer;
}
public Team() {
super();
}
public List<Developer> getDeveloper() {
return developers;
}
public void setDeveloper(List<Developer> developer) {
this.developers = developer;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and my other entity..
package com.demo.springbootdemo.entity;
#Entity
#JsonIdentityInfo(generator = IntSequenceGenerator.class)
public class Developer {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Team team;
private Long phone;
private String name;
public Developer() {
super();
}
public Developer(Team team, Long phone, String name) {
super();
this.team = team;
this.phone = phone;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public Long getPhone() {
return phone;
}
public void setPhone(Long phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
below is my JSON payload, which returns null "developers" when i call the post method.i have tried changing the number of properties in json payload but, still i am not able to figure out why my json is not de-serilaized to List of developers..
{
"id": 1004,
"name": "claim",
"developers": [
{
"id" :1,
"phone": 9092123,
"name": "raina"
}
]
}
I am not sure what Deserializer are you using, but with the Jackson ObjectMapper I solved it changing the method names of the getter and setter for the developers properties: they should be called setDevelopers and getDevelopers. In your code they are called setDeveloper and getDeveloper, without the final S.
To avoid problem like these, I just add Lombok as a dependency and it takes care of creating setters and getters.
With Lombok your Team class would look like this:
// ... more imports here...
import lombok.Data;
#Data
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class Team {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Developer> developers;
}
You may need to add more Lombok annotations for generating constructor methods according to your needs.
I populated the cart and cartitem classes using this json request -
{
"userId": "1246772",
"cartItemRequests": [
{
"itemId": "12345",
"quantity": 5
}
],
"discount": {
"promoCode": "MYPROMO",
"discountPercentage": 0
}
}
I am seeing this error in the response - javax.persistence.EntityNotFoundException: Unable to find cart.model.CartItem with id d3931cb8-ab81-4c1b-b722-841c7fcf2f77.
When I debugged the code , I was able to see "d3931cb8-ab81-4c1b-b722-841c7fcf2f77" the cartItem list present in cart. However when I save using Spring Data JPA , I see the above error
#Entity
public class CartItem {
#Id
private String id;
private String itemId;
private int quantity;
#ManyToOne
private Cart cart;
public CartItem() {
this.id = UUID.randomUUID().toString();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getItemId() {
return itemId;
}
public void setItemId(String itemId) {
this.itemId = itemId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
}
#Entity
public class Cart {
#Id
private String id;
private String userId;
#OneToMany(fetch = FetchType.LAZY)
private List<CartItem> cartItems;
#Embedded
private Discount discount;
public Cart() {
this.id = UUID.randomUUID().toString();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public List<CartItem> getCartItems() {
return cartItems;
}
public void setCartItems(List<CartItem> cartItems) {
this.cartItems = cartItems;
}
public Discount getDiscount() {
return discount;
}
public void setDiscount(Discount discount) {
this.discount = discount;
}
}
can you update your code with below one n check:---
#OneToMany(mappedBy = "Cart", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<CartItem> cartItems;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Cart cart;
When I send a GET request in POSTMAN to get all my child entity (Town) the parent entity (Province) is not shown in the JSON response.
This is my controller.
#RequestMapping(value ="api/v1/town",method = RequestMethod.GET)
public ResponseEntity<List<Town>> getAllTowns() {
List<Town> towns = townService.getAllTowns();
if(towns.isEmpty()) {
return new ResponseEntity<List<Town>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<Town>>(towns, HttpStatus.OK);
}
And these are my entities.
Parent Class
#Entity
#Table(name = "PROVINCE")
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PROVINCE_ID")
private long id;
private String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "province", targetEntity = Town.class)
#JsonManagedReference("Province-Town")
private List<Town> towns;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Town> getTowns() {
return towns;
}
public void setTowns(List<Town> towns) {
this.towns = towns;
}
}
Child Class
#Entity
#Table(name = "TOWN")
public class Town {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TOWN_ID")
private long id;
private String name;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "PROVINCE_ID")
#JsonBackReference("Province-Town")
private Province province;
private long kilometer;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Province getProvince() {
return province;
}
public void setProvince(Province province) {
this.province = province;
}
public long getKilometer() {
return kilometer;
}
public void setKilometer(long kilometer) {
this.kilometer = kilometer;
}
}
The response that I'm getting is like this
{
"id" : 1,
"name" : "Some Town",
"kilometer" : 350
}
What I'm expecting is
{
"id" : 1,
"name" : "Some Town",
"province" : {
//Province data.....
}
"kilometer" : 350
}
I was able to show something like this, but the Objects that I used are not Spring-data-jpa entities, just simple POJOs.
Is there any problem with my Entities? Or is there anything else?
Swap #JsonBackReference and #JsonManagedReference. Basically:
#JsonManagedReference
private Province province;
#JsonBackReference
private List<Town> towns;
The jhipster doesn't support create many to many relationships with extra fields.
What is the best way to create many to many association with extra columns in jhispter? Should i create a two one-to-many relationship with extra fields?
Using JHipster Domain Language (JDL), a #ManytoMany holding extra properties (columns) can be easily achieved using an association entity and two ManyToOne relationships. See below:
entity Foo{
...
}
entity Bar{
...
}
entity FooBarAssociation{
extraProperty1 String
extraProperty2 String
...
}
relationship ManyToOne {
FooBarAssociation{foo} to Foo{bars}
FooBarAssociation{bar} to Bar{foos}
}
You will have to do it manually.
this post describes how: https://hellokoding.com/jpa-many-to-many-extra-columns-relationship-mapping-example-with-spring-boot-maven-and-mysql/
In general, as #Antares42 said, you should create an entity for the Many-To-Many table like so:
first entity:
#Entity
public class Book{
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Book() {
}
public Book(String name) {
this.name = name;
bookPublishers = new HashSet<>();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
secound entity:
#Entity
public class Publisher {
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Publisher(){
}
public Publisher(String name){
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "publisher")
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
Join table entity:
#Entity
#Table(name = "book_publisher")
public class BookPublisher implements Serializable{
private Book book;
private Publisher publisher;
private Date publishedDate;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
#Id
#ManyToOne
#JoinColumn(name = "publisher_id")
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
#Column(name = "published_date")
public Date getPublishedDate() {
return publishedDate;
}
public void setPublishedDate(Date publishedDate) {
this.publishedDate = publishedDate;
}
}
This entity describes the relationship between Book and Publisher and the extra field is published_date
Let's say you have entities like Movie, Rater and needs a join table Ratings. You can write a JDL script like the following:
entity Movie { title String}
entity Rater { name String}
entity Rating { value Integer} //the extra field
relationship ManyToMany {
Rating{rater(name)} to Rater,
Rating{movie(title)} to Movie
}
save it in file.jdl in the project folder, open cmd type
jhipster import-jdl file.jdl
and you have everything
I have model. there is this part:
model was mapped by jpa annotations.Everywhere I use fetchType = EAGER. If I load vacancy from database, I have 2 duplicates status_for_vacancy objects.
I use property hbm2ddl.auto = update.
If I make new schema of database and fill data, I haven't duplicates status_for_vacancy objects.
It really?
code:
vacancy:
#Entity
#Table(name = "vacancy")
#XmlRootElement(name="vacancy")
public class Vacancy {
private List<VacancyStatus> statusList = new LinkedList<VacancyStatus>();
#OneToMany(mappedBy = "vacancy", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<VacancyStatus> getStatusList() {
return statusList;
}
public void setStatusList(List<VacancyStatus> statusList) {
this.statusList = statusList;
}
}
status_for_vacancy:
#Entity
#Table(name = "status_for_vacancy")
public class StatusForVacancy extends AbstractStatus {
public StatusForVacancy() {
super();
}
public StatusForVacancy(Integer id, String name) {
super(id, name);
}
}
#MappedSuperclass
#XmlRootElement
public abstract class AbstractStatus {
private Integer id;
private String name;
public AbstractStatus() {
super();
}
public AbstractStatus(String name) {
super();
this.name = name;
}
public AbstractStatus(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name ="id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
#NotEmpty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
vacancy_status:
#Entity
#Table(name = "vacancy_status")
public class VacancyStatus extends AbstractHistoryStatus {
private Vacancy vacancy;
private StatusForVacancy status;
public VacancyStatus() {
super();
}
public VacancyStatus(Integer id, User author, Date date,
Vacancy vacancy, StatusForVacancy status) {
super(id, author, date);
this.vacancy = vacancy;
this.status = status;
}
#ManyToOne
#JoinColumn(name = "vacancy_id")
public Vacancy getVacancy() {
return vacancy;
}
public void setVacancy(Vacancy vacancy) {
this.vacancy = vacancy;
}
#ManyToOne
#JoinColumn(name = "status_id")
public StatusForVacancy getStatus() {
return status;
}
public void setStatus(StatusForVacancy status) {
this.status = status;
}
}
#MappedSuperclass
public abstract class AbstractHistoryStatus {
private Integer id;
private User author;
private Date date;
public AbstractHistoryStatus() {
}
public AbstractHistoryStatus(Integer id, User author, Date date) {
super();
this.id = id;
this.author = author;
this.date = date;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
#Column(name="creation_date")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
It is all mapping code for these entities.
in debugger:
both id==500 ==> hibernate understand, that it is same objects.
I try add all data from old database to new database - I get old error(
I fix cause of appearance of this problem. It appearances if I add record to note table:
I highly recommend you write equals() and hashCode() methods. The standard equals()/hashCode() implement referential equality (do 2 objects reference the same memory location). So if hibernate has 2 of the 'same' object in memory, but they don't reference the same memory location then you will see the object show up twice. But if you implement equals() based on primary key being equal, then even if there are two copies of the same object in memory, Hibernate won't give you duplicates.
See the JPA spec:
2.4 Primary Keys and Entity Identity
Every entity must have a primary key. ... The value of its primary key
uniquely identifies an entity instance within a persistence context
and to EntityManager operations
Also see this SO post.