Cascade on bidirectional #OneToOne with #MapsId - java

I work on spring boot 2.1.1.RELEASE, hibernate 5.3.7.FINAL
Rules are, that user can have no phone (phone is nullable in user) but, phone can't exist without a user (user is not null in phone).
Entities:
#Entity
public class Phone {
#Id
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "id")
private User user;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(final User user) {
this.user = user;
}
}
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Phone phone;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public Phone getPhone() {
return phone;
}
public void setPhone(final Phone phone) {
this.phone = phone;
}
}
Controller:
#RestController
#RequestMapping
public class UserController {
private final UserService userService;
public UserController(final UserService userService) {
this.userService = userService;
}
#GetMapping("/demo")
public void createUserAndAddPhone() {
final User user = new User();
userService.save(user);
final Phone phone = new Phone();
phone.setUser(user);
user.setPhone(phone);
userService.update(user);
}
}
Repository:
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}
Serivce:
#Service
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
#Transactional
public void save(final User user) {
userRepository.save(user);
}
#Transactional
public void update(final User user) {
userRepository.save(user);
}
}
Tables:
CREATE TABLE `phone` (
`id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
application yml:
spring:
datasource:
url: jdbc:mysql://localhost:3308/demo?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
hibernate:
ddl-auto: validate
pom xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Call GET http://localhost:8080/demo gives me an error:
org.hibernate.id.IdentifierGenerationException: attempted to assign id
from null one-to-one property [com.example.demo.Phone.user]
When I comment out userService.save(user);, it works and generates:
insert into `user`
values ( )
-- Generated identifier: 13, using strategy: org.hibernate.id.ForeignGenerator
insert into `phone` (`id`) values (?)
-- Binding parameter [1] as [BIGINT] - [13]
but if the user is persisted and then updated, it doesn't work (raises the above exception)

Ah, in the end, this is a hibernate bug (HHH-12436).
It is reproducible in a pure hibernate application by the following use case:
Session session = sessionFactory.openSession();
Transaction tr1 = session.beginTransaction();
User user = new User();
session.persist(user);
tr1.commit();
Transaction tr2 = session.beginTransaction();
Phone newPhone = new Phone();
user.setPhone(newPhone);
newPhone.setUser(user);
session.merge(user);
tr2.commit();
session.close();
As you can see from the above link it is fixed in hibernate 5.4 branch.
P.S. I was able to reproduce the problem in the latest 5.3 version (5.3.18.Final)

Related

Postman Get request returns duplicate results

When I issue a Postman Get request, it returns duplicate result. Even when I had only three records in my database, Postman returns hundreds of same record duplicated.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.resource</groupId>
<artifactId>akademiks</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>akademiks</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Entity class:
#Entity
#Table(name = "Chemistry")
public class Chemistry {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "date")
private Date date;
#Column(name = "question_no")
private Integer questionNo;
#Column(name = "question")
private String question;
#OneToOne(mappedBy = "question",cascade = CascadeType.ALL)
private ChemistryAnswer answer = new ChemistryAnswer();
public Chemistry() {}
public Chemistry(Date date, Integer questionNo, String question) {
this.date = date;
this.questionNo = questionNo;
this.question = question;
this.answer.setDate(date);
this.answer.setQuestionNo(questionNo);
this.answer.setQuestion(this);
}//accessors
Entity class:
#Entity
#Table(name = "Chemistryanswer")
public class ChemistryAnswer {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "date")
private Date date;
#Column(name = "question_no")
private Integer questionNo;
#Column(name = "solution")
private String solution;
#Column(name = "solution_url")
private String solutionUrl;
#OneToOne
#JoinColumn(name = "question_id")
private Chemistry question;
public ChemistryAnswer() {}
public ChemistryAnswer(Integer questionNo, String solution, String solutionUrl) {
this.questionNo = questionNo;
this.solution = solution;
this.solutionUrl = solutionUrl;
}
public ChemistryAnswer(Date date, Integer questionNo) {
this.date = date;
this.questionNo = questionNo;
}
public Date getDate() {
return date;
}//accessors
jpaRepository:
#RepositoryRestResource(collectionResourceRel = "chemistry", path = "chemistry")
public interface ChemistryRepo extends JpaRepository<Chemistry, Integer> {
}
Service class:
public interface ChemistryService {
public List<Chemistry>findAll();
public void save(Chemistry chemistry);
}
Service implementation:
#Service
public class ChemistryServiceImpl implements ChemistryService {
private ChemistryRepo repo;
public ChemistryServiceImpl() {}
#Autowired
public ChemistryServiceImpl(ChemistryRepo repo) {
this.repo = repo;
}
#Override
public List<Chemistry> findAll() {
return repo.findAll();
}
#Override
public void save(Chemistry chemistry) {
Chemistry tempChemistry = new Chemistry(chemistry.getDate(),
chemistry.getQuestionNo(), chemistry.getQuestion());
ChemistryAnswer answer = tempChemistry.getAnswer();
tempChemistry.setAnswer(answer);
repo.save(tempChemistry);
}
}
RestController class:
#RestController
public class ChemistryController {
private ChemistryService service;
#Autowired
public ChemistryController(ChemistryService service) {
this.service = service;
}
#GetMapping("/chemistries")
public ResponseEntity<Object>findAll(){
return new ResponseEntity<Object>(service.findAll(), HttpStatus.OK);
}
#PostMapping("/chemistry")
public void save(#RequestBody Chemistry chemistry,
HttpServletResponse response) throws IOException {
service.save(chemistry);
response.sendRedirect("/chemistries");
}
}
Stacktrace Get request:
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.5.jar:2.12.5]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.5.jar:2.12.5]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.5.jar:2.12.5]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.5.jar:2.12.5]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.5.jar:2.12.5]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.5.jar:2.12.5]
The issue you are facing is quite abstract. "Postman returns hundreds of same record duplicated." does not tell us much. Still, I guess that the issue is your bi-directional relationship. Try adding #JsonManagedReference and #JsonBackReference to your bi-directional relationship in your model as follows:
#JsonManagedReference
#OneToOne(mappedBy = "question",cascade = CascadeType.ALL)
private ChemistryAnswer answer = new ChemistryAnswer();
#OneToOne
#JsonBackReference
#JoinColumn(name = "question_id")
private Chemistry question;

#EnableJpaRepositories is not present in spring boot 2.1.4.RELEASE

I created a project using spring boot 2.1.4.RELEASE with the following dependencies:
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I have the following entity and repository:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "last_name")
private String lastName;
#Column(name = "age")
private Integer age;
#Column(name = "createdAt")
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
...
}
PersonRepository.java
#Repository
public interface PersonRepository extends CrudRepository<Person, Integer> {
}
The following is my Application class:
#SpringBootApplication
public class SpringDataApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(SpringDataApplication.class, args);
PersonRepository personRepository = context.getBean(PersonRepository.class);
Person p1 = new Person("Juan", "Camaney", 55);
Person p2 = new Person("Arturo", "Lopez", 33);
Person p3 = new Person("Pancho", "Coscorin", 22);
personRepository.save(p1);
personRepository.save(p2);
personRepository.save(p3);
Iterator<Person> people = personRepository.findAll().iterator();
while (people.hasNext()) {
Person temp = people.next();
System.out.println(temp);
}
}
}
If i execute my application i get the following error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.devs4j.spring.data.repositories.PersonRepository' available
The solution of it is add the following configuration class:
#Configuration
#EnableJpaRepositories("com.devs4j.spring.data.repositories")
public class JpaConfiguration {
}
But I get the error :
EnableJpaRepositories cannot be resolved to a type
If i downgrade to 2.0.5.RELEASE everything works fine.
I'm confused because when i check the following spring documentation https://docs.spring.io/spring-data/jpa/docs/2.1.6.RELEASE/reference/html/ I see that it is still using #EnableJpaRepositories("com.acme.repositories")
Am i doing something wrong ?
In your configuration.java, have you added the import:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

Error executing DDL via JDBC Statement in Spring-MVC project

I'm trying to run my Java Spring MVC project application, but I get the following error (I have connectted my MySql database to the project):
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL via JDBC Statement
which is caused by:
Caused by: java.sql.SQLException: Cannot add foreign key constraint
The foreign key problem above is about the "OrderItems" Entity in my database, here is its code:
package com.example.WebAppProcess20.Entities;
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name = "ordersitems", schema = "theprocess")
#IdClass(OrdersitemsEntityPK.class)
public class OrdersitemsEntity {
private String orderId;
private String productId;
private Integer qunatity;
private OrdersEntity ordersByOrderId;
#Id
#Column(name = "orderId")
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
#Id
#Column(name = "product_id")
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
#Basic
#Column(name = "qunatity")
public Integer getQunatity() {
return qunatity;
}
public void setQunatity(Integer qunatity) {
this.qunatity = qunatity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
OrdersitemsEntity that = (OrdersitemsEntity) o;
return Objects.equals(orderId, that.orderId) &&
Objects.equals(productId, that.productId) &&
Objects.equals(qunatity, that.qunatity);
}
#Override
public int hashCode() {
return Objects.hash(orderId, productId, qunatity);
}
#ManyToOne
#JoinColumn(name = "orderId", referencedColumnName = "orderId", nullable = false, updatable = false, insertable = false)
public OrdersEntity getOrdersByOrderId() {
return ordersByOrderId;
}
public void setOrdersByOrderId(OrdersEntity ordersByOrderId) {
this.ordersByOrderId = ordersByOrderId;
}
}
This Entity specifies the products included in each order, therefore it has two foreign keys - productId and OrderId.
also adding the pom.xml and hibernate.cfg.xml files if necessary:
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/theprocess?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- DB schema will be updated if needed -->
<property name="hbm2ddl.auto">update</property>
<mapping class="com.example.WebAppProcess20.Entities.ClientsEntity"/>
<mapping class="com.example.WebAppProcess20.Entities.InvoicesEntity"/>
<mapping class="com.example.WebAppProcess20.Entities.OrdersEntity"/>
<mapping class="com.example.WebAppProcess20.Entities.OrdersitemsEntity"/>
<mapping class="com.example.WebAppProcess20.Entities.ProductsEntity"/>
</session-factory>
</hibernate-configuration>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>WebAppProcess2.0</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>WebAppProcess2.0</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Can you please explain what the problem is?
Thanks!

How to implement API returns nested JSON with OneToMany relationship in Spring Boot?

I am developing simple API for practice project Online Shopping System. I am totally new in Spring Boot framework and creating API.
I want to return JSON similar to this:
[
{
"id": 1,
"name": "pname_46",
"description": "pdesc_793_793_793_79",
"price": 519.95,
"details": [{"orderId": 10,
"productId": 1,
"quantity": 4
}
{"orderId": 12,
"productId": 1,
"quantity": 5
}]
},
{
"id": 2,
"name": "pname_608",
"description": "pdesc_874_874_874",
"price": 221.7,
"details": [{"orderId": 20,
"productId": 2,
"quantity": 2
}
{"orderId": 3,
"productId": 2,
"quantity": 67
}]
}]
Here is my #Entity classes:
Product.java
#Entity
#Table(name = "Products")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "pcod")
private int id;
#Column(name = "pnam")
private String name;
#Column(name = "pdes")
private String description;
#Column(name = "price")
private Double price;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Detail> details = new ArrayList<>();
//Constructor, setter, and getter ..
}
Detail.java
#Entity
#Table(name = "Details")
public class Detail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ordid")
private Order order;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pcod")
private Product product;
#Column(name = "qty")
private int quantity;
//constructor, setters, and getters ..
}
There is also class named Order.java similar to Product.java
ProductRepository.java
#Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
}
OnlineShoppingApiController.java
#RestController
public class OnlineShoppingApiController {
#Autowired
ProductRepository productRepository;
#GetMapping("/products")
public List<Product> getAllProducts(){
return productRepository.findAll();
}
#GetMapping("/products/id={id}")
public Optional<Product> getOneProduct(#PathVariable String id){
int pid = Integer.parseInt(id);
return productRepository.findById(pid);
}
}
ProjectApplication.java
#SpringBootApplication
public class ProjectApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
}
This program gets data from MySql database. There are stored data in tables.
Tables look like this:
Products:
- pcod
- pnam
- pdes
- price
Details:
- ordid
- pcod
- qty
Here is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>project</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
When I run the application and check the API using POSTMAN, I am getting this result:
{
"timestamp": "2018-04-04T13:39:44.021+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.Product[\"details\"])",
"path": "/products"
}
How can I solve this problem?
Thanks for the answer
When your Product entity is being converted to Json, the product have a List of Details, the details are converted to Json as well but they are referencing the Product again and this starts and endless loop and you get the error.
A solution could be to add a #JsonIgnore in one side of the relationship
#Entity
public class Detail {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pcod")
#JsonIgnore
private Product product;
...
}
Using #JsonManagedReference and #JsonBackReference annotations in the two entities can solve the problem ,as well.
please refer to this article about Jackson bidirectional relationships.

Spring Data doesn't update with save() method

I'm currently working on an application for an university project. For this project, my team and I are using a Vaadin/Spring/Maven configuration. With this we also include a database connected with repositories.
The repositories are build the following way. First a class for objects:
import javax.persistence.*;
import java.util.Set;
#Entity
#Table (name = "component")
public class Component {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
long id;
String name;
float price;
boolean isVegetarian;
#OneToMany(mappedBy = "component", cascade = {CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
Set<InvoicePosition> Positions;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
ComponentCategory category_id;
String path;
public Component() {
}
public Component(String name, float price, boolean isVegetarian, ComponentCategory category, String path) {
this.name = name;
this.price = price;
this.isVegetarian = isVegetarian;
this.category_id = category;
this.path = path;
}
Then the additional repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ComponentRepository extends JpaRepository<Component, Long>{}
Then there is a repository for component category, in which we should be able to store a list of components:
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "componentCategory")
public class ComponentCategory {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String name;
#OneToMany(mappedBy = "category_id", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
List<Component> components;
public ComponentCategory() { }
public ComponentCategory(String name) {
this.name = name;
components = new ArrayList<>();
}
public void addComponentToCategory(Component component) {
if(!components.contains(component))
{
components.add(component);
component.category_id = this;
}
}
And then the same repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ComponentCategoryRepository extends JpaRepository<ComponentCategory, Long> {}
I am able to create new entries and they are also properly stored in the database. This is all stored in the Application.Java file to start the Vaadin application
ComponentCategory cat1 = new ComponentCategory("examp1");
Component test1 = new Component("example1", 1.25f, true, cat1, "");
Component test2 = new Component("example2", 0.89f, true, cat1, "");
cat1.addComponentToCategory(test1);
cat1.addComponentToCategory(test2);
compcatrep.saveAndFlush(comp); // compcatrep is the ComponentCategoryRepository, which is passed on to the init() function
Now I'm trying to update the entries in the database with the following logic:
Component test11 = new Component("Test", 2.15f, true, compcatrep.getOne((long)4), "");
ComponentCategory comp = compcatrep.getOne((long)4);
comp.addComponentToCategory(test11);
compcatrep.saveAndFlush(comp);
I made sure that there is an entry with the ID (long) 4. It does work and does not show me any error, when I execute the application. Also if I check the length of the stored list in the ComponentCategory after the saveAndFlush():
System.out.println(comp.getComponents().size());
System.out.println(compcatrep.getOne((long)4).getComponents().size());
I do receive different length of the lists. The second Syso does show one less then the first. I tried a lot and did not find the error. Some more information about my issue:
ApplicationProperties in target folder:
spring.datasource.url=jdbc:h2:file:./mensarioDB;DB_CLOSE_ON_EXIT=TRUE
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.hs-augsburg.bpap</groupId>
<artifactId>mensario</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mensario</name>
<description>mensario management</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<vaadin.version>8.1.0</vaadin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Categories

Resources