Spring Boot JPA returns correct count but no data - spring-boot

Evening,
I have a Spring application that is connected to a PostgresSQL db. I can connect to the database and see that the query is returning the correct number of elements for the array but nothing in them:
curl http://localhost:8080/books
[{},{},{}]%
My Book model looks like this:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.math.BigDecimal;
#Entity
public class Book {
#Id
#GeneratedValue
private Long id;
private String name;
private String author;
private BigDecimal price;
public Book() {}
public Book(String name, String author, BigDecimal price) {
this.name = name;
this.author = author;
this.price = price;
}
}
and the controller:
#RestController
public class BookController {
#Autowired
private BookRepository repository;
// Find
#GetMapping("/books")
List<Book> findAll() {
List<Book> books = repository.findAll();
System.out.println(books);
return repository.findAll();
}
}
I've looked at these questions here, here and here but those answers didn't fit with this.
What am I not doing to see data come back?

In order for your entity to be serialized by Spring the entity needs to have getters for its properties. You could use lombok to auto-generate getter/setters for you entity properties or just write them your own.

Related

SpringBoot StackOverflow error when getting entity

I have following problem. I'm new to Spring. I have created 2 entities and now using postman I want to get all books but I keep getting StackOverflowError.
Here is book model
package com.example.demo;
import jakarta.persistence.*;
import java.util.List;
#Entity
public class BookEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
#ManyToMany
private List<Author> author;
public BookEntity() {
}
public BookEntity(String title) {
this.title = title;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Author> getAuthor() {
return author;
}
public void setAuthor(List<Author> author) {
this.author = author;
}
}
Author class model
package com.example.demo;
import jakarta.persistence.*;
import java.util.List;
#Entity
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany
private List<BookEntity> book;
public Author() {
}
public Author(String name) {
this.name = name;
}
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<BookEntity> getBook() {
return book;
}
public void setBook(List<BookEntity> book) {
this.book = book;
}
}
repository for books
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface BookRepository extends JpaRepository<BookEntity, Long> {
}
repository for author
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {
}
controller for books
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/books")
public class BookController {
private final AuthorRepository authorRepository;
private final BookRepository bookRepository;
public BookController(AuthorRepository authorRepository, BookRepository bookRepository) {
this.authorRepository = authorRepository;
this.bookRepository = bookRepository;
}
#GetMapping
List<BookEntity> getAllBooks() {
return bookRepository.findAll();
}
}
Can you please explain what is happening? I can't get any further. I'm stuck
Well this is a common issue. The problem is that you have Book and Author related as ManyToMany. So now whenever you reach for Books, they have an Author field, and when Jackson is trying to add Author it turns out that Author has Books which again have an Author.
Im am aware of 2 ways out of here. First one is DTO you should create a class to be displayed by you controller looking somewhat like this:
public class BookDTO {
private long bookId;
private String bookTitle;
private List<AuthorDTO> authors;
// constructors getters setters
}
situation is a bit complicated because of Many to many so yo need another DTO for authors
public class AuthorDTO {
private long authorId;
private String authorName;
//constructors getters setters
}
you could use a service layer to do all of the mapping. Then you should return BookDTO in your controller.
Another way out are annotations:
#ManyToMany
#JsonManagedReference
private List<Author> author;
and
#ManyToMany
#JsonBackReference
private List<BookEntity> book;
#JsoonManaged and back References will stop Jackson from digging into another entity.
Another thing is you should consider mappedBy in one of your Entities to prevent creating 2 tables.

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.

I18n for custom error messages into JPA entity

I looking to understand how to internationalize JPA entity error message. I understand how its work into a controller using autowired MessageSource but in my case I want to do this into a JPA entity. I'm not intresting about using the same way as the controller issue because I think is not optimized to autowired the full MessageSource on this entity. If someone have a simple example to show me how its work with a simple entity like mine. My project using spring-boot 2.2 ; JPA ; and thymeleaf.
The entity I using:
package com.bananasplit.weblab2.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
#Entity
#Table(name = "todo")
public class Todo {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name", nullable = false)
#NotEmpty
#Size(min=2, max=30) // error message is already internationalized here with spring-boot
private String name;
#Column(name = "category", nullable = false)
#NotEmpty
#Pattern(regexp="(WORK|PERSONAL|SPECIAL)",
message="Category must be WORK or PERSONNAL or SPECIAL.") // here is the message I want to internationalize
private String category;
public Todo() {}
public Todo(String name, String category) {
this.name = name;
this.category = category;
}
#Override
public String toString() {
return String.format(
"Todo[id=%d, name='%s', category='%s']",
id, name, category);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
By default Spring boot uses this ValidationMessages.properties but you can override by adding this file in resources.
#Size(min=2, max=30, message="{empty.todo.name")
private String name;
In ValidationMessages.properties file
empty.todo.name = Cannot be blank
If you want to manage which package messages should be scanned by Spring then should follow this link

How to know the name of the resource from an Entity class, to build a Hateoas link to that resource?

Suppose I have two resources Person and Article
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long person_id;
private String firstName;
private String lastName;
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private List<Article> articles = new ArrayList<>();
}
#Entity
#Table(name="article")
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String details;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
private Person person;
}
I now want to add HATEOAS support to the response of the controller for which I am using org.springframework.hateoas.ResourceAssembler
public class PersonResourceAssembler implements ResourceAssembler<Person, Resource<Person>> {
private EntityLinks entityLinks;
public UserJobResourceAssembler(EntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
#Override
public Resource<Person> toResource(Person entity) {
Resource<UserJob> resource = new Resource<>(entity);
resource.add(
entityLinks.linkFor(Person.class).withSelfRel()),
entityLinks.linkFor(...logic...).withRel("articles")) //here I am hardcoding the relation link name i.e "article"
);
return resource;
}
}
So, in above code the "article" is hardcoded for the link name, but I don't want to do it this way. I want it do in the way Spring-Data-REST handles it i.e for every relationship it auto detects the name of the variable used inside the Entity class e.g articles will be picked from Person and person will be picked from Article.
I have no idea how Spring-Data-REST handles it, but are there any readymade/custom solutions for this requirement?
You can use the reflection API to introspect the entity and find associations. Something like:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.OneToMany;
public class AssociationUtility {
public static List<Field> getAssociatedFields(Object entity) {
Stream<Field> fields = Arrays.stream(entity.getClass().getDeclaredFields());
return fields.filter(field -> field.getAnnotation(OneToMany.class)
!= null).collect(Collectors.toList());
}
public static void main(String[] args) {
List<Field> fields = getAssociatedFields(new Customer());
fields.stream().forEach(f -> System.out.println("Make a link for Class: "
+ ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]
+ " with rel: " + f.getName()));
System.exit(0);
}
}

Spring boot MongoDb complex query

I have been learning myself MongoDB implementation in Spring Boot.
However, I came into a problem with complex queries.
I cannot find any right solution for how to implement complex queries to MongoDB from Spring boot.
I am querying the database with MongoRepository interface implementation.
Let's say that I have three collections:
Person - 1 Person can have many Pets.
Pet - 1 Pet can have 1 PetToy and 1 Person who owns him.
PetToy - 1 PetToy can belong to 1 Pet.
POJO classes are bellow
What do I want to achieve?
I want to make a query, which would be returned me a Person, whose Pet has a Toy (PetToy) with the name "Teddy".
I could not have found a way how to do it. Furthermore, is it the best practice to even use such complex queries, or is it better to write more of little ones in MongoDB?
POJOs:
#Document
#Data
#ToString
public class Person {
#Id
private String id;
private String firstname;
private String lastname;
private int age;
#DBRef
private Pet pet;
}
#Document
#Data
#ToString
public class Pet {
#Id
private String id;
private String name;
private int age;
#DBRef
private List<PetToy> toys;
}
#Document
#Data
#ToString
public class PetToy {
#Id
private String id;
private String name;
}
I have tried to use MongoRepositories; however, I was not able to make the complex query.
How can one write such a query to a MongoDB from Spring Boot?
Thank you very much in advance.
If you can use embedded attributes, the class model should be:
#Document
#Data
#Builder
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private int age;
private List<Pet> pets;
}
#Data
#Builder
public class Pet {
private String name;
private int age;
private List<PetToy> toys;
}
#Data
#Builder
public class PetToy {
private String name;
}
The repository with the method that achieves what you want:
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> getByPetsToysName(String name);
}
The getByPetsToysName method basically navigate between Person's attributes Person->pets->toys->name. More info here.
An example
#Configuration
#EnableMongoRepositories
public class TestMongo implements CommandLineRunner {
private final PersonRepository repository;
public TestMongo(PersonRepository repository) {
this.repository = repository;
}
#Override
public void run(String... args) throws Exception {
repository.save(Person.builder()
.firstName("John")
.lastName("Doe")
.age(20)
.pets(Stream.of(Pet.builder()
.name("Ursa")
.age(1)
.toys(Stream.of(PetToy.builder()
.name("Teddy")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
repository.save(Person.builder()
.firstName("Phillip")
.lastName("Larson")
.age(21)
.pets(Stream.of(Pet.builder()
.name("Bella")
.age(5)
.toys(Stream.of(PetToy.builder()
.name("Lolo")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
List<Person> persons = repository.getByPetsToysName("Teddy");
System.out.println(persons.size());
List<Person> persons1 = repository.getByPetsToysName("Lolo");
System.out.println(persons1.size());
}
}
Logs:
find using query: { "pets.toys.name" : "Teddy" } fields: Document{{}} for class: class Person in collection: person
If you want more complex queries you can to take a look at the Spring Data MongoDB docs.

Resources