I'm trying to create a post API in a spring boot app but it's not working and i can't find the issue in the code
This is service:
#Transactional
public Invoice saveInvoice(Invoice invoice) {
Invoice newInvoice = invoiceRepository.save(invoice);
return newInvoice;
}
This is the controller:
#PostMapping("/save")
public ResponseEntity<Invoice> addInvoice(#RequestBody InvoiceDTO invoice) {
try {
Invoice newInvoice = invoiceService
.saveInvoice(new Invoice(invoice.getSerialNumber(), invoice.getStatus(), invoice.getCreatedDate()));
return new ResponseEntity<>(newInvoice, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
and this is the model :
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "status")
private String status;
#Column(name = "created_date")
private Timestamp createdDate;
#Column(name = "is_deleted")
private boolean isDeleted;
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
#ManyToOne
#JoinColumn(name = "employee_id")
private Employee employee;
#OneToMany
private Set<InvoiceHistory> invoiceHistories;
#ManyToMany
private Set<Item> items;
public Invoice(long serialNumber, String status, Timestamp createdDate) {
this.serialNumber = serialNumber;
this.status = status;
this.createdDate = createdDate;
this.isDeleted = false;
}
}
But when I am running in in postman its returning 500 Internal Server Error
Update error message :
, message=Cannot invoke
"com.example.invoices.repository.IInvoiceRepository.save(Object)"
because "this.invoiceRepository" is null, path=/invoice/save}]
where's the problem ?
For your service class, how are you declaring and creating invoiceRepository? If it's like this:
private InvoiceRepository invoiceRepository;
Then you need to add an #Autowired:
#Autowired
private InvoiceRepository invoiceRepository;
If you do have it #Autowired, make sure your service class is annotated with #Service:
#Service
public class InvoiceService {...}
If you have that, make sure you aren't creating InvoiceService with "new" in your controller. Instead of:
private InvoiceService invoiceService = new InvoiceService();
or "new"ing it in a constructor,
use #Autowire
#Autowire
private InvoiceService invoiceService;
Related
the application uses java sprin boot framework as backend.
In the class Produit.java there are two foreign keys:
Categorie.java and Laboratoire.java
and they are both created in the database.
Produit.java
`
#Entity
#Data
public class Produit
{
#Id #GeneratedValue
private Long id;
#ManyToMany(mappedBy = "produits") #JsonIgnore #OnDelete(action = OnDeleteAction.CASCADE)
private List<Facture> factures;
#ManyToMany(mappedBy = "produits") #JsonIgnore #OnDelete(action = OnDeleteAction.CASCADE)
private List<Utilisateur> utilisateurs;
#ManyToOne #JsonIgnore
private Categorie categorie;
#ManyToOne #JsonIgnore
private Laboratoire laboratoire;
#OneToMany(mappedBy = "produit") #OnDelete(action = OnDeleteAction.CASCADE) #JsonIgnore
private List<Commande> commandes;
private String lib;
private String description;
private Double prix;
#DateTimeFormat(pattern = "dd-MM-yyyy")
#JsonFormat(pattern = "dd-MM-yyyy")
private Date dateAjout;
private Float prixLiv;
}
`
Categorie.java
`
#Entity
#Data
public class Categorie
{
#Id #GeneratedValue
private Long id;
#OneToMany(mappedBy = "categorie") #JsonIgnore #OnDelete(action = OnDeleteAction.CASCADE)
private List<Produit> produits;
private String lib;
}
`
Laboratoire.java
`
#Entity
#Data
public class Laboratoire
{
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "laboratoire")
#OnDelete(action = OnDeleteAction.CASCADE) #JsonIgnore
private List<Produit> produits;
private String lib;
}
`
here are these objects in angular
Produit.ts
`
export class Produit
{
id?:number
lib?:string
description?:string
categorie?:Categorie
laboratoire?:Laboratoire
dateAjout?:Date
prix?:number
prixLiv?:number
}
`
But when using the http post method it returns the object without the foreign keys
console outupt
i tryed to change the attribute names but it didn't work
ProduitService.java
`
#Service
public class ProduitService
{
#Autowired
ProduitRepository pr;
#Transactional
public List<Produit> findAll() { return pr.findAll(); }
#Transactional
public Produit add(Produit produit) { return pr.save(produit); }
}
`
ProduitController.java
`
#RestController
#RequestMapping("produits")
#CrossOrigin(origins = "http://localhost:4200")
public class ProduitController
{
#Autowired
ProduitService ps;
#GetMapping("findAll")
public List<Produit> findAll() { return ps.findAll(); }
#PostMapping("add")
public Produit add(#RequestBody Produit produit) { return ps.add(produit); }
}
`
I have a simple API, built with Spring Boot, where I am trying to convert entity classes to the corresponding dtos.
Student Entity
#Entity(name = "students")
#AllArgsConstructor
#Getter
#Setter
public class Student extends AbstractUpdatable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "student_id")
private Long id;
#Column(name = "student_first_name", nullable = false)
#NotNull(message = "First name is mandatory")
private String firstName;
#Column(name = "student_last_name", nullable = false)
#NotNull(message = "Last name is mandatory")
private String lastName;
#Column(name = "student_ssn", unique = true, nullable = false)
#NotNull(message = "SSN is mandatory")
#Size(min = 10, max = 10)
private String ssn;
#Column(name = "student_age")
#Min(5)
#Max(100)
private Integer studentAge;
#Column(name = "student_email")
#Email
private String email;
#Column(name = "student_level")
private Integer studentLevel; // TODO could be enum or separate entity
#Column(name = "student_creation_date")
private Date creationDate; // TODO check spring's feature to manage creation and update dates (auditing)
#ManyToOne
#JoinColumn(name = "group_id")
private Group group;
public Student() {
this.creationDate = new java.util.Date(); // TODO this will be removed when spring's auditing is utilized
}
}
Group Entity
#Entity(name = "groups")
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Group extends AbstractUpdatable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private Long id;
#Column(name = "group_name")
private String Name;
#OneToMany(mappedBy = "group")
#JsonIgnore
private List<Student> students;
}
Student DTO
public class StudentDto extends AbstractStudentDto implements Serializable {
private final Long id;
private final Date creationDate;
public StudentDto(String firstName, String lastName, String email, Long id, GroupDto group, Integer studentAge, Integer studentLevel,
Date creationDate) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.group = group;
this.studentAge = studentAge;
this.studentLevel = studentLevel;
this.creationDate = creationDate;
}
public Long getId() {
return id;
}
public Date getCreationDate() {
return creationDate;
}
}
Group DTO
public class GroupDto extends AbstractGroupDto{
private final Long id;
public GroupDto(Long id, String name, List<StudentDto> students) {
this.id = id;
this.name = name;
this.students = students;
}
public Long getId() {
return id;
}
}
GroupToGroupDtoConverter
#Component
public class GroupToGroupDtoConverter implements org.springframework.core.convert.converter.Converter<Group, GroupDto> {
private final ConversionService conversionService;
#Autowired
public GroupToGroupDtoConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
#Override
public GroupDto convert(Group source) {
var convertedStudents = new ArrayList<StudentDto>();
if (!CollectionUtils.isEmpty(source.getStudents())) {
source.getStudents().forEach(student ->
convertedStudents.add(conversionService
.convert(student, StudentDto.class)));
}
return new GroupDto(source.getId(), source.getName(), convertedStudents);
}
}
And a very similar StudentToStudentDtoConverter.
The issue is that when the code needs to do the conversion from any of the entities to their dtos I get
org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type
[com.studentmanagement.model.Group] to type
[com.studentmanagement.dto.group.GroupDto]
Now if I try to remove the conversion of the students' list to a list of student dtos in the converter above, so the converter looks like this:
#Component
public class GroupToGroupDtoConverter implements org.springframework.core.convert.converter.Converter<Group, GroupDto> {
#Override
public GroupDto convert(Group source) {
return new GroupDto(source.getId(), source.getName(), new ArrayList<StudentDto>());
}
}
The conversion works with no issues (with a dummy students list of course). Am I missing something when I am adding the conversion service inside my converters?
I tried to replicate the issue and did a small working demo on this.
One thing I found while doing that, that could be relevant to your case, is that injecting a conversion service into a converter is not trivial (see e.g. this and this relevant issues).
Also, important as well, do not forget to register the converters, as shown below on the code samples, and e.g. here. From the error message you posted, seems like the service cannot find the needed converter.
For my demo, please note I removed the group field from the StudentDTO class to simplify things. I hope it helps, happy to share the full code as well in github.
I used the following converter for Group:
#Component
public class GroupToGroupDtoConverter implements org.springframework.core.convert.converter.Converter<Group, GroupDto> {
private ConversionService conversionService;
public GroupToGroupDtoConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
#Override
public GroupDto convert(Group group) {
List<StudentDto> studentDtoList =
group.getStudents().stream().map(a -> conversionService.convert(a, StudentDto.class)).collect(Collectors.toList());
GroupDto groupDto = new GroupDto(group.getId(), group.getName(), studentDtoList);
return groupDto;
}
}
But in order to successfully inject the conversion service and register the converters I added this:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Autowired
#Lazy
ConversionService conversionService;
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new GroupToGroupDtoConverter(conversionService));
registry.addConverter(new StudentToStudentDtoConvervter());
}
}
If, for example, I comment out the first addConverter line, I get the Converter not found exception:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.example.conversion_entities.Group] to type [com.example.conversion_entities.GroupDto]
Once all is done, the following test passes:
#RunWith(SpringRunner.class)
#SpringBootTest
class GroupToGroupDtoConverterTest {
#Autowired
ConversionService conversionService;
#Test
void convert() {
Student studentA = new Student();
studentA.setFirstName("John");
studentA.setLastName("Doe");
Student studentB = new Student();
studentB.setFirstName("Jane");
studentB.setLastName("Doe");
List<Student> studentList = new ArrayList<>();
studentList.add(studentA);
studentList.add(studentB);
Group group = new Group(1L, "groupA", studentList);
GroupDto convertedGroupDto = conversionService.convert(group, GroupDto.class);
assertEquals("John", convertedGroupDto.getStudents().get(0).getFirstName());
assertEquals("Jane", convertedGroupDto.getStudents().get(1).getFirstName());
}
}
I think the problem is ConversionService can't convert your classes by default. Instead try to inject a class implementing ConversionService with correct convert method implementation inside.
I am making a small Rest API using Spring boot and mysql as database. I am getting "No serializer found" error with fetch type lazy. My application is working fine with fetch type eager but I want fetch type to lazy so how can I resolve this.
User Model:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(nullable = false,length = 50)
private String firstName;
#Column(nullable = false,length = 50)
private String lastName;
#Column(nullable = false,unique = true)
private String email;
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "address_id",referencedColumnName = "id")
#JsonIgnoreProperties("user")
private Address address;
}
Address Model:
#Entity
#Table(name = "addresses")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(nullable = false,length = 255)
private String street;
#Column(nullable = false)
private int postalCode;
#Column(nullable = false,length = 100)
private String city;
#OneToOne(mappedBy = "address",fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JsonIgnoreProperties("address")
private User user;
}
User Service:
#Service
public class UserService {
private final UserRepository userRepository;
#Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User save(User user){
return userRepository.save(user);
}
public List<User> find(){
return userRepository.findAll();
}
public User find(Integer id){
return userRepository.findById(id).get();
}
public void delete(Integer id){
userRepository.deleteById(id);
}
}
User Controller:
#RestController
#RequestMapping(path = "users")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
public List<User> get(){
return userService.find();
}
#GetMapping(path = "{id}")
public User get(#PathVariable Integer id){
return userService.find(id);
}
#PostMapping
public User post(#RequestBody User user){
return userService.save(user);
}
#DeleteMapping(path = "{id}")
public boolean delete(#PathVariable Integer id){
userService.delete(id);
return true;
}
}
Stack Trace:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.kumasroh.usersapp.models.User["address"]->com.kumasroh.usersapp.models.Address$HibernateProxy$VM2Pif4w["hibernateLazyInitializer"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.13.1.jar:2.13.1]
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300) ~[jackson-databind-2.13.1.jar:2.13.1]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.13.1.jar:2.13.1]
I think fetchType Lazy is used in OneToMany relationship on the otherhand fetchType Eager is used in OneToOne Relationship.
i am new in spring boot and i could not find solution for this for a day now.
#GetMapping used to retrive item gives a responce of infinite loop of foreignkey object "user".
why am i getting this infinite loop?
how to fix it?
user object in infinite loop(the problem)
result that i want
item entity
#Entity
public class Item{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long ItemId;
#ManyToOne
#JoinColumn(name = "owner_id")
private User user;
private String ItemName;
// #Column(columnDefinition="text")
private String Description;
private double Price;
private int AvailableQuantity;
private double shippingWeight;
// #Transient
// private MultipartFile Picture;
#Enumerated(value = EnumType.STRING)
private Category category;
#OneToMany(mappedBy = "item")
#JsonIgnore
private List<CartItem> CartItemList;
}
user entity
#Entity
#Table(name = "Utilisateur")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idU;
private String username;
private String password;
private String firstname;
private String lastname;
private String gender;
private Long phone;
private String adress;
#Temporal(TemporalType.DATE)
private Date dateofbirth;
private int rating;
private String email;
public Role role;
private Integer status;
#OneToMany(mappedBy = "user")
private List<Item> ItemList;
}
item service
#Service
public class ItemService implements ItemServiceInterface{
#Autowired
ItemRepository itemrepository;
public Optional<Item> getItemById(long id){
return itemrepository.findById(id);
}
}
item controller
#RestController
public class ItemControl {
#Autowired
ItemServiceInterface itemservice;
#GetMapping("/getitem/{id}")
public Optional<Item> getitembyid(#PathVariable Long id) {
return itemservice.getItemById(id);
}
}
You can use combination of #JsonManagedReference and #JsonBackReference to discourage Jackson from infinite serialization.
#Entity
#Table(name = "Utilisateur")
public class User {
// omitted
#JsonManagedReference
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Item> ItemList;
}
#Entity
public class Item{
// omitted
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "owner_id")
private User user;
}
More details could be found here https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can make use of lazy loading to cut the dependency loop between user and item. However, following that approach might potentially affect other parts of your projects because other codes might use the entity with an assumption that item list in user entity is already eager fetched.
A better way is not return the entity object directly to the REST response. You can define a data model for the rest response and convert the entity to that model in your service class. This way, you can completely control what to return and not to.
Another approach if you still want to use the entity as response: https://www.baeldung.com/spring-data-jpa-named-entity-graphs. This way, you can define when to use the lazy load with each specific query.
I started building my first REST webservice in Java using Spring and JPA.
Now I'm trying to create sign-up service. I have no problem with sending a request containing all Entity fields what looks:
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
#Entity
#Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Enumerated(EnumType.STRING)
private Gender gender;
#Column(name = "email")
private String email;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "registration_date")
#CreatedDate
private LocalDateTime registrationDate;
#OneToMany(mappedBy = "bookOwner", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Book> bookList = new ArrayList<>();
}
But what to do in situation I want my registration form having only login, password and email fields and filling the rest user details would be optional - after confirmation of registration?
I consider using ModelMapper and creating separate classes for every form, but is there any better approach?
I solved problem by my own using mentioned ModelMapper. I paste my code. Can be useful if someone's interested. Didn't make tests, but my DB looks fine and no exceptions are thrown.
public class DTOMapper {
private static final ModelMapper MAPPER = new ModelMapper();
private DTOMapper(){}
public static <S, T> T map(S source, Class<T> targetClass){
return MAPPER.map(source, targetClass);
}
}
#Service
#Transactional
public class SignUpService {
private final UserRepository userRepository;
#Autowired
public SignUpService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User registerUser(SignUpForm form){
if(userRepository.findByLogin(form.getLogin())!=null){
throw new LoginAlreadyUsedException(form.getLogin());
}
if(userRepository.findByEmail(form.getEmail())!=null){
throw new EmailAlreadyUsedException(form.getEmail());
}
User user = DTOMapper.map(form, User.class);
User saved = userRepository.save(user);
return DTOMapper.map(saved, User.class);
}
}
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class SignUpForm implements Serializable {
private static final long serialVersionUID = 1L;
#NotEmpty
#Size(min = 5)
private String login;
#NotEmpty
#Size(min = 7)
private String password;
//todo email validation
#NotEmpty
private String email;
}
#RestController
public class SignUpController {
private static final Logger log = LoggerFactory.getLogger(SignUpController.class);
#Autowired
private SignUpService signUpService;
#PostMapping(value = "/signup")
public ResponseEntity<?> addUser(#RequestBody #Valid SignUpForm form, BindingResult errors){
if(errors.hasErrors()){
throw new InvalidRequestException(errors);
}
signUpService.registerUser(form);
return new ResponseEntity<>(form, HttpStatus.CREATED);
}
}