I am very new to Spring boot and this is my first spring dummy project where I'm trying to learn to save an array of objects in mysql-db.
Actually, I am trying to use the saveAll method from the crudRepository in my controller. But it's giving me an error no instance(s) of type variable(s) S exist so that Iterable<S> conforms to List<User>
My JSON looks like this:
[
{
"first_name": "Jack",
"last_name": "Jill",
"email": "jacjill#gmail.com",
"password": "passq3623"
},
{
"first_name": "John",
"last_name": "Doe",
"email": "john#gmail.com",
"password": "pass23"
}
]
This is my entity class
package com.example.testbatch.Entities;
import javax.persistence.*;
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
public String first_name;
public String last_name;
public String email;
public String password;
}
Here is my User Model Class
package com.example.testbatch.Models;
public class User {
public int id;
public String firstName;
public String lastName;
public String email;
public String password;
}
Here is my repository
package com.example.testbatch.Repositories;
import com.example.testbatch.Entities.UserEntity;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface UserRepository extends CrudRepository<UserEntity, Integer> {
#Override
List<UserEntity> findAll();
}
and here is my rest controller
package com.example.testbatch.Controllers;
import com.example.testbatch.Entities.UserEntity;
import com.example.testbatch.Models.User;
import com.example.testbatch.Repositories.UserRepository;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
#RestController
#RequestMapping("/api/v1/user")
public class UserController {
#Autowired
UserRepository userRepository;
#PostMapping("/register")
public void saveUsers(#RequestBody UserEntity userEntity) {
List<User> persistedUser = userRepository.saveAll(List.of(userEntity));
}
}
Can anyone please help me? This is ery first time I am usig spring boot
You have some problems about your code:
At first you have a DTO (User class), so, you should use this DTO inside your controller in saveUsers method and more importantly you pass an array of objects in your JSON, so you have to use a List not an Object as the parameter:
public void saveUsers(#RequestBody List<User> users) {...}
Another problem is you don't have any getters and setters inside you DTO (public class User) and Entity (public class UserEntity) classes and also you have to add a method inside your DTO class for mapping DTO to Entity:
public class User {
...
public static UserEntity dtoToEntity(User user){
UserEntity userEntity = new UserEntity();
userEntity.setEmail(user.getEmail());
userEntity.setPassword(user.getPassword());
userEntity.setFirst_name(user.getFirstName());
userEntity.setLast_name(user.getLastName());
return userEntity;
}
}
Also you must change your JSON properties to match your DTO's properties:
[
{
"firstName": "Jack",
"lastName": "Jill",
...
},
...
]
As the last one, change your controller saveUsers method as (saveAll method return Iterable not List):
#PostMapping("/register")
public void saveUsers(#RequestBody List<User> users) {
List<UserEntity> userEntities = new ArrayList<>();
for (User user : users) {
userEntities.add(dtoToEntity(user));
}
Iterable<UserEntity> persistedUser = userRepository.saveAll(userEntities);
}
Notice: As a point remember that because you have auto-generated id inside your Entity, so you don't need id property for your DTO, obviously because it's auto-generated and you don't need to get it from input;
The user_repo is returning a List of type UserEntity
while in yout controller you are asking for a List of type User
There is not mapping between UserEntity and User.
Related
I am Learning java-spring boot and i was able to store the normal data in db. but i wasn't able to store the complex json data.
the json data:
{
"id": 1,
"name": "Leanne Graham",
"address": {
"street": "Kulas Light",
"city": "Gwenborough"
}
}
User.java
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
#Entity
#Table(name = "User_tbl")
public class User {
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private Long id;
private String name;
#Embedded
#Column(name = "Address")
private Address address;
}
Address.java
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
#Embeddable
public class Address {
private String street;
private String city;
}
UserController.java
import com.example.JsonParser.model.User;
import com.example.JsonParser.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/list")
public Iterable<User> list() {
return userService.list();
}
#PostMapping("/add")
public User save(User user){
return userService.add(user);
}
}
UserRepository.java
import com.example.JsonParser.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
}
UserService.java
import com.example.JsonParser.model.User;
import com.example.JsonParser.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class UserService {
#Autowired
private UserRepository repo;
public Iterable<User> list() {
return repo.findAll();
}
public Iterable<User> save(List<User> users) {
return repo.saveAll(users);
}
public User add(User user) {
repo.save(user);
return user;
}
}
application.yml
spring:
h2:
console:
enabled: 'true'
datasource:
url: jdbc:h2:mem:Ramesh
jpa:
defer-datasource-initialization: 'true'
when i send the post request with json data
{
"id": 1,
"name": "Leanne Graham",
"address": {
"street": "Kulas Light",
"city": "Gwenborough"
}
}
i got response as
{
"id": 1,
"name": null,
"address": null
}
H2 db:
also, the db is empty what i should modify or add in order to store the data?
The first thing is that the data from the request is not reaching the Controller. If you put a breakpoint in your save method you can see that all the attributes inside the User will be null. So the first thing you need to do is to annotate the method argument with this #RequestBody, so automatically the JSON data will be converted to your Bean (given that the fields are having the same name).
#PostMapping("/add")
public User save(#RequestBody User user){
return userService.add(user);
}
Second thing is that both your User and Address class should be annotated with #NoArgsConstructor.
So once you've done both the issue will be solved, and data will be saved and retrieved properly.
As you have created Two Entity Classes. First, you have to save the Address and then take Address Instance and set it in the User Entity class and then save it.
OR
Make address as a string instead of Entity class and Try to Pass the data as JSON String to the controller so that I will save it as JSON String
public class User {
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private Long id;
private String name;
private String address;
}
{
"id": 1,
"name": "Leanne Graham",
"address": "{ "street": "Kulas Light", "city": "Gwenborough" }"
}
I have the following question. I want to rename fields in JSON response. In my DTO fields are defined in camal case. In response json a want to use separate words with underscores. I tried to use #JsonPropperty annotation but it is not working. My DTO class is below:
package com.example.demo.dto.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
#Data
public class UserResponseDto {
private String id;
private String name;
#JsonProperty("second_name")
private String secondName;
private String email;
private String login;
private String password;
#JsonProperty("date_of_birth")
private Date dateOfBirth;
#JsonProperty("date_of_registration")
private Date dateOfRegistration;
private Set<RoleResponseDto> roles = new HashSet<>();
}
And the response is still in camal case:
{
"id": "d6c0873b-166b-4d6f-90b4-91d9f0dfa0a0",
"name": "Simon",
"secondName": "Wilder",
"email": "myNewEmail#gmail.com",
"login": "QMY",
"password": "$2a$10$Lj3EctARwD34EInzmwsjjuTsKiOzKtI7Gf7pskUfxcPt1PNRDHF1m",
"dateOfBirth": "1991-12-21T00:00:00.000+00:00",
"dateOfRegistration": "2022-04-07T12:52:24.059+00:00",
"roles": [
{
"id": "a9cc27ba-532a-456a-a9b5-ad14052190f8",
"title": "ROLE_USER"
},
{
"id": "684bf3b6-721d-494c-9c66-caeee44933d2",
"title": "ROLE_ADMIN"
}
]
}
Do You have any ideas? Or it is a bug, or what? Everywhere it is written to use #JsonPropperty annotation.
So there are 2 ways to do what you want but first make sure you are using #ResponseBody on all your Controller methods or make sure your controller classes are using #RestController.
if you want the whole application to use the underscore case in the response body then add this to your configs:
Gson Method
#Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(createGsonHttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
#Bean
public Gson createGson() {
return new GsonBuilder().disableHtmlEscaping()
.serializeNulls()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
}
#Bean
public GsonHttpMessageConverter createGsonHttpMessageConverter() {
GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
gsonConverter.setGson(createGson());
return gsonConverter;
}
}
Reference
Jackson Method
spring.jackson.property-naming-strategy=SNAKE_CASE
Reference:
if you want it on a single DTO then you can use
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
Refercences:
I am new to java and springboot. I am trying to create one CRUD application using springboot.
I am using MySQL for storing the data.
Employee Model -
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "employees")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email_id")
private String emailId;
public Employee() {
}
public Employee(String firstName, String lastName, String emailId) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
}
Employee Repository -
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.raksh.springboot.model.Employee;
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
Employee Controller -
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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 com.raksh.springboot.model.Employee;
import com.raksh.springboot.repository.EmployeeRepository;
#CrossOrigin(origins = "http://localhost:3000/")
#RestController
#RequestMapping("/api/v1/")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
// get all employees
#GetMapping("/employees")
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
}
The above controller is giving me the result in the JSON array of objects form as shown below
[
{
"id": 1,
"firstName": "Tony",
"lastName": "Stark",
"emailId": "tony#gmail.com"
},
{
"id": 2,
"firstName": "Thor",
"lastName": "Odinson",
"emailId": "thor#asgard.com"
}
]
But I need the response in the below form
{
total_items: 100,
has_more: true,
employees : {
1 : {
"id": 1,
"firstName": "Raksh",
"lastName": "Sindhe",
"emailId": "raksh#gmail.com"
},
2: {
"id": 2,
"firstName": "Thor",
"lastName": "Odinson",
"emailId": "thor#asgard.com"
}
}
}
Really appreciate the help.
You should create EmployeeResponse model (change the name as you see fit).
Add the additional fields you require.
The total_items could be calculated using list.size().
For the other field, I would add an additional query to the database for counting the number of rows, for example by id column.
compare if it's more than 100 and set the field to true.
You can see example here: Does Spring Data JPA have any way to count entites using method name resolving?
If in the "findAll" method you don't limit to 100 rows and you actually get all the employees and then move all except 100, you can set this field without the additional count query.
Simply you need to encapsulate the results in a DTO class and pass it back with the response.
total_items - can be inferred by the size of the list returned by the repository.
has_more - If you are using findAll() with repository call then you would get all the employees in the DB. Otherwise, you might have to introduce pagination with the repository.
employees - Include employees in a Map
ResponseDTO
public class ResponseDTO {
private int total_items;
private boolean has_more;
private Map<Integer, Employee> employees;
public ResponseDTO(int totalItems, boolean hasMore, Map<Integer, Employee> employees) {
this.total_items=totalItems;
this.has_more=hasMore;
this.employees=employees;
}
//constructors, getters, and setters
}
EmpoyeeController
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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 com.raksh.springboot.model.Employee;
import com.raksh.springboot.repository.EmployeeRepository;
#CrossOrigin(origins = "http://localhost:3000/")
#RestController
#RequestMapping("/api/v1/")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
// get all employees
#GetMapping("/employees")
public List<Employee> getAllEmployees(){
Pageable employeePage = PageRequest.of(0, 100); //so you expect first 100 slice from all the employees in the DB.
Page<Employee> employees = employeeRepository.findAll(employeePage);
Map<Integer, Employee> employeeMap = getEmployeeMap(employees.getContent());
return new ResponseDTO(employees.getNumberOfElements(),employees.hasNext(),employeeMap );
}
private Map<Integer, Employee> getEmployeeMap(List<Employee> empoyees){
if(employees!=null && !empoyees.isEmpty){
Map<Integer, Employee> employeeMap = new HashMap<>();
for(Employee emp:empoyees){
employeeMap.put(emp.getId(),emp);
}
return employeeMap;
}
return null;
}
}
I'm trying to post body in RESTAPI using spring boot but I cannot. I suffered for long hours till now I also could not find the problem why what is the reason I do not know what mistake I made please help me.
Pojo class
Person.java
package com.thila.family.test;
import javax.annotation.Generated;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Person {
#Id
private int id;
private String familyName;
private String familyMembers;
private long contactNo;
public Person() {
}
public Person(int id,String familyName, String familyMembers,long contactNo) {
super();
this.familyName = familyName;
this.familyMembers = familyMembers;
this.contactNo = contactNo;
}
//getters setters
}
PersonService.java
package com.thila.family.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
#Service
public class PersonService {
private List<Person> persons=new ArrayList<>();
public void addPerson(Person person) {
persons.add(person);
}
}
PersonController.java
#RestController
public class PersonController {
#Autowired
private PersonService personService;
#RequestMapping(method=RequestMethod.POST,value="/fam")
public void addPerson(#RequestBody Person person){
personService.addPerson(person);
}
}
My JSON request to the body
{
"id":"1",
"familyName": "panchala",
"familyMembers":"5",
"contactNo":"234567"
}
I got an error
{
"timestamp": "2021-01-02T04:39:55.307+00:00",
"status": 404,
"error": "Not Found",
"message": "",
"path": "/fam"
}
```
please help me I don't know why I got this error
In PersonController.java add the following annotation just below the #RestController annotation:
#RequestMapping(value = "/person", produces = MediaType.APPLICATION_JSON_VALUE)
Only if this is present the controller gets resolved and the path reaches the particular controller method you are trying to call.
Now your URL might look like this : localhost:8080/person/fam
In the Controller class
#RequestMapping(value = "/fam", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void addPerson(#RequestBody Person person){
{
....
}
Just a suggestion it would be also good to return a response with a correct Response status
#RequestMapping(value = "/fam", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity addPerson(#RequestBody Person person){
{
......
return new ResponseEntity<>(HttpStatus.OK);
}
I had the same issue and I was not figuring out how to solve it, but I decided to see my imports, in case of imports being wrong.
I was importing #RequestBody from:
io.swagger.v3.oas.annotations.parameters.RequestBody;
Instead of importing the #RequestBody from:
org.springframework.web.bind.annotation.*
If any question, just comment.
I want to assure that the username is unique when creating a new one or updating one username. I wrote the code below and it works fine for creating and updating the username. But when updating only other user fields, like age or sex, and maintaining the same username, it returns invalid because the username already exists.
It's important to say that the validation I want is using BindingResult. I already have a database which considers username a unique constraint. But now I want the same validation with BindingResult. But the way I did causes error when updating an user.
Defining of the annotation #UniqueUsername
package app.condominio.validator;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Documented
#Constraint(validatedBy = UniqueUsernameValidator.class)
#Target({ ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface UniqueUsername {
String message() default "Nome de usuário já está sendo usado";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Implementing the validator
package app.condominio.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import app.condominio.service.UsuarioService;
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
#Autowired
UsuarioService usuarioService;
#Override
public void initialize(UniqueUsername username) {
}
#Override
public boolean isValid(String username, ConstraintValidatorContext cxt) {
return !usuarioService.existe(username);
}
}
In Usuario.class
#Entity
#Table(name = "Usuarios")
public class Usuario implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#UniqueUsername
private String username;
#NotBlank
private String password;
#NotBlank
private String nome;
#Email
private String email;
...
UsuarioService
#Service
#Transactional
public class UsuarioServiceImpl implements UsuarioService {
#Autowired
private UsuarioDao usuarioDao;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public boolean existe(String username) {
return usuarioDao.existsByUsername(username);
}
UsuarioDao
public interface UsuarioDao extends CrudRepository<Usuario, Long> {
Usuario findByUsername(String username);
Boolean existsByUsername(String username);
}
Controller
#Controller
#RequestMapping("conta")
public class UsuarioController {
#Autowired
private UsuarioService usuarioService;
#GetMapping("/cadastrar/sindico")
public ModelAndView preCadastro(#ModelAttribute("usuario") Usuario usuario, ModelMap model) {
model.addAttribute("conteudo", "cadastrarSindico");
return new ModelAndView("site/layout", model);
}
#PostMapping("/cadastrar/sindico")
public ModelAndView posCadastro(#Valid #ModelAttribute("usuario") Usuario usuario, BindingResult validacao, ModelMap model) {
if (validacao.hasErrors()) {
return preCadastro(usuario, model);
}
usuarioService.salvarSindico(usuario);
model.addAttribute(usuario);
model.addAttribute("conteudo", "cadastrarCondominio");
return new ModelAndView("site/layout", model);
}
I think UniqueUsernameValidator actions as expected.
I am not very clear about your application.So it is difficult to implement an method to to check if validating user have the same id or not of user in database.May be you should provide more details, such as code of method usuarioService.existe(username).
For me, to assure that the username is unique, i do this:
#Column(unique=true)
String username;
or use #UniqueConstraint
#Table(name = "users", uniqueConstraints = {
#UniqueConstraint(columnNames = {
"username"
})
})
but note that it will work if you let JPA create your tables.
#UniqueConstraint
You can try following approach using validation groups:
Create a marker interface (or class)
public interface UserCreateValidationGroup {
}
Then use it in you entity like following
#NotBlank
#UniqueUsername(groups = UserCreateValidationGroup.class)
private String username;
And then use it in Controller along with #Validated (Spring's variant for JSR-303). Note that you'll have to split your single method into separate create and update methods, to distinct validation.
Create:
#PostMapping("/cadastrar/sindico")
public ModelAndView create(
#Validated(UserCreateValidationGroup.class) #ModelAttribute("usuario") Usuario usuario,
BindingResult validacao,
ModelMap model
) { ... }
Update:
// note, here I did use PUT and no argument for #Validated annotation
#PutMapping("/cadastrar/sindico")
public ModelAndView update(
#Validated #ModelAttribute("usuario") Usuario usuario,
BindingResult validacao,
ModelMap model
) { ... }
However, you'll have to perform manual check (UsuarioService.existe) for username in the update case.