Spring Boot Jpa mapping - java

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();

Related

Creating entity with nested entity JPA

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?

How can I filter out a get request of all users in my DB so that users with an admin role are not shown in the list?

I am building a spring boot application that allows an admin to view a list of all users saved in my Database. However, at the moment, all users including the ones with an admin role are being displayed. I was suggested to filter out admins in the backend code but I am not sure how to go about doing this. If anyone could help me, it would very much appreciated. Thank you! (I am using JPA + Hibernate)
UPDATE:
I was suggested a solution that was very useful below. I needed to make a getUsers method which queries data from my Database in a way that filters out admins. At first, I was having issues with the SQL part of the query since I am using hibernate (I think, correct me if I am wrong). I was made aware that you can't use a JPQL query to access raw entity data from tables, since I was using #JoinTable to create my user_roles table that doesn't have an entity class for it, I was confused. Seen in my User class as follows:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
I was receiving a red line in my user repository class at user_role ur in FROM User u,Role r, user_role ur. Futhermore, I was receiving the following error: user_role is not mapped [SELECT u.id,u.username,u.email FROM com.Application.models.User u,com.Application.models.Role r, user_role ur WHERE u.id=ur.id AND r.id=ur.id AND r.name<>'ADMIN']. My Role entity is used to map the role ID and the role name whereas my user_role table contains a column of the user ID and the role ID in one table for mapping the user id to a role id. That is where my last error was.
My Solution:
#Query("SELECT u FROM User u join u.roles r WHERE NOT EXISTS" + "(SELECT r FROM u.roles WHERE r.name = 'ROLE_ADMIN')")
List<User> getUsers();
This succesfuly filtered out users with an admin role in my case. I hope this helps someone one day, I was stuck on this for a minute. Thanks for all the help!
User.java:
package com.Application.models;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
})
public class User {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
#NotBlank
#Size(max = 20)
private String username;
#Column(name = "email")
#NotBlank
#Size(max = 50)
#Email
private String email;
#NotBlank
#Size(max = 120)
private String password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user")
private Set<UserPost> userPosts = new HashSet<>();
public User(String username, String email
,String password) {
this.username = username;
this.email = email;
this.password = password;
}
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
TestController.java:
package com.Application.controller;
import com.Application.models.User;
import com.Application.repository.UserPostsRepository;
import com.Application.repository.UserProfileRepository;
import com.Application.repository.UserRepository;
import com.Application.security.jwt.JwtUtils;
import com.Application.security.services.UserDetailsService;
import com.Application.security.services.UserPostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
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;
#CrossOrigin(origins = "http://localhost:3000")
#RestController
#RequestMapping("/api/test")
public class TestController {
private static final String AUTH_HEADER = "authorization";
#Autowired
private final UserPostsRepository userPostRepo;
#Autowired
private final UserRepository userRepo;
#Autowired
private final UserProfileRepository userProfRepo;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserPostService userPostService;
#Autowired
JwtUtils jwtUtils;
public TestController(UserPostsRepository userPostRepo, UserRepository userRepo, UserProfileRepository userProfRepo) {
this.userPostRepo = userPostRepo;
this.userRepo = userRepo;
this.userProfRepo = userProfRepo;
}
#GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
#GetMapping("/user")
#PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess() {
return "User Content.";
}
#GetMapping("/mod")
#PreAuthorize("hasRole('MODERATOR')")
public String moderatorAccess() {
return "Moderator Board.";
}
#GetMapping("/admin")
#PreAuthorize("hasRole('ADMIN')")
public List<User> adminAccess(Model model) {
List<User> allUsers = userRepo.getUsers();
allUsers.forEach(user -> model.addAttribute("username", user.getUsername()));
return allUsers;
}
}
Role.java:
package com.Application.models;
import javax.persistence.*;
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
#Column(length = 20)
private ERole name;
public Role() {
}
public Role(ERole name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public ERole getName() {
return name;
}
public void setName(ERole name) {
this.name = name;
}
}
ERole.java:
package com.Application.models;
public enum ERole {
ROLE_USER,
ROLE_MODERATOR,
ROLE_ADMIN
}
UserRepository.java:
package com.Application.repository;
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.stereotype.Repository;
import com.Application.models.User;
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
User findUserById(Long id);
Optional<User> findByEmail(String email);
List<User> findAll();
#Query("SELECT u.id,u.username,u.email FROM User u,Role r, user_role ur WHERE u.id=ur.id AND r.id=ur.id AND r.name<>\'ADMIN\'") //Error at users u, roles r, user_roles ur even though the table names are right
List<User> getUsers();
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
RoleRepository.java:
package com.Application.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.Application.models.ERole;
import com.Application.models.Role;
#Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(ERole name);
}
There are better ways to do this but here is a simple way.Just add the below method in your repository interface
#Query("SELECT u.firstname,u.lastname,u.email FROM user u,role r,user_role ur WHERE u.id=ur.user_id AND r.id=ur.role_id AND r.name<>\'ADMIN\'")
List<User> getUsers();
getUsers() retuns a list of users except admin users.
Of course you can change the query depending on what columns are in user table and change user_roles with the name of the appropriate table in your database.

