SpringBoot StackOverflow error when getting entity - spring-boot

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.

Related

Why am I getting null for the date when I create a Todo entity?

What is wrong with my to-do application? I want the user to be able to add a todo and have it be saved in my MySQL database with the time it was created, but I don't know what I'm doing wrong.
I am new to learning Springboot and would appreciate any suggestions or advice.
Todo Entity:
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import javax.persistence.*;
import java.util.Date;
#Entity(name = "Todo")
#NoArgsConstructor
#Table(name = "todos")
public class Todo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name="description")
private String description;
#Column(name="target_date")
#CreationTimestamp
private Date targetDate;
public Todo(String description) {
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getTargetDate() {
return targetDate;
}
public void setTargetDate(Date targetDate) {
this.targetDate = targetDate;
}
#Override
public String toString() {
return "Todo{" +
"id=" + id +
", description='" + description + '\'' +
", targetDate=" + targetDate +
'}';
}
}
Adding a Todo with Spring Data JPA
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
import java.util.List;
#Repository
#Component
public interface TodoRepository extends JpaRepository<Todo, Integer> {
#Modifying
#Query(value = "INSERT INTO todos (description) VALUES (:description)", nativeQuery=true)
#Transactional
void addTodo(#Param("description") String description);
}
TodoController
#RestController
#RequestMapping(value = "/api/v1/todos")
#AllArgsConstructor
public class TodoController {
#Autowired
private ITodoService todoService;
#PostMapping(value = "/add-todo")
public String addTodo(#RequestParam String description) {
Todo todo = new Todo();
todo.setDescription(description);
todoService.addTodo(todo);
return todo.toString();
}
after getting a post request, the target_date is getting NULL in MySQL
I assume you can solve it by using persist():
#Autowired EntityManager entityManager;
#PostMapping(value = "/add-todo")
public String addTodo(#RequestParam String description) {
Todo todo = new Todo();
todo.setDescription(description);
entityManager.persist(todo);
return todo.toString();
}

Get from VIEW in Spring boot

I am beginner with Spring Boot and trying to improve my skills to get new job, so I hope you help me even if the question maybe easy for you as I search a lot and gain nothing.
I need to get by id, but return data is duplicated with only one record, I will show you what I do and the result for more explanation.
In DB side:
I have VW_Prices view in DB and it's data as shown below:
In Spring Boot side:
VW_Prices class is :
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Immutable;
#Entity
#Table(name = "VW_PRICES")
public class VW_Prices implements Serializable {
private long dealId;
private Long quotationId;
private Long productPriceForEjada;
private Long productPriceForClient;
private Long productId;
private Long productQuantity;
private String productName;
#Id
#Column(name = "ID")
public long getDealId() {
return dealId;
}
public void setDealId(long dealId) {
this.dealId = dealId;
}
#Column(name = "PRODUCT_QUANTITY")
public Long getProductQuantity() {
return productQuantity;
}
public void setProductQuantity(Long productQuantity) {
this.productQuantity = productQuantity;
}
#Column(name = "PRODUCT_NAME")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
#Column(name = "PRODUCT_PRICE_FOR_EJADA")
public Long getProductPriceForEjada() {
return productPriceForEjada;
}
public void setProductPriceForEjada(Long productPriceForEjada) {
this.productPriceForEjada = productPriceForEjada;
}
#Column(name = "PRODUCT_PRICE_FOR_CLIENT")
public Long getProductPriceForClient() {
return productPriceForClient;
}
public void setProductPriceForClient(Long productPriceForClient) {
this.productPriceForClient = productPriceForClient;
}
#Column(name = "PRODUCT_ID")
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
#Column(name = "QUOTATION_ID")
public Long getQuotationId() {
return quotationId;
}
public void setQuotationId(Long quotationId) {
this.quotationId = quotationId;
}
}
and I create VW_PricesRepository
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import springboot.deals_tracker_system.models.VW_Prices;
import springboot.deals_tracker_system.models.VW_Prices_interface;
public interface VW_PricesRepository extends JpaRepository<VW_Prices, Long> {
#Query( nativeQuery = true,value = "SELECT distinct * from VW_Prices v where v.id = :dealID " )
List<VW_Prices> findByDealId( #Param("dealID") Long id);
}
and my in my Service
public List<VW_Prices> findByDealId(Long dealId) {
System.out.println("we are in service");
List<VW_Prices> variableForDebug = VW_pricesRepository.findByDealId(dealId);
for (VW_Prices vw_Prices : variableForDebug) {
System.out.println(vw_Prices.getDealId() + " " + vw_Prices.getProductName());
}
return variableForDebug;
//return VW_pricesRepository.findByDealId(dealId);
}
When I pass dealId = 39 the result comes duplicated and not correct as in below:
how can I get correct data??
The view is made for Quotation Product Table to get product name.
i think the problem is the id annotation you must add GeneratedValue
fro the class:
#Entity
#Table(name = "VW_PRICES")
public class VW_Prices implements Serializable {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private long dealId;
private Long quotationId;
private Long productPriceForEjada;
private Long productPriceForClient;
private Long productId;
private Long productQuantity;
private String productName;
//code..
}
You dont have to use JPQL for this type of queries it's already exist in jpa:
VW_PricesRepository:
public interface VW_PricesRepository extends JpaRepository<VW_Prices, Long> {
}
to get data by id use findById like that:
public VW_Prices findByDealId(Long dealId) {
System.out.println("we are in service");
VW_Prices vw_Prices = VW_pricesRepository.findById(dealId);
System.out.println(vw_Prices.getDealId() + " " +
vw_Prices.getProductName());
}
return vw_Prices;
}
All data should be deleted from VW_Prices table because ids are not unique, try to insert new data with unique id then try the above code
I detect the problem, The view has main table Quotation and I didn't select it's ID and I used ID of the secondary table as the main ID for the View
I just write it if any one Google for such problem

findBy( ) methods not getting displayed

I am trying to use Spring data JPA find inbuilt methods.
I want to use findByName() , findByDescription but as of now findById() is the only method getting displayed for me.
This is my Entity class.
package com.sood1.springdata.product.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Product {
#Id
private int id;
private String name;
#Column(name = "description")
private String desc;
private Double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
This is my Repository
package com.sood1.springdata.product.repos;
import org.springframework.data.repository.CrudRepository;
import com.sood1.springdata.product.entities.Product;
public interface ProductRepo extends CrudRepository<Product, Integer> {
}
Can anyone suggest why findByName() or other find methods not coming for me.
You must declare a method in the repository interface like
List<Product> findProductByName(final string name);
or something like that
Johan

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 don't let me create a repository without database

I've created a project on Spring Boot.
I've two providers extending the same Abstract provider, and i load on startup the one i'm interested in via Spring Profile.
One of the providers is based on JPA, the other have his interface implemented where i make calls to webservices.
This is the interface of the provider wich i don't want to use databases:
package net.worldline.mst.metro.ds.core.massilia.provider;
import net.worldline.mst.metro.ds.core.contract.IProductRepository;
import net.worldline.mst.metro.ds.core.massilia.model.MassiliaProduct;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.NoRepositoryBean;
#Profile("massilia")
#NoRepositoryBean
public interface MassiliaProductRepository extends IProductRepository<MassiliaProduct,String> {
}
And this is the interface for the provider using database :
package net.worldline.mst.metro.ds.core.local.provider;
import net.worldline.mst.metro.ds.core.contract.IProductRepository;
import net.worldline.mst.metro.ds.core.local.model.Product;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
import org.springframework.stereotype.Repository;
#Profile("local")
#Repository
public interface MonBoProductRepository extends IProductRepository<Product,String> {
#Query("select p.variants from Product p where p.ean = :ean")
List<Product> findVariantByEan(#Param("ean") String ean);
#Query("select p.companions from Product p where p.ean = :ean")
List<Product> findCompanionByEan(#Param("ean") String ean);
}
They extend this interface in common :
package net.worldline.mst.metro.ds.core.contract;
import net.worldline.mst.metro.ds.core.model.AbstractProduct;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.web.bind.annotation.PathVariable;
import java.io.Serializable;
import java.util.List;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.PathVariable;
import java.io.Serializable;
import java.util.List;
#NoRepositoryBean
public interface IProductRepository<T extends AbstractProduct,ID extends Serializable> extends CrudRepository<T, ID> {
#RestResource(path = "byEAN")
T findByEan(#Param("ref") Integer ean);
T findProductByEan(#PathVariable ID ean);
List<T> findVariantByEan(#PathVariable ID ean);
List<T> findCompanionByEan(#PathVariable ID ean);
}
The provider wich isn't using database have an implementation, for job reasons, i can't show you the implementation, but it calls inside webservices
Like my providers, i've two models, extending the same abstract class.
One is annoted with #Entity,#Id and co, and i don't want to add this annotations on the other class, because for me, i've precised that i didn't want any database by asking none in the application-${profile}.properties.
This is this Model i used with the bdd :
package net.worldline.mst.metro.ds.core.local.model;
import net.worldline.mst.metro.ds.core.model.AbstractProduct;
import net.worldline.mst.metro.ds.core.model.AbstractProductCharacteristic;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "PRODUCTS")
#Profile("local")
public class Product extends AbstractProduct {
private static final Logger log = LoggerFactory.getLogger(Product.class);
#ManyToMany(
fetch = FetchType.LAZY
)
#JoinTable(
name="products_to_variants",
joinColumns = #JoinColumn(name="productEan"),
inverseJoinColumns = #JoinColumn(name="productEanVariant")
)
private List<Product> variants;
#ManyToMany(
fetch = FetchType.LAZY
)
#JoinTable(
name="products_to_companions",
joinColumns = #JoinColumn(name="productEan"),
inverseJoinColumns = #JoinColumn(name="productEanCompanion")
)
private List<Product> companions;
#Column(name = "accroche")
private String accroche;
#Id
#Column(name = "ean", unique = false)
private String ean;
#Column(name = "descriptif")
private String descriptif;
#Column(name = "libelle")
#NotEmpty
private String libelle;
#Column(name = "oldPrice")
private String oldPrice;
#Column(name = "price")
#NotEmpty
//#Digits(fraction = 0, integer = 10)
private String price;
#Column(name = "stock")
private String stock;
#OneToMany(mappedBy = "ean" )
protected List<ProductCharacteristic> characteristics;
#OneToMany(mappedBy = "product" )
#NotEmpty
protected List<ProductVisual> visuals;
public List<Product> getVariants() {
return variants;
}
public void setVariants(List<Product> variants) {
this.variants = variants;
}
public List<Product> getCompanions() {
return companions;
}
public void setCompanions(List<Product> companions) {
this.companions = companions;
}
#Override
public String getAccroche() {
return accroche;
}
#Override
public void setAccroche(String accroche) {
this.accroche = accroche;
}
#Override
public String getEan() {
return ean;
}
public void setRef(String ean) {
this.ean = ean;
}
#Override
public String getLibelle() {
return libelle;
}
#Override
public void setLibelle(String libelle) {
this.libelle = libelle;
}
#Override
public String getOldPrice() {
return oldPrice;
}
#Override
public void setOldPrice(String oldPrice) {
this.oldPrice = oldPrice;
}
#Override
public String getPrice() {
return price;
}
#Override
public void setPrice(String price) {
this.price = price;
}
#Override
public String getStock() {
return stock;
}
#Override
public void setStock(String stock) {
this.stock = stock;
}
#Override
public List<? extends AbstractProductCharacteristic> getCharacteristics() {
return characteristics;
}
#Override
public List<ProductVisual> getVisuals() {
return visuals;
}
public String getDescriptif() {
return this.descriptif;
}
public void setDescriptif(String descriptif) {
this.descriptif=descriptif;
}
}
This is the model i don't want to use with a database:
package net.worldline.mst.metro.ds.core.massilia.model;
import net.worldline.mst.metro.ds.core.model.AbstractProduct;
import org.springframework.context.annotation.Profile;
import javax.persistence.*;
import java.util.List;
#Profile("massilia")
public class MassiliaProduct extends AbstractProduct {
#Override
public String getEan() { return this.ean; }
#Override
public String getLibelle() { return this.libelle; }
#Override
public String getPrice() { return this.price; }
#Override
public String getAccroche() { return this.accroche; }
#Override
public String getOldPrice() { return oldPrice; }
#Override
public String getStock() { return stock; }
#Override
public String getDescriptif() {
return descriptif;
}
#Override
public List<MassiliaCharacteristic> getCharacteristics() {
return (List<MassiliaCharacteristic>)characteristics;
}
#Override
public List<MassiliaProductVisual> getVisuals() {
return (List<MassiliaProductVisual>)visuals;
}
}
They share this model in common :
package net.worldline.mst.metro.ds.core.model;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.core.Relation;
import java.util.List;
#Relation(value = "product", collectionRelation = "product")
public abstract class AbstractProduct extends ResourceSupport {
protected String ean;
protected String libelle;
protected String accroche;
protected String price;
protected String oldPrice;
protected String stock;
protected String descriptif;
protected List<? extends AbstractProductCharacteristic> characteristics;
protected List<? extends AbstractProductVisual> visuals;
public abstract String getEan();
public abstract String getLibelle();
public abstract String getPrice();
public abstract String getAccroche();
public abstract String getOldPrice();
public abstract String getStock();
public abstract List<? extends AbstractProductCharacteristic> getCharacteristics();
public abstract List<? extends AbstractProductVisual> getVisuals();
public abstract String getDescriptif();
public void setEan(String ean) {
this.ean = ean;
}
public void setLibelle(String libelle) {
this.libelle = libelle;
}
public void setPrice(String price) {
this.price = price;
}
public void setAccroche(String accroche) {
this.accroche = accroche;
}
public void setOldPrice(String oldPrice) {
this.oldPrice = oldPrice;
}
public void setStock(String stock) {
this.stock = stock;
}
public void setCharacteristics(List<? extends AbstractProductCharacteristic> characteristics) {
this.characteristics = characteristics;
}
public void setVisuals(List<? extends AbstractProductVisual> visuals) {
this.visuals = visuals;
}
public void setDescriptif(String descriptif) {
this.descriptif = descriptif;
}
}
In the application-${profile}.properties, i precise :
spring.datasource.platform = hsqldb for the jpa instance.
spring.datasource.platform = none for the instance where i call my webservices.
My problem is simple : i was hoping spring letting me do what i want by implementing the repository, but when i launch my server, spring say that my objects are not managed, so if i don't add #Entity to my model, it doesn't want to run.
So why Spring data looks like it loads JPA repository by default ?
It was a human error in fact.
I'v forgotten a spring.datasource.platform = hsqldb in my application.properties file.
I wasn't looking at it cause i'm using spring profiles so i was looking at my application-massilia.properties wich contains spring.datasource.platform = none and is listened now cause i've deleted the duplicate in the other file.

Resources