Spring boot REST API not returnning expect Json data - spring-boot

I just begin my first web application using Spring Boot, and I create the first project as well as the Database for this project. It used to fine to return the list of products with encapsulated datas that a product has. When I leave it for several Days and come back, the query api return instead of the products themselves but links.
so when I type "http://localhost:8080/api/products/1" in the browser, it return this
unexpected return JSON data.
Where it use to return something like
{
"sku" : "BOOK-TECH-1000",
"name" : "Crash Course in Python",
"description" : "Learn Python at your own pace. The author explains how the technology works in easy-to-understand language. This book includes working examples that you can apply to your own projects. Purchase the book and get started today!",
"unitPrice" : 14.99,
"imageUrl" : "assets/images/products/books/book-1000.png",
"active" : true,
"unitsInStock" : 100,
"dateCreated" : "2021-01-04T21:05:48.000+0000",
"lastUpdated" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/products/1"
},
"product" : {
"href" : "http://localhost:8080/api/products/1"
},
"category" : {
"href" : "http://localhost:8080/api/products/1/category"
}
}
}
I dont know why the data informations are not return anymore, any suggestion?
The return page of the GET query
The DAO class I Created for the JPA
The product class
database data, which is what should be return in the result page
Product.java:
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
#Entity
#Table(name="product")
#Data
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//join with Entity "ProductCategory". vise verse, many products to one product Category.
#ManyToOne
#JoinColumn(name = "category_id", nullable = false)
private ProductCategory category;
#Column(name = "sku")
private String sku;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "unit_price")
private BigDecimal unitPrice;
#Column(name = "image_url")
private String imageUrl;
#Column(name = "active")
private boolean active;
#Column(name = "units_in_stock")
private int unitsInStock;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "last_updated")
#UpdateTimestamp
private Date lastUpdated;
}
ProductRepository.java:
import com..Entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.web.bind.annotation.CrossOrigin;
#CrossOrigin("http://localhost:4200")
public interface ProductRepository extends JpaRepository<Product, Long> {
}

I faced something similar due to lombok not working popery. Writing the getter and setter manually should work. In my case, adding lombok extension for vs code solved the problem.

Related

Spring Data CrudRepository's save throws InvocationTargetException

I have spent the whole weekend trying to debug this piece of code. I have a Spring RestController :
import com.tsakirogf.schedu.model.ContactMean;
import com.tsakirogf.schedu.model.DefaultContactMean;
import com.tsakirogf.schedu.model.human.Business;
import com.tsakirogf.schedu.services.BusinessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
import java.util.Set;
#RestController
#RequestMapping("api/v1/business/")
public class BusinessController
{
#Autowired
BusinessService businessService;
#GetMapping(value = "businesss")
Iterable<Business> list()
{
Iterable<Business> retVal = businessService.findAll();
return retVal;
}
#RequestMapping(value = "business", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
Business create(#RequestBody Business business)
{
CollectionOfContactMethods collectionOfContact = business.getContact();
collectionOfContact.setBusiness(business);
Set<ContactMean> contactMeanSet = collectionOfContact.getContactMeans();
DefaultContactMean defaultContactMeanSet = collectionOfContact.getDefaultContactMean();
defaultContactMeanSet.getCollectionOfContactMethodsDefault().setId(collectionOfContact.getId());
for (ContactMean element : contactMeanSet)
{
element.setCollectionOfContactMethods(collectionOfContact);
}
collectionOfContact.setDefaultContactMean(defaultContactMeanSet);
business.setContact(collectionOfContact);
Business retval = businessService.save(business);
return retval;
}
#RequestMapping(value = "business/{id}", method = RequestMethod.GET )
Optional<Business> get(#PathVariable Long id)
{
return businessService.findById(id);
}
}
And the service :
public interface BusinessService extends CrudRepository<Business, Long>
{
}
This is the model :
#Table(name = "business")
public class Business
{
#Id
#Column(name = "business_id", nullable = false)
private Long id;
#JsonProperty("name")
private String name;
#Embedded
#JsonProperty("address")
private Address address;
#OneToMany(mappedBy = "business",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
#JsonProperty("operatives")
#JsonIgnore
Set<Professional> operatives;
#OneToOne(mappedBy = "business",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false)
#JsonBackReference
#JsonProperty("contact_numbers")
private CollectionOfContactMethods contact;
public Business()
{
}
// Getters and Setters
}
When I send a POST request like this :
Where I got the following
{
"timestamp": "2021-11-01T08:59:06.343+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/api/v1/business/business"
}
I debug and I am getting InvocationTargetException as seen below
This is the controller, right before save() which seems to throw :
And here is the catch :
I found this article posted in a similar event in StackOverflow but I don't think that's what is happening in this case since I have only H2 database for now.
This is application.properties file :
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.hbm2ddl.auto=create
I would appreciate any ideas. Thanks for your time.
If you look at your last screenshot you see a message indicating that there is an id field that has no value.
In your entity you have the following declaration of your id field:
#Id
#Column(name = "business_id", nullable = false)
private Long id;
Which indicates to hibernate that it shouldn't generate a key or that there is no database assigned one. Which means you will manually need to set the value for id. If you don't you will run into this exception.
Now I assume that this was a mistake and that you actually wanted to have a sequence or auto-incremented id field. For this add the #GeneratedValue annotation to add this behavior.
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE))
#Column(name = "business_id", nullable = false)
private Long id;
This will instruct hibernate to use a sequence to generate the id upon inserting the entity. If your database supports identity columns you might want to use GenerationType.IDENTITY instead of GenerationType.SEQUENCE.