OneToOne with hibernate JPA

I'm trying to relate two tables
User and Address
A user has a single address, an address belongs to only one user. Keys are listed by the ID of an Address
so I create my address first and then I create a user and link it with an address id
But I can't do it at all, I have the following error in return:
Error creating bean with name 'entityManagerFactory' defined in class path resource [org / springframework / boot / autoconfigure / orm / jpa / HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException: Cannot invoke "org.hibernate.mapping.PersistentClass.getTable ()" because "classMapping" is null
i'm totally new to hibernate but i need this project for college so forgive me for the ignorance on the subject
Thats my code:
USER/USUARIO Class:
import org.hibernate.validator.constraints.br.CPF;
import javax.persistence.*;
import javax.validation.constraints.*;
public class Usuario{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
#NotNull
#Size(min = 5,max = 30)
#Pattern(regexp = "^[a-zA-Z\s]*$", message = "Nome inválido! Digite apenas letras e espaçamento") //Permite apenas letras e espaço
private String nome;
#NotNull
#CPF
private String cpf;
#NotNull
#Email
private String email;
#NotNull
#Size(min = 5,max = 12)
private String senha;
private Integer telefone;
#DecimalMin("0")
#DecimalMax("5")
private Double avaliacao;
#NotNull
#OneToOne(cascade = CascadeType.ALL,mappedBy = "id")
private Endereco endereco;
//Atributos para usuários autônomos
private Boolean isAutonomo;
private String categoriaAutonomo;
private Double precoAutonomo;
//Getters and Setters
ADRESS/ENDERECO Class
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
#Table(name = "endereco")
public class Endereco {
#Id
#OneToOne
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
#Size(min = 8,max = 8)
private String cep;
#NotNull
private String bairro;
#NotNull
private String logradouro;
#NotNull
private Integer numeroLogradouro;
private String complemento;
#NotNull
#Size(min = 2,max = 2)
private String uf;
#NotNull
private String cidade;
CONTROLLER
import br.com.bandtec.projetocaputeam.dominio.*;
import br.com.bandtec.projetocaputeam.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
#RestController
#RequestMapping("/caputeam")
public class CaputeamController {
#Autowired
private UsuariosRepository usuariosRepository;
#Autowired
private EnderecoRepository enderecoRepository;
//--- USERS
#GetMapping("/usuarios")
public ResponseEntity getUsuarios(){
List<Usuario> usuarios = usuariosRepository.findAll();
return !usuarios.isEmpty() ? ResponseEntity.status(200).body(usuarios) :
ResponseEntity.status(204).build();
}
#PostMapping("/cadastrar-usuario")
public ResponseEntity cadastrarUsuario(#RequestBody #Valid Usuario novoUsuario){
usuariosRepository.save(novoUsuario);
return ResponseEntity.ok().build();
}
//--- ADRESS
#PostMapping("/cadastrar-endereco")
public ResponseEntity cadastrarEndereco(#RequestBody #Valid Endereco novoEndereco){
enderecoRepository.save(novoEndereco);
return ResponseEntity.ok().build();
}
}
APPLICATION
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ProjetoCaputeamApplication {
public static void main(String[] args) {
SpringApplication.run(ProjetoCaputeamApplication.class, args);
}
}
And thats my Logic Model
EDIT
I tried to delete the "mapped by" part and remove the #OneToOne from Address but now it returns the following error when I try to send an POST of Adress:
org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation: "FKMXNOON0IKGA83W1A203Y6OFPN: PUBLIC.ENDERECO FOREIGN KEY(ID) REFERENCES PUBLIC.USUARIO(ID) (1)"; SQL statement:
insert into endereco (bairro, cep, cidade, complemento, logradouro, numero_logradouro, uf, id) values (?, ?, ?, ?, ?, ?, ?, ?) [23506-200]
as if he didn’t enter any Address fields
Im sending my POST by Postman like this:
{
"bairro": "Vila Prmavera",
"cep": "03388110",
"cidade": "São Paulo",
"complemento": "b1",
"logradouro": "Rua das dores",
"numeroLogradouro": 7,
"uf": "SP"
}
Don't map on the Id. Map means entity mapping not id mapping.
public class Endereco {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToOne
private Usuario usuario
....
}
Or if you don't want Endereco to hold a reference to a Usuario just remove it. But you can't place #OneToOne on the id field. If you have only on one side the #OneToOne then you need also the annotation #MapsId.
public class Usario {
#NotNull
#MapsId
#OneToOne(cascade = CascadeType.ALL)
private Endereco endereco;
public class Endereco {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id
}
Because #OneToOne tries to Map with an entity which means to a table in Database. For id there isn't any entity or Table in the database. That is why it complains

Load single entity without relations jpa

I have two entities students and grades. There is a one to many relationship between them. But when I make an api call to get students, I get all grades with them. Is there a way to load only student entity ? I tried FetchType.LAZY but it did not work.
Student model:
#Entity
#Table
public class Student {
#Id
#GeneratedValue(
strategy= GenerationType.AUTO,
generator="native"
)
#GenericGenerator(
name = "native",
strategy = "native"
)
private Long id;
#NotBlank(message = "Name cannot be null")
private String name;
#NotBlank(message = "Lastname cannot be null")
private String lastname;
#NotNull(message = "Age cannot be null")
private int age;
#NotBlank(message = "Email cannot be null")
private String email;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "student")
private Set<Grade> grades = new HashSet();
}
Grade model:
#Entity
#Table
public class Grade {
#Id
#GeneratedValue(
strategy= GenerationType.AUTO,
generator="native"
)
#GenericGenerator(
name = "native",
strategy = "native"
)
private Long id;
private String subject;
private double value;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id", nullable = false)
private Student student;
Student service :
#Service
public class StudentService {
private final IStudentRepository studentRepository;
public StudentService(IStudentRepository studentRepository){
this.studentRepository = studentRepository;
}
public List<Student> GetAll(){
return studentRepository.findAll();
}
Hibernate output:
Hibernate: select student0_.id as id1_1_, student0_.age as age2_1_, student0_.email as email3_1_, student0_.lastname as lastname4_1_, student0_.name as name5_1_ from student student0_
Hibernate: select grades0_.student_id as student_4_0_0_, grades0_.id as id1_0_0_, grades0_.id as id1_0_1_, grades0_.student_id as student_4_0_1_, grades0_.subject as subject2_0_1_, grades0_.value as value3_0_1_ from grade grades0_ where grades0_.student_id=?
To load only the student entity, you can create a separate projection like StudenDTO and use it to pass across repo-service-controller.
The relevant part of projection is here
interface StudentDTO {
String getName();
String getLastname();
Integer getAge();
String getEmail();
}
Now when you hit localhost:8080/students/create-dto-return?id=1
You won't see the next query being fired and this is triggered jackson during serialization.
Entire code is as below:
package com.example.demo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;
#RestController
#RequestMapping("/students")
public class StudentsController {
private final StudentService studentService;
#Autowired
public StudentsController(StudentService studentService) {
this.studentService = studentService;
}
#GetMapping
public Iterable<Student> list() {
return studentService.list();
}
#PostMapping
public Student createEntityReturn(#RequestBody Student student) {
return studentService.save(student);
}
#GetMapping(value = "/create-dto-return")
public StudentDTO getByDto(#RequestParam("id") Integer id) {
return studentService.findStudentOnlyByIdDto(id);
}
#GetMapping(value = "/create-entity-return")
public Student getById(#RequestParam("id") Integer id) {
return studentService.findStudentById(id);
}
}
#Getter
#Setter
#ToString
#Entity
class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotBlank(message = "Name cannot be null")
private String name;
#NotBlank(message = "Lastname cannot be null")
private String lastname;
#NotNull(message = "Age cannot be null")
private int age;
#NotBlank(message = "Email cannot be null")
private String email;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "student")
private Set<Grade> grades = new HashSet<>();
}
#Getter
#Setter
#Entity
#Table
class Grade {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String subject;
private double value;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id", nullable = false)
private Student student;
}
#Service
class StudentService {
private final StudentRepository studentRepository;
#Autowired
StudentService(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
#Transactional
public Student save(Student student) {
return studentRepository.save(student);
}
#Transactional(readOnly = true)
public Iterable<Student> list() {
return studentRepository.findAll();
}
#Transactional(readOnly = true)
public StudentDTO findStudentOnlyByIdDto(Integer id) {
return studentRepository.findStudentById(id);
}
#Transactional(readOnly = true)
public Student findStudentById(Integer id) {
return studentRepository.findById(id).orElseThrow(() -> new RuntimeException("Unable to find student"));
}
}
interface StudentDTO {
String getName();
String getLastname();
Integer getAge();
String getEmail();
}
#Repository
interface StudentRepository extends JpaRepository<Student, Integer> {
StudentDTO findStudentById(Integer id);
}

Unable to evaluate the expression Method threw 'org.hibernate.exception.GenericJDBCException' exception

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;

Categories