I am working on a project using Springboot to create API to call all provinces in the list. So first i create an entity class
package example.parameter.entity;
import lombok.Data;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.List;
#Data
#Entity
#Table(name = "provinces",indexes = {
#Index(name = "PROVINCES_INDX_0", columnList = "name")
})
public class Province extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "provinces_generator")
#SequenceGenerator(name = "provinces_generator", sequenceName = "provinces_seq", allocationSize = 1)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#Column(name = "is_deleted")
private Boolean isDeleted;
#OneToMany(mappedBy = "province", cascade=CascadeType.ALL, orphanRemoval = true)
private List<Regency> regencies;
#NotBlank
#Column(name = "name")
private String name;
}
and then I created this responseDTO
package example.parameter.api.response;
import lombok.Data;
#Data
public class ProvinceResponseDTO {
private String id;
private String name;
}
after that I create the repository
package example.parameter.repository;
import example.parameter.api.response.ProvinceResponseDTO;
import example.parameter.entity.Province;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface ProvinceRepository extends JpaRepository<Province,Long> {
public List<ProvinceResponseDTO> findAllByIsDeleted(Boolean isDeleted);
}
when I am trying to hit the API I am getting this error on data layer.
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [example.parameter.entity.Province] to type [example.api.response.ProvinceResponseDTO]
I don't know thy this is happening, any solution to fix this issue?
Part:1
JpaRepository Query creation from method name should have the default return type of the entity object. In your case, your repository is for the Province entity. So, it should be like
public interface ProvinceRepository extends JpaRepository<Province, Long> {
public List<Province> findAllByIsDeleted(Boolean isDeleted);
}
Reference:-
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html
Part: 2
If you need to return a custom object from the query creation from a method name, then you can use dynamic projections like below:-
public interface ProvinceRepository extends JpaRepository<Province, Long> {
public <T> List<T> findAllByIsDeleted(Boolean isDeleted, Class<T> dynamicClassType);
}
while calling this method you define like
provinceRepository.findAllByIsDeleted(true, ProvinceResponseDTO.class)
Reference:- https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections.dtos
Related
I have a case, when from request I get a BookDTO with author id, and I have to create and save new book to database with nested Author entity. Now I get author object from database before creating book, but I am not sure that this is a right way. What if I with have multiple nested entity inside one. I have to get them all from DB before save it, or there is a more clean and fast way to do it?
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import javax.persistence.*;
#Entity
#Getter
#Setter
class Book
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "author_id")
private Author author;
public Book(BookDTO bookDTO, Author author)
{
this.setName(bookDTO.getName());
this.setAuthor(author);
}
}
#Getter
#Setter
class BookDTO
{
private String name;
private Long author_id;
}
#Entity
class Author
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
#Repository
interface BookRepo extends JpaRepository<Book, Long>
{
}
#Repository
interface AuthorRepo extends JpaRepository<Author, Long>
{
}
#Controller
class BookController
{
#Autowired
AuthorRepo authorRepo;
#Autowired
BookRepo bookRepo;
#MutationMapping
public Book createUser(BookDTO bookDTO)
{
// Getting author from DB before create book
Author author = authorRepo.getReferenceById(bookDTO.getAuthor_id());
Book book = new Book(bookDTO, author);
return bookRepo.save(book);
}
}
How can I save author ID for book without getting Author from database?
I have the following entities
#JsonInclude(JsonInclude.Include.NON_NULL)
#Embeddable
public class EmbeddableEntity
{
#Cascade(CascadeType.SAVE_UPDATE)
#OneToOne
#JoinColumn(name = "fk_entity_A")
private EntityA entityA;
}
#Entity
#Table(name = "tb_entity_a")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", length = 50)
#JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class EntityA
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
protected Long id;
#JsonIgnore
#OneToOne(mappedBy = "embeddableEntity.entityA", orphanRemoval = true)
protected EntityB entityB;
#NotNull
#Column(name = "boolean_field", columnDefinition = "TINYINT DEFAULT 0", nullable = false)
private Boolean booleanField = false;
}
#Entity
#Table(name = "tb_entity_b")
#DiscriminatorColumn(name = "type", length = 50)
#DiscriminatorValue("entityb")
public class EntityB
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Embedded
private EmbeddableEntity embeddableEntity;
}
I want JPAMetaModelEntityProcessor to generate a MetaModel class like this:
package br.com.loopec.loopkey.server.corp.persistence.entity;
import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EmbeddableEntity.class)
public abstract class EmbeddableEntity_ {
public static volatile SingularAttribute<EmbeddableEntity, EntityB> entityB;
public static volatile SingularAttribute<EmbeddableEntity, Boolean> booleanField;
public static final String ENTITY_B = "entityB";
public static final String BOOLEAN_FIELD = "booleanField";
}
But all it generates for me is a class like this, omitting the entityB field I want generated:
package br.com.loopec.loopkey.server.corp.persistence.entity;
import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EmbeddableEntity.class)
public abstract class EmbeddableEntity_ {
public static volatile SingularAttribute<EmbeddableEntity, Boolean> booleanField;
public static final String BOOLEAN_FIELD = "booleanField";
}
I don't know it's useful information, but I've been facing this problem building the project from Docker.
My version of hibernate is: 5.4.15.Final
Can you see if I did something wrong with my code? I don't know if it's a hibernate bug, but I found a thread on stack overflow with another developer having a similar problem. JPA Hibernate 5: OneToOne in nested Embeddable causes metamodel issue
I wish to retrieve the information contained in the database thanks to my DTO class.
The problem is that my query doesn't work without me understanding why...
Entity from database
#Entity
#Table(name = "historiquedeploiement")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class HistoriqueDeploiement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "idnamespace", nullable = false)
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("idnamespace")
private Namespace namespace;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "idservice", nullable = false)
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("idservice")
private Service service;
#NotEmpty
#Size(max = 100)
private String tagvalue;
}
DTO :
#Data
#NoArgsConstructor
#AllArgsConstructor
public class HistoriqueDeploiementReadingDTO {
private Integer id;
#NotEmpty
private String namespacename;
#NotEmpty
private String servicename;
#NotEmpty
private String tagvalue;
}
My Query :
#Repository
public interface HistoriqueDeploiementRepository extends JpaRepository<HistoriqueDeploiement, Integer> {
List<HistoriqueDeploiement> findAll();
// Problem Here
Error creating bean with name 'historiqueDeploiementRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.example.jpa.repository.HistoriqueDeploiementRepository.findAllDeploiement()!
#Query("SELECT new com.example.jpa.dto.HistoriqueDeploiementReadingDTO(historiquedeploiement.id, namespace.namespacename, service.servicename, historiquedeploiement.tagvalue) FROM historiquedeploiement, namespace, service WHERE namespace.id = historiquedeploiement.idnamespace and service.id = historiquedeploiement.idservice")
List<HistoriqueDeploiementReadingDTO> findAllDeploiement();
}
My goal is to have this query working :)
If you think you have a better idea than solving this problem let me know !
Thanks
Your HistoriqueDeploiement entity is missing the #Entity:
#Entity
public class HistoriqueDeploiement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
... the rest of the class
}
Without that tag Spring-boot ORM does not know that the class represents an entity and can't perform queries on it.
Here you can find the explanation on the docs about the #Entity tag:
The Customer class is annotated with #Entity, indicating that it is a JPA entity. (Because no #Table annotation exists, it is assumed that this entity is mapped to a table named Customer.)
The solution that is working on my side is this one !
package com.example.jpa.services.historiquedeploiement;
import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import com.example.jpa.repository.HistoriqueDeploiementRepository;
import com.example.jpa.dto.HistoriqueDeploiementReadingDTO;
import com.example.jpa.model.HistoriqueDeploiement;
#Service
#Configuration
public class MapService {
#Autowired
private HistoriqueDeploiementRepository historiqueDeploiementRepository;
#Autowired
private ModelMapper modelMapper;
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
return modelMapper;
}
public List<HistoriqueDeploiementReadingDTO> getAllHistorique() {
return ((List<HistoriqueDeploiement>) historiqueDeploiementRepository
.findAll())
.stream()
.map(this::convertToHistoriqueDeploiementReadingDTO)
.collect(Collectors.toList());
}
private HistoriqueDeploiementReadingDTO convertToHistoriqueDeploiementReadingDTO(HistoriqueDeploiement historiqueDeploiement) {
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.LOOSE);
HistoriqueDeploiementReadingDTO historiqueDeploiementReadingDTO = modelMapper
.map(historiqueDeploiement, HistoriqueDeploiementReadingDTO.class);
return historiqueDeploiementReadingDTO;
}
}
I've two entities which are named Airport and Route. Route has two field which named startPoint and endPoint. Both of them will be id value of Airport entity. I'm adding two airport entity, after that, I want to add Route by using id values of these airport records. I got an error like that
"message": "JSON parse error: Cannot construct instance of com.finartz.airlines.entity.Airport (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.finartz.airlines.entity.Airport (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1)\n at [Source: (PushbackInputStream); line: 2, column: 18] (through reference chain: com.finartz.airlines.entity.Route[\"startPoint\"])"
these are my entities:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
#Data
#Builder
#Entity
#Table(name = "airport")
#NoArgsConstructor
#AllArgsConstructor
#EntityListeners(AuditingEntityListener.class)
public class Airport implements Serializable {
private static final long serialVersionUID = -3762352455412752835L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "code")
private String code;
#Column(name = "name")
private String name;
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
#Column(name = "description")
private String description;
#CreationTimestamp
#Column(name = "created_on", nullable = false, updatable = false)
private LocalDateTime createdOn;
#UpdateTimestamp
#Column(name = "updated_on")
private LocalDateTime updatedOn;
}
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
#Data
#Builder
#Entity
#Table(name = "route")
#NoArgsConstructor
#AllArgsConstructor
#EntityListeners(AuditingEntityListener.class)
#ApiModel(value = "route")
public class Route implements Serializable {
private static final long serialVersionUID = -8451228328106238822L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_start_point",referencedColumnName = "id")
private Airport startPoint;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_end_point",referencedColumnName = "id")
private Airport endPoint;
#CreationTimestamp
#Column(name = "created_on", nullable = false, updatable = false)
private LocalDateTime createdOn;
#UpdateTimestamp
#Column(name = "updated_on")
private LocalDateTime updatedOn;
}
And repository of the Route is below:
import com.finartz.airlines.entity.Route;
import com.finartz.airlines.repository.RouteRepository;
import com.finartz.airlines.util.HibernateUtil;
import lombok.AllArgsConstructor;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
#AllArgsConstructor
public class RouteRepositoryImpl implements RouteRepository {
public Long add(Route route){
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx;
Long routeId;
tx = session.beginTransaction();
routeId = (Long) session.save(route);
tx.commit();
session.close();
return routeId;
}
}
How can I add new Route by using request which provided below?
{
"startPoint":1,
"endPoint":2
}
I would use the session's entity manager to get a reference to an airport from the DB.
Construct using this code whenever you want to create a Route this way:
EntityManagerFactory emf = session.getEntityManagerFactory();
EntityManager em = emf.createEntityManager();
Airport startPoint = em .getReference(Airport.class, startPointID);
Airport endPoint = em .getReference(Airport.class, endPointID);
It should be something like this
session.beginTransaction();
session.save(route);
tx.commit();
Long id = route.getId();
session.close();
return id;
You should not use begin/close transaction. Use Spring Data JPA, it manage transaction session automatically. Your way is not best practices since you use Spring Boot (Let's see https://github.com/donhuvy/library/blob/master/src/main/java/com/example/library/controller/BookController.java#L29 very simple and easy).
I have an entity called Recipe and I created a Repository which extends JpaRepository and I would like to search up the recipes which contains every element of the search array for dietLabelList.
#Getter
#Setter
#Entity
#NoArgsConstructor
#RequiredArgsConstructor
#ToString
#Table(name = "recipe")
public class Recipe {
#Column(name = "id", nullable = false)
#Id
#GeneratedValue
private UUID rid;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "dietLabelList", nullable = false)
private UUID[] dietLabelList;
}
#Repository
public interface RecipeRepository extends JpaRepository<Recipe, UUID> {
List<Recipe> findByTitleContaining(String title);
List<Recipe> findByDietLabelList(UUID[] dietLabels);
}
e.g. I have a recipe that has as a dietLabelList like this one ["Balanced", "High-Fiber", "High-Protein"] and findByDietLabelList(["Balanced", "High-Fiber"]) should be able to find it. Is something like possible with JpaRepository?
You can use QueryParam and specify your custom Query
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
#Repository
public interface RecipeRepository extends JpaRepository<Recipe, UUID> {
List<Recipe> findByTitleContaining(String title);
#Query(value = "SELECT r FROM Recipe r WHERE r.dietLabels in :dietLabels")
List<Recipe> findByDietLabelList(#Param("dietLabels") UUID[] dietLabels);
}