Ignore property in nested object of the same type with Spring Boot

I have one class which is entity and use the same class as a property:
#Entity
public class Employee {
private String name;
#OneToOne
#JoinColumn(name = "supervisor_id", referencedColumnName = "id")
private Employee supervisor;
//getters and setters
}
I want to get the supervisor of an employee, but not the supervisor of the supervisor. Can I manage this somehow?
{
"name": "PersonName",
"supervisor": {
"name": "Supervisor name",
"supervisor": null // i don't want this one
}
}
In the end I just used nested classes for both - dto and entity with fields needed.
True that I duplicated properties of the classes, but at least it's clean and simple.
Just ignore the nulls in the Json. The following works for me:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.persistence.*;
#Getter
#Setter
#ToString
#Entity
#JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "supervisor_id", referencedColumnName = "id")
private Employee supervisor;
}
Here's the test
#Test
public void test2() throws Exception {
Employee employee = makeEmployee("employee 1");
Employee supervisor1 = makeEmployee("supervisor 1");
employee.setSupervisor(supervisor1);
Employee save = employeeRepository.save(employee);
System.out.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(save));
}
Here's my test output:
{
"id" : 1,
"name" : "employee 1",
"supervisor" : {
"id" : 2,
"name" : "supervisor 1"
}
}
Make sure you use the correct JsonSerialize org.codehaus.jackson.map.annotate.JsonSerialize not com.fasterxml.jackson.databind.annotation.JsonSerialize

No property .. found for type .. in spring boot

I'm a beginner with spring and I have this little issue. "No property questionId found for type CourseTestCompleteField!" I have 2 model classes that are connected via a one to one join.
That 2 model class are:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "questions")
public class CourseTestQuestion {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="question_id")
private Long id;
#NotBlank
#Column(name = "question_course")
private String questionCourse;
#NotBlank
#Column(name = "question_type")
private String questionType;
public CourseTestQuestion(){
}
public CourseTestQuestion(String questionCourse, String questionType) {
this.questionCourse = questionCourse;
this.questionType = questionType;
}
// public getters and setters for all fields here
}
And:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "quiz_complete_field_questions",
uniqueConstraints = {
#UniqueConstraint(columnNames = "question_id")
}
)
public class CourseTestCompleteField {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Column(name = "question_content")
private String questionContent;
#NotBlank
#Column(name = "answer")
private String answer;
#NotBlank
#Column(name = "points")
private String points;
#NotBlank
#Column(name = "course")
private String course;
#NotBlank
#Column(name = "teacher_username")
private String teacher;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "question_id", referencedColumnName = "question_id")
private CourseTestQuestion courseTestQuestion;
public CourseTestCompleteField(){
}
public CourseTestCompleteField(CourseTestQuestion courseTestQuestion, String question, String answer, String points, String course, String teacher) {
this.courseTestQuestion = courseTestQuestion;
this.questionContent = question;
this.answer = answer;
this.points = points;
this.course = course;
this.teacher = teacher;
}
// public getters and setters for all fields here
}
My repo for both:
package com.example.springboot.repository;
import com.example.springboot.models.Course;
import com.example.springboot.models.CourseTestQuestion;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
#Repository
public interface CourseTestQuestionRepository extends JpaRepository<CourseTestQuestion, Long> {
Optional<CourseTestQuestion> findById(Long id);
Optional<CourseTestQuestion> findByQuestionCourse(String questionCourse);
}
And:
package com.example.springboot.repository;
import com.example.springboot.models.CourseTestCompleteField;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
#Repository
public interface CourseTestCompleteFieldRepository extends JpaRepository<CourseTestCompleteField, Long> {
Optional<CourseTestCompleteField> findById(Long id);
Optional<CourseTestCompleteField> findByQuestionId(Long questionId);
Optional<CourseTestCompleteField> findByCourse(String course);
List<CourseTestCompleteField> findByQuestionContent(String questionContent);
List<CourseTestCompleteField> findByTeacher(String teacher);
Boolean existsByQuestionContent(String questionContent);
}
The problem is with Optional<CourseTestCompleteField> findByQuestionId(Long questionId);but I don't get it why, because in database I have the table for CourseTestCompleteFieldModel with question_id column, and in CourseTestCompleteField I have CourseTestQuestion object. Tho, the table for CourseTestCompleteField has a different name, could be this a problem? I should rename the table to course_test_complete_field?
Can someone help me please? Thank you
Since,This is a query on nested Object. You need to update your query as this.
Optional<CourseTestCompleteField> findByCourseTestQuestion_Id(Long questionId);
This works even without "_"
Optional<CourseTestCompleteField> findByCourseTestQuestionId(Long questionId);
But better to put "_" while accessing nested fields for better readability.
There is no field call questionId in you entity and you have id only.
That's you got error. You can use that findyById(). That's only enough.
If you would like write JPA repository method like findBy..., getBy..., deleteBy...., countBy..., After this you need append exact field name from entity.
For example if you entity have name then can write below methods. findByName(); deleteByName(); countByName();
So try as below.
findBycourseTestQuestion(Object o);
Pass questions object.

