spring boot include OneToMany association in json response - java

I have an entity
#Entity
public class Post {
#Id
private Long id;
#NotNull
private Date createdAt;
#NotNull
private String text;
#NotNull
private Date updatedAt;
#OneToMany
private List<Comment> comments;
}
and another entity
#Entity
public class Comment {
#Id
private Long id;
#NotNull
private Date createdAt;
#NotNull
private String text;
#NotNull
private Date updatedAt;
}
and I have a simple controller that returns post json given id
#RestController
#RequestMapping("/posts")
public class ProductDimensionsController {
#RequestMapping(method = RequestMethod.GET)
public Post getPost(#RequestParam(value = "id") String id) throws ApiException {
...
...
...
}
}
I am getting in response:
{
"id": 401,
"createdAt": 1482364510614,
"updatedAt": 1482364510614,
"text": "abc",
}
I want follwing:
{
"id": 401,
"createdAt": 1482364510614,
"updatedAt": 1482364510614,
"text": "abc",
"comments": [{
"id": 101,
"createdAt": 1482364510614,
"updatedAt": 1482364510614,
"text": "xyz",
}]
}
How can I include associated OneToMany entities in the json response?

In order to get the result you want, you will have to mark the #ManyToOne(fetch = FetchType.LAZY) on the Comment entity. This will result in fetching the results in FetchType.LAZY mode.
Another approach could be:
#Entity
public class Post {
#Id
private Long id;
#NotNull
private Date createdAt;
#NotNull
private String text;
#NotNull
private Date updatedAt;
#OneToMany
#JsonIgnore
private List<Comment> comments;
#JsonIgnore
public List<Comment> getComments() {
return comments;
}
#JsonProperty
public void setComments(List<Comment> comments) {
this.comments = comments;
}
}

Related

How to do a post for a composite key Postman, Java API

I'm having some troubles attempting to do a post request to a bridging entity via postman
My service class for clientBook:
public ClientBook create(ClientBook clientBook) {
return this.clientBookIRepository.save(clientBook);
}
The clientBookIrepository is just a class that extends JpaRepository
My client class
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#SuperBuilder
public class Client implements Serializable {
#Id
#Column(name="clientId")
private String clientId;
#Embedded
#Column(name="name")
private Name name;
#OneToMany(mappedBy = "client")
private Set<ClientBook> clientBookSet;
#OneToMany(mappedBy = "client")
private Set<ClientContact> clientContactSet;
}
My book class
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#SuperBuilder
public class Book implements Serializable {
#Id
#Column(name = "bookId")
private String bookId;
#NotNull
private String bookName;
#NotNull
private String author;
#NotNull
private String genre;
#NotNull
private String description;
#NotNull
private String isRented;
#NotNull
private String imgUrl;
#OneToMany(mappedBy = "book")
private Set<ClientBook> clientBookSet;
}
My clientBook class
#Entity
#IdClass(ClientBookId.class)
public class ClientBook implements Serializable {
#Id
#ManyToOne
#PrimaryKeyJoinColumn(name="bookId",referencedColumnName = "bookId")
private Book book;
#Id
#ManyToOne
#PrimaryKeyJoinColumn(name="clientId",referencedColumnName = "clientId")
private Client client;
//Getters, setters, constructor and builder pattern here
I tested it 2 ways
Option 1:
{
"client": {
"clientId": "3",
"name": {
"firstName": "ABC",
"middleName": "",
"lastName": "123"
}
},
"book": {
"bookId": "1",
"bookName": "Hunger Games",
"author": "Suzanne Collins",
"genre": "Fiction",
"description": "The Hunger Games film series is composed of science fiction dystopian adventure films",
"isRented": "Available",
"imgUrl": "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1586722975i/2767052.jpg",
"clientBookSet": null
}
}
which returns status code 500 and returns this in the console:
Resolved [org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String' to required type 'testing.domain.Client' for property 'client'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'testing.domain.Client' for property 'client': no matching editors or conversion strategy found]
and option 2
{
"clientId": 3,
"bookId": 1
}
which also returns status code 500 and returns this in the console:
Column 'book_book_id' cannot be null

Spring Boot JPA one-to-many bidirectional foreign key is null

I have two entities Account and Order where one account can have one or more orders. I want to be able to log into an Account and then have order(s) for that account. Whenever I post an order for an existing account everything gets filled in properly but "account_id"(the foreign key). I would like to know why "account_id" is null and what I can do to address this problem, thanks.
Here is the Account entity:
#Entity
#Table(name="account")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
#Column(name="user_name")
private String userName;
#Column(name="password")
private String password;
#Column(name="address")
private String address;
#Column(name="city")
private String city;
#Column(name="state")
private String state;
#Column(name="country")
private String country;
#Column(name="zipcode")
private String zipcode;
#Column(name="credit_card_number")
private String creditCardNumber;
#Column(name="credit_card_code")
private int creditCardCode;
#Column(name="credit_card_name")
private String creditCardName;
#Column(name="credit_card_expiration_month")
private int creditCardExpirationMonth;
#Column(name="credit_card_expiration_year")
private int creditCardExpirationYear;
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
private List<Order> orders;
/* constructor */
/* Getters and setters */
Here is the Order entity:
#Entity
#Table(name="orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="total_price")
private int totalPrice;
#Column(name="total_quantity")
private int totalQuantity;
#Column(name="date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name="shipping_address")
private String shippingAddress;
#ManyToOne
#JoinColumn(name = "account_id",nullable = false, insertable = false, updatable = false)
private Account account;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
private Set<OrderItem> orderItems = new HashSet<>();
/* constructor */
/* Getters and setters */
Here is the Order Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
Page<Order> findByAccountId(#Param("id") Long id, Pageable pageable);
}
Here is the Order Controller
#RestController
#RequestMapping("/api")
public class OrderController {
#Autowired
private OrderRepository orderRepository;
#PostMapping("/save-order")
public Order saveOrder(#RequestBody Order order) {
return orderRepository.save(order);
}
}
Here is the postman post for order
URL: "http://localhost:8080/api/save-order"
{
"totalPrice": 10.17,
"totalQuantity": 3,
"dateCreated": "2022-09-05",
"shippingAddress": "123 Road, London, United Kingdom",
"account": [{
"id": 1,
"firstName": "Winston",
"lastName": "Smith",
"email": "smith22#gmail.com",
"userName": "wilsonsmith22",
"password": "mypassword22",
"address": "123 Road",
"city": "London",
"state": "London Region",
"country": "United Kingdom",
"zipcode": "220220",
"creditCardNumber": "4422336644885500",
"creditCardCode": 234,
"creditCardName": "Winston Smith",
"creditCardExpirationMonth": 5,
"creditCardExpirationYear": 2025
}]
}
You have :
#ManyToOne
#JoinColumn(name = "account_id",nullable = false, insertable = false, updatable = false)
private Account account;
#Column(name = "account_id")
private long accountId;
Both fields account & accountId have the same name, which souldn't happen I guess.

Hibernate Entity Mapping

Good night everyone,
I want to model a database that has the following entities and their perspective relationships:
But everytime I run the Java project to create the model at database, what I create is something like this:
There is another way to map this relationship? I'm mapping like that:
Article entity:
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private Boolean featured;
#Column(nullable = false)
private String title;
#Column(nullable = false)
private String url;
#Column(name = "image_url", nullable = false)
private String imageUrl;
#Column(name = "news_site", nullable = false)
private String newsSite;
#Column(nullable = false)
private String summary;
#Column(name = "published_at", nullable = false)
private String publishedAt;
#OneToMany
#JoinColumn(name = "launches_id")
private List<Launches> launches;
#OneToMany
#JoinColumn(name = "events_id")
private List<Events> events;
}
Launches entity
#Entity
public class Launches {
#Id
private String id;
private String provider;
}
Events entity:
#Entity
public class Events {
#Id
private Long id;
private String provider;
}
And I want to map this JSON, with this same launcher and events appearing in other articles:
{
"id": 4278,
"title": "GAO warns of more JWST delays",
"url": "https://spacenews.com/gao-warns-of-more-jwst-delays/",
"imageUrl": "https://spacenews.com/wp-content/uploads/2019/08/jwst-assembled.jpg",
"newsSite": "SpaceNews",
"summary": "",
"publishedAt": "2020-01-28T23:25:02.000Z",
"updatedAt": "2021-05-18T13:46:00.284Z",
"featured": false,
"launches": [
{
"id": "d0fa4bb2-80ea-4808-af08-7785dde53bf6",
"provider": "Launch Library 2"
}
],
"events": []
},
{
"id": 4304,
"title": "Government watchdog warns of another JWST launch delay",
"url": "https://spaceflightnow.com/2020/01/30/government-watchdog-warns-of-another-jwst-launch-delay/",
"imageUrl": "https://mk0spaceflightnoa02a.kinstacdn.com/wp-content/uploads/2020/01/48936479373_2d8a120c8e_k.jpg",
"newsSite": "Spaceflight Now",
"summary": "",
"publishedAt": "2020-01-30T04:08:00.000Z",
"updatedAt": "2021-05-18T13:46:01.640Z",
"featured": false,
"launches": [
{
"id": "d0fa4bb2-80ea-4808-af08-7785dde53bf6",
"provider": "Launch Library 2"
}
],
"events": []
}
According to your diagram, it should be:
#ManyToOne
#JoinColumn(name = "launches_id")
private Launches launches;
#ManyToOne
#JoinColumn(name = "events_id")
private Events events;
...and not #OneToMany ;) (Can there be an "Article" (with id=x) having launchers_id=y AND launchers_id=z? No, vice versa!:)
...for the #OneToMany, you should find the join columns "on the other side" (of relationship(s)).
According to your JSON, it is OneToMany. But then, we have to draw/expect:
#Entity
class Article {
//... id, columns, blah
#OneToMany
#JoinColumn(name = "article_id") // Launches "owns the relationship"/column
private List<Launches> launches;
#OneToMany
#JoinColumn(name = "article_id") // Events...!
private List<Events> events;
}
Generally, (when you expose your db model via json,) ensure:
no "circles" (in bi-directional associations). (#JsonManagedReference, #JsonBackReference, #JsonIgnoreProperties, ... )
not to expose data, that you don't want to expose. (#JsonIgnoreProperties, ...)
Regarding Hibernate-ManyToOne, please refer to https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
Regarding spring-data-jpa, best to:
gs-data-jpa
gs-data-rest
spring-boot-ref, data-jpa
reference-doc, data-jpa
reference-doc, data-rest

Working with Foreign keys in JpaRepository

I am developing simple API for practice project Online Shopping system. Since I am very new in working with APIs, I am having a trouble with my Entities and relationships. First, I give all my schema and classes before introduce the problem.
Here is a link for my database schema.
These are #Entity classes:
----
#Entity
#Table(name = "Customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "country")
private String country;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// constructor, getters, setters ....
#Entity
#Table(name = "Order")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "date")
private Date date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cust_id", nullable = false)
#JsonIgnore
private Customer customer;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Detail> details = new ArrayList<>();
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Invoice> invoices = new ArrayList<>();
//constructor, setters, getters ....
#Entity
#Table(name = "Product")
public class Product {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "price")
private Double price;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Detail> orderDetails = new ArrayList<>();
//cons, setters, getters ...
#Entity
#Table(name = "Detail")
public class Detail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ord_id", nullable = false)
#JsonIgnore
private Order order;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pr_id", nullable = false)
#JsonIgnore
private Product product;
#Column(name = "quantity")
private int quantity;
//similar classes for Invoice and Payment (no problem with them)
Here is my Sample Repository class:
#Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
}
Also here is my controller class:
#RestController
public class OnlineShoppingApiController {
#Autowired
ProductRepository productRepository;
#Autowired
OrderRepository orderRepository;
#Autowired
CustomerRepository customerRepository;
#Autowired
DetailRepository detailRepository;
#Autowired
InvoiceRepository invoiceRepository;
#Autowired
PaymentRepository paymentRepository;
#GetMapping("/products")
public List<Product> getProductsList(){
return productRepository.findAll();
}
#GetMapping("/customers")
public List<Customer> getCustomersList(){
return customerRepository.findAll();
}
#GetMapping("/orders")
public List<Order> getOrdersList(){
return orderRepository.findAll();
}
#GetMapping("/invoices")
public List<Invoice> getInvoicesList(){
return invoiceRepository.findAll();
}
#GetMapping("/payments")
public List<Payment> getPaymentsList(){
return paymentRepository.findAll();
}
#GetMapping("/details")
public List<Detail> getDetailsList(){
return detailRepository.findAll();
}
I am doing the same approach for all APIs and relationships.
When I call for /products in postman, I am getting result JSON like this:
[{
"id": 3,
"name": "pname_816",
"description": "pdesc_871_871_871_87",
"price": 1.41,
"orderDetails": [
{
"id": 9,
"quantity": 831
},
{
"id": 51,
"quantity": 701
},
{
"id": 87,
"quantity": 310
}
]
},
{
"id": 4,
"name": "pname_395",
"description": "pdesc_495_495_495_49",
"price": 26.65,
"orderDetails": [
{
"id": 85,
"quantity": 853
}
]
}]
Same fine results for /details, /invoices, and /payments.
The problem is if I send GET request for /customers, the result:
{
"timestamp": "2018-04-05T11:53:39.558+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Could not write JSON: could not extract ResultSet; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: java.util.ArrayList[0]->com.example.project.pojo.Customer[\"orders\"])",
"path": "/customers"
}
And if i send request for /orders, the result is:
{
"timestamp": "2018-04-05T11:54:37.316+0000",
"status": 500,
"error": "Internal Server Error",
"message": "could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet",
"path": "/orders"
}
The same approach is not working for all. I cannot understand where the problem is. Please help me to find it.
Thanks for the answer
I have finally found the answer by myself. Here the problem is not with Annotation or key referencing but with the naming the Entities.
Since order is reserved keyword for MySql, naming the entity and variables like this causes unexpected problems.
So I have just changed the Entity name to Orders in schema and code and working fine.
Hope this post will help for others too

Hibernate detached entity passed to persist

None of the similar questions on this site have been able to resolve my problem.
Error: org.hibernate.PersistentObjectException: detached entity passed to persist: healthcheckapi.model.Checks
I am trying to use Hibernate (JPA) to persist two objects to a MYSQL database. "Health" has a oneToMany relationship with "Checks".
Code:
Health Class:
#Entity
#Table(name="health")
public class Health {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String server;
private String description;
private String timestamp;
private String offset;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "health")
private List<Checks> checks;
public Health() {
}
public Health(int id, String server, String description, String timestamp, String offset, List<Checks> checks) {
this.server = server;
this.description = description;
this.timestamp = timestamp;
this.offset = offset;
this.checks = checks;
}
Checks Class:
#Entity
#Table(name="checks")
public class Checks {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private int status;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "healthid", referencedColumnName="id")
private Health health;
public Checks() {
}
public Checks(String name, int status) {
this.name = name;
this.status = status;
}
Sample JSON Health Object:
{
"server": "Server18",
"description": "Fine",
"timestamp": "00:02:30",
"offset": "00:01:00",
"checks": [
{ "id": "1",
"name": "cpu",
"status": 90
},
{
"id": "2",
"name": "memory",
"status": 88
},
{
"id": "3",
"name": "apacheService",
"status": 76
}
]
}
Note that the id's for both objects are auto-generated which I think is part of the problem.
Use the CascadeType.MERGE instead of CascadeType.ALL.

Categories

Resources