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
Related
I have the following entities:
Book.java
#Entity #Data
public class Book {
#Id
private Long id;
#Column(unique = true, nullable = false)
private String title;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "book")
#JsonManagedReference
private List<Note> notes;
}
Note.java
#Entity #Data
public class Note {
#Id
private Long id;
#Column(nullable = false)
private String title;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "book_id", nullable = false)
#JsonBackReference
private Book book;
}
When I call my BookRestController, it returns a JSON containing all the properties I need:
{
"id": 15,
"title": "A fé explicada",
"author": "Leo J. Trese",
"notes": [{
"id": 10,
"title": "Sobre o pecado mortal"
}]
}
But when I call NoteRestController, the Book attribute is missing:
{
"id": 10,
"title": "Sobre o pecado mortal"
// missing "book" property here...
}
What am I doing wrong?
I'm using #OneToMany and #ManyToOne annotations to declare that it's a 1-N relationship; #JsonBackReference and #JsonManagedReference have the simple purpose to avoid infinite recursion.
Of course Book is omitted, that is what literally #JsonBackReference is for
#JsonBackReference is the back part of reference – it will be omitted from serialization.
(https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion)
The solution is as simple as it sounds: Instead of returning a Note entity in your REST controller (which I try to avoid as far as possible anyway to keep entities as entities that don't have any use in the REST context anyway) you can create a explicit transport object called e. g. NoteDTO which contains a reference to a book (that omits the notes of the book to not have infinite recursion):
public class NoteDTO {
private Long id;
private String title;
private BookReferenceDTO book;
// getters and setters
}
public class BookReferenceDTO {
private Long id;
private String title;
// getters and setters
}
I need to put and get data from MySql with spring boot (Hibernate, Jackson)
Sample JSON:
[
{
"name": "Product1",
"colors": [
{
"color": [
"#dddd33",
"#676767"
]
},
{
"color": [
"#FFdDFF"
]
}
]
}
]
Same JSON I want to receive but added "id". What should I do to achieve this?
Object:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonProperty("name")
private String name;
#JsonProperty("colors")
#OneToMany(mappedBy = "product_id")
private Set<Color> colors;
}
#Entity
public class Color {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long product_id;
#JsonProperty("color")
#CollectionTable(name = "color", joinColumns = #JoinColumn(name = "color_id"))
#Column(name = "color")
#ElementCollection
#OrderBy
private List<String> color;
}
This not work
I get error
java.sql.SQLException: Field 'id' doesn't have a default value
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
We are attempting to have Swagger 2.0. Basically, it's great except it is ignoring the #JsonIdentityInfo and #JsonIdentityReference annotations.
public class Source {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JsonIdentityReference(alwaysAsId=true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name")
#JsonProperty("sourceDefinitionName")
private SourceDefinition sourceDefinition;
... getters and setters
}
Resulting in the Swagger schema output:
{
"id": 0,
"sourceDefinitionName": {
"configuration": {},
"driver": "string",
"id": "string",
"name": "string",
"sourceType": "QUERY",
"title": "string"
}
}
You can see that it indeed reads the #JsonProperty annotation renaming the "sourceDefinition" to "sourceDefinitionName" but the value should just be a string.
Does anyone have any insight into this sort of problem with this integration?
#ManyToOne(targetEntity = Source.class, optional = false)
#JoinColumn(name = "SOURCE_DEF_NAME", referencedColumnName = "name", nullable = false,
foreignKey = #ForeignKey(name = "FK_sourceDefName"))
private SourceDefinition sourceDefinition;
#Column(name = "SOURCE_DEF_NAME", insertable = false, updatable = false)
private String sourceDefinitionName;
Json identity is not working for me too. I have fond following workaround. see working example json-sample
I have 3 entities with Many to many relationship. For example Class, Student and Subject.
There are two relationship tables/entities ClassStudent and StudentSubject. There tables take care of the relationship. ClassStudent class looks something like this:
public class ClassStudent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
#NotNull
private Long classId;
#Column(nullable = false)
#NotNull
private Long studentId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "classId", updatable = false, insertable = false)
private Class class
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "studentId", updatable = false, insertable = false)
private Student student;
}
StudentSubject class is also on the same pattern.
I am trying to write a query which each Class, it's Students and every Student's Subjects. Something Like
[
{
"class":{
"id":1,
"name":Maths
},
"studentDetails":[ {
"Student":{
"id":1
"name":"First Name"
},
"Subjects": [{
{
"id":1,
"code":xxx
},
{
"id":2,
"code":yy
}
}]
}]
I tried writing a basic query but it did not work at all.
SELECT c, cs.student, ss.subject FROM ClassStudent cs, Class c, Student s, StudentSubject ss, Subject subject"
+ " LEFT JOIN cs.student student"
+ " LEFT JOIN ss.subject subject"