Links with list of entities are getting rendered for any link instead of proper page in Spring boot application

I am relatively new to the Spring boot application. This has been working fine lately until I introduced a new entity as follows:
#Entity
#Table(name = "student_resource_access")
public class StudentResourceAccess extends implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private StudentResourceAccessPK studentResourceAccessPK;
#MapsId("studentId")
#ManyToOne
#JoinColumn(name = "student_id", referencedColumnName = "id")
private Student student;
#MapsId("userId")
#OneToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#Enumerated(EnumType.STRING)
#Column(name = "role")
private UserRole role;
}
Associated PK class is as follows:
#Embeddable
public class StudentResourceAccessPK implements Serializable {
private String studentId;
private String userId;
#Column(name = "external_entity_type")
#Enumerated(EnumType.STRING)
private ExternalEntityType externalEntityType;
}
Now after making this change, when I am launching my Spring boot application, any link I would like to browse, I am seeing very weird content just like below:
{
"_links" : {
"entity1" : {
"href" : "https://example.com/entity1{?page,size,sort}",
"templated" : true
},
"entity2" : {
"href" : "https://example.com/entity2{?page,size,sort}",
"templated" : true
},
...
"studentResourceAccess" : {
"href" : "https://example.com/studentResourceAccess{?page,size,sort}",
"templated" : true
},
..
I don't have any idea, why is this coming and how should I fix this? Could anyone please help here? Thanks.
EDIT:
I have spring-data-rest in the dependency tree for a long time. This has been working fine. Suddenly with the above change, as I mentioned, it started behaving this way. No idea, why is this happening.

Lazy Load in Hibernate

I can not make Lazy Load work on Spring.
#Entity
public class Livro {
#JsonInclude(Include.NON_NULL)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "Campo nome é obrigatorio")
private String nome;
#JsonInclude(Include.NON_NULL)
#JsonFormat(pattern = "dd/mm/yyy")
#NotNull(message = "Campo publicacao é obrigatorio")
private Date publicacao;
#JsonInclude(Include.NON_NULL)
private String editora;
#JsonInclude(Include.NON_NULL)
private String resumo;
#OneToMany( mappedBy = "livro", fetch = FetchType.LAZY )
private List<Comentario> comentarios;
//Comentario.Java
#Entity
public class Comentario {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonProperty("comentario")
private String texto;
private String usuario;
#JsonFormat(pattern = "dd/mm/yyy")
private Date data;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "LIVRO_ID")
#JsonIgnore
private Livro livro;
//LivrosRepository.java
package com.curso.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.curso.domain.Livro;
public interface LivrosRepository extends JpaRepository<Livro, Long> {
}
//ComentariosRepository.java
package com.curso.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.curso.domain.Comentario;
public interface ComentariosRepository extends JpaRepository<Comentario, Long> {
}
//LivrosService.java
#Service
public class LivrosService {
#Autowired
private LivrosRepository livrosRepository;
#Autowired
private ComentariosRepository comentariosRepository;
// [...]
public List<Livro> listar() {
return livrosRepository.findAll();
}
}
When I make a request to list the books, the behavior I expect is that I list all the data in books, but without the comments, since I'm using the java annotation
fetch = FetchType.LAZY, but the behavior I have is the return of all the data in the workbook.
[
{
"id": 4,
"nome": "Teste2",
"publicacao": "01/01/2018",
"editora": "Polenta",
"comentarios": [
{
"id": 1,
"usuario": "tester",
"data": "26/03/2019",
"comentario": "Comentario 1"
}
]
}
]
Hibernate Session exists within method with #Transactional. Passing entity outside Service class is a bad practise because session is being closed after leaving your listar method. On the other hand your entity contains lazy initialised collections (List<Comentario> comentarios), which cannot be pulled once session is closed.
The good practise is to map entity onto transport object and return those transport objects from service (not raw entities).
First of all you should wrap your public List<Livro> listar() method with #Transactional. Hibernate Session will be alive during execution of this method. It means you can pull lazy initialized elements within this method.
Secondly you should define LivroDto class with all necessary fields and map your Livro entity onto this POJO within this method then return LivroDro from the service.

Resources