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?
Related
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
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 have a users table which contains the user details. i also have a authorities table which has the role of a user. The user and authorities table has one to many mapping. When i try to save the details using Jpa the foreign key column is blank no data is inserted in that field. i have a form in which i am specifying the role of the user along with other details.
package com.example.StarsProject.Model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
#Entity
#Table
#Getter
#Setter
public class Authorities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String role;
#ManyToOne(cascade = CascadeType.PERSIST,fetch=FetchType.EAGER)
#JoinColumn(name = "users_id", referencedColumnName = "id")
private Users users;
public Authorities(String role){
this.role = role;
}
}
package com.example.StarsProject.Model;
import com.example.StarsProject.DTO.UserDTO;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table
#Getter
#Setter
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "first_name")
private String firstname;
#Column(name = "last_name")
private String lastname;
#Column(unique = true)
private String email;
private String password;
#OneToMany(fetch = FetchType.EAGER,targetEntity = Authorities.class,mappedBy = "users", cascade = CascadeType.PERSIST)
private Set<Authorities> authorities;
public Users(UserDTO userDTO) {
this.email = userDTO.getEmail();
this.firstname = userDTO.getFirstname();
this.lastname = userDTO.getLastname();
this.password = userDTO.getPassword();
// Authorities authorities = new Authorities();
// authorities.setRole(userDTO.getRole());
// Set<Authorities> set = new HashSet<>();
// set.add(authorities);
this.authorities = new HashSet<Authorities>(Arrays.asList(new Authorities(userDTO.getRole())));
}
}
package com.example.StarsProject.Service;
import com.example.StarsProject.DTO.UserDTO;
import com.example.StarsProject.Model.Users;
import com.example.StarsProject.Repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserDetailsServiceImpl implements UserDetailsServiceInterface{
#Autowired
UserRepository userRepository;
#Override
public void storeUserDetails(UserDTO userDTO) {
Users users = new Users(userDTO);
userRepository.save(users);
}
}
When i try to save the user details it doesn't insert any value in the foreign key column. Can someone tell me what i am doing wrong.
You need to setusers field in Authorities manually. Hibernate won't fill it for you.
I have 2 models in my application which have the one-to-many and many-to-one relationships.
The model classes are:
Invoice Model
#Entity
#Data
public class Invoice implements java.io.Serializable {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
private String id;
private String business;
private String client;
private String invoiceNo;
#Enumerated(EnumType.STRING)
private InvoiceStatus status;
private String additionalInfo;
#OneToMany(mappedBy = "invoice")
private Set<InvoiceItem> items = new HashSet<>();
#CreationTimestamp
private LocalDateTime createdAt;
#UpdateTimestamp
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
InvoiceItem Model
#Entity
#Data
public class InvoiceItem {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
private String id;
#ManyToOne
#JoinColumn(name="invoice_id", nullable = false)
private Invoice invoice;
private String description;
#Enumerated(EnumType.STRING)
private InvoiceItemType type;
#CreationTimestamp
private LocalDateTime createdAt;
#UpdateTimestamp
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
I have an api endpoint which is used to create an invoiceItem for an invoice
#RestController
#RequestMapping(path="/api/v1/invoices/{invoice}/items")
public class InvoiceItemController {
#Autowired
private ModelMapper modelMapper;
#Autowired
private InvoiceItemService invoiceItemService;
#Autowired
private InvoiceService invoiceService;
#PostMapping
public ResponseEntity<InvoiceItem> addInvoiceItem(#PathVariable("invoice") String invoiceId, #RequestBody InvoiceItemCreationDto invoiceItemCreationDto) throws NotFoundException {
Optional<Invoice> invoiceOptional = invoiceService.findInvoiceById(invoiceId);
if (!invoiceOptional.isPresent()) {
throw new NotFoundException("Invoice not found");
}
InvoiceItem invoiceItem = modelMapper.map(invoiceItemCreationDto, InvoiceItem.class);
invoiceItem.setInvoice(invoiceOptional.get());
InvoiceItem savedInvoiceItem = invoiceItemService.addInvoiceItem(invoiceItem);
return new ResponseEntity<>(savedInvoiceItem, HttpStatus.CREATED);
}
}
Invoice Service
package com.spencerfeng.invoiceservice.services;
import com.spencerfeng.invoiceservice.models.Invoice;
import com.spencerfeng.invoiceservice.repositories.InvoiceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Optional;
public class InvoiceServiceImpl implements InvoiceService {
#Autowired
InvoiceRepository invoiceRepository;
#Override
public Invoice addInvoice(Invoice invoice) {
return invoiceRepository.save(invoice);
}
#Override
public Optional<Invoice> findInvoiceById(String id) {
return invoiceRepository.findById(id);
}
}
Invoice Repository
package com.spencerfeng.invoiceservice.repositories;
import com.spencerfeng.invoiceservice.models.Invoice;
import org.springframework.data.repository.CrudRepository;
public interface InvoiceRepository extends CrudRepository<Invoice, String> {
}
InvoiceItem Service
package com.spencerfeng.invoiceservice.services;
import com.spencerfeng.invoiceservice.models.InvoiceItem;
import com.spencerfeng.invoiceservice.repositories.InvoiceItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
public class InvoiceItemServiceImpl implements InvoiceItemService {
#Autowired
private InvoiceItemRepository invoiceItemRepository;
#Override
public InvoiceItem addInvoiceItem(InvoiceItem invoiceItem) {
return invoiceItemRepository.save(invoiceItem);
}
}
But when I call this api endpoint to create an invoiceItem for an invoice, the items property in the invoiceOptional has the 'Unable to evaluate the expression Method threw 'org.hibernate.exception.GenericJDBCException' exception, while other properties are fine.
You can try to exclude the circular tostring\hashcode reference on one POJO:
#ToString(exclude = "invoice")
#EqualsAndHashCode(exclude = "invoice")
#Entity
#Getter #Setter
public class InvoiceItem {
or to do it by using #EqualsAndHashCode.Exclude & #ToString.Exclude on the field
#Entity
#Data
public class InvoiceItem {
#ManyToOne
#EqualsAndHashCode.Exclude #ToString.Exclude
#JoinColumn(name="invoice_id", nullable = false)
private Invoice invoice;
I am learning spring boot data JPA.
Here is my code
Users.java
#Entity
#Table(name = "user_Details")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="user_id", referencedColumnName="user_id")
private Set<usersAction> usersAction;
usersAction.java
#Entity
#Table(name="user_Action")
public class usersAction {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="action_id")
private Integer Action_id;
#Column(name="user_id")
private Integer id;
#Column(name="users_Role")
private String usersRole;
UsersRepository.java
package com.demo.repository;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.demo.model.Users;
#Repository
public interface UsersRepository extends JpaRepository<Users, Integer> {
#Query(value="select u.password,u.user_id,u.name,ua.users_Role from user_details as u"
+ " inner join user_action as ua"
+ " on u.user_id=ua.user_id",nativeQuery=true)
Set<Users> findById();
}
UsersController.java
#RestController
#RequestMapping("/users")
public class usersController {
#Autowired
UsersRepository usersRepository;
#GetMapping("/all")
public List<Users> getAll() {
return usersRepository.findAll();
}
#RequestMapping("/byid")
public Set<Users> findByName()
{
Set<Users> obj1=usersRepository.findById();
return obj1;
}
}
When I am accessing this http://localhost:8080/users/byid
I am getting output as id ,name ,password from users_details table and also Action_id,id and users_role from users_Action
But I am expecting in Result only name from users_detail and usersRole from users_Action table
What i need to change for that?
You need to create interfaces like this
interface UsersSummary {
String getName();
String getName();
UsersActionSummary getUsersAction();
interface UsersActionSummary {
String getUsersRole();
Integer getId();
Integer getAction_Id();
}
}
And then change your repository method to return this
#Query(value="select u.password,u.user_id,u.name,ua.users_Role from user_details as u"
+ " inner join user_action as ua"
+ " on u.user_id=ua.user_id",nativeQuery=true)
Collection<UsersSummary> findById();