Embedded and Embeddable data not stored in the database - java

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" }"
}

Related

How to save array of object in sprig boot with crudrepository?

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.

Rename fields in response dto. Java, Spring

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:

How to create list of list in Java Springboot?

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;
}
}

How to get around error "Unrecognized field "Name, not marked as ignorable", to insert JSON data into H2 database with Spring?

I am trying to insert the following json data into H2 database with Spring by following the process shown in this Dan Vega video on YouTube. But, I get the following error:
Error:
Unable to save products: Unrecognized field "Name" (class com.saurabhsomani.domain.Product), not marked as ignorable (6 known properties: "salesCount", "price", "name", "category", "cust_rating", "id"])
at [Source: (BufferedInputStream); line: 2, column: 12] (through reference chain: java.util.ArrayList[0]->com.saurabhsomani.domain.Product["Name"])
Could you please help me fix this issue? Below are the code details:
My JSON (product.json) looks like:
[{
"Name": "P1",
"ID": 1,
"Price": 970,
"SalesCount": 300,
"Category": "A",
"Cust_Rating": 3.7
},
{
"Name": "P2",
"ID": 2,
"Price": 1170,
"SalesCount": 718,
"Category": "A",
"Cust_Rating": 3.8
},
{
"Name": "P3",
"ID": 3,
"Price": 1090,
"SalesCount": 1253,
"Category": "A",
"Cust_Rating": 0.5
}
]
Project Structure looks like:
Project Structure
JsondbApplication.java
package com.saurabhsomani;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.saurabhsomani.domain.Product;
import com.saurabhsomani.service.ProductService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
#SpringBootApplication
public class JsondbApplication {
public static void main(String[] args) {
SpringApplication.run(JsondbApplication.class, args);
}
#Bean
CommandLineRunner runner(ProductService productService){ //will help us when the application starts up
return args -> { //functional interface
//read json and write to db
ObjectMapper mapper = new ObjectMapper();
//We want a list of products
TypeReference<List<Product>> typeReference = new TypeReference<List<Product>>(){};
InputStream inputStream = TypeReference.class.getResourceAsStream("/json/product.json");
try{
//mapper helps us map json structure to the domain object
List<Product> products = mapper.readValue(inputStream, typeReference);
productService.save(products);
System.out.println("Products Saved!");
} catch (IOException e){
System.out.println("Unable to save products: " + e.getMessage());
}
};
}
}
ProductController.java
package com.saurabhsomani.controller;
import com.saurabhsomani.domain.Product;
import com.saurabhsomani.service.ProductService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/products")
public class ProductController {
//no business logic in controller
private ProductService productService;
//constructor
public ProductController(ProductService productService) {
this.productService = productService;
}
public ProductService getProductService() {
return productService;
}
#GetMapping("/list")
public Iterable<Product> list(){
return productService.list();
}
}
Product.java
package com.saurabhsomani.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Data
#AllArgsConstructor
#Entity
public class Product {
#Id
#GeneratedValue( strategy = GenerationType.AUTO)
private String name;
private int id;
private int price;
private int salesCount;
private String category;
private double cust_rating;
public Product(){
}
}
ProductService.java
package com.saurabhsomani.service;
import com.saurabhsomani.domain.Product;
import com.saurabhsomani.repository.ProductRepository;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class ProductService {
private ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Iterable<Product> list(){
return productRepository.findAll();
}
//to save one product
public Product save(Product product){
return productRepository.save(product);
}
//to save list of products
public void save(List<Product> products) {
productRepository.saveAll(products);
}
}
ProductRepository
package com.saurabhsomani.repository;
import com.saurabhsomani.domain.Product;
import org.springframework.data.repository.CrudRepository;
public interface ProductRepository extends CrudRepository <Product, String>{
}
In Product class you need to add Jackson's #JsonProperty annotation on properties relevant to attributes of source JSON as they are not identical (but differently cased)
public class Product {
...
#JsonProperty("Name")
private String name;
#JsonProperty("Price")
private int price;
#JsonProperty("Cust_Rating")
private double cust_rating;
// and others
}
P.S. try to avoid underscores in names outside of unit tests and stick to Java Code Conventions (https://google.github.io/styleguide/javaguide.html)

REST Web service JSON format

I am trying to create a REST web service that returns the details of a user.
Here is my code:
//Actual web service methods implemented from here
#GET
#Path("login/{email}/{password}")
#Produces("application/json")
public Tourist loginUser(#PathParam("email") String email, #PathParam("password") String password) {
List<Tourist> tourists = super.findAll();
for (Tourist tourist : tourists) {
if (tourist.getEmail().equals(email) && tourist.getPassword().equals(password)) {
return tourist;
}
}
//if we got here the login failed
return null;
}
This produces the following JSON:
{
"email": "adrian.olar#email.ro",
"fname": "Adrian",
"lname": "Olar",
"touristId": 1
}
What i need is:
{"tourist":{
"email": "adrian.olar#email.ro",
"fname": "Adrian",
"lname": "Olar",
"touristId": 1
}
}
What would i need to add to my code to produce this?
If you really want to wrap a Tourist into another object, you can do this.
Tourist.java:
package entities;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Tourist {
int touristId;
String email;
String fname;
String lname;
TouristWrapper.java:
package entities;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class TouristWrapper {
Tourist tourist;
SOResource.java:
package rest;
import entities.Tourist;
import entities.TouristWrapper;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
#Path("/so")
public class SOResource {
#GET
#Path("/tourists/{id}")
#Produces("application/json")
public TouristWrapper loginUser(#PathParam("id") int id) {
Tourist tourist = new Tourist(id, "foo#example.com", "John", "Doe");
TouristWrapper touristWrapper = new TouristWrapper(tourist);
return touristWrapper;
}
}
I have simplified your usecase but you get the point: No Tourist is returned but a TouristWrapper. The JSON returned is this:
{
"tourist": {
"email": "foo#example.com",
"fname": "John",
"lname": "Doe",
"touristId": 1
}
}

Categories