Many-To-One : JSON parse error Cannot construct instance - mysql

I devleloping a REST API using spring boot
I have two entities with bidirectional OneToMany
Product class
public class Product {
private Long productId;
private String name;
private String description;
private List<ProductList> productList;
public Product() {
}
public Product(Long productId, String name, String description, List<ProductList> productList) {
this.productId = productId;
this.name = name;
this.description = description;
this.productList = productList;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<ProductList> getProductList() {
return productList;
}
public void setProductList(List<ProductList> productList) {
this.productList = productList;
}
ProductList class
public class ProductList {
private Long productListId;
private String productListName;
private Product product;
public ProductList(Long productListId, String productListName, Product product) {
this.productListId = productListId;
this.productListName = productListName;
this.product = product;
}
public ProductList() {
}
public Long getProductListId() {
return productListId;
}
public void setProductListId(Long productListId) {
this.productListId = productListId;
}
public String getProductListName() {
return productListName;
}
public void setProductListName(String productListName) {
this.productListName = productListName;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
ProductEntity class:
#Entity
#Data
#Table(name = "PRODUCT")
public class ProductEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Integer productId;
#Column
private String name;
#Column
private String description;
#OneToMany(cascade = CascadeType.ALL, mappedBy="product", fetch =
FetchType.EAGER)
private List<ProductListEntity> productList;
// Getters,Setters,No-ArgCOnstructor, All-ArgConstructor
ProductListEntity class:
#Table
#Entity
#Data
public class ProductListEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productListId;
private String productListName;
#ManyToOne(fetch = FetchType.EAGER, targetEntity = ProductEntity.class)
#JoinColumn(name = "FK_Product", referencedColumnName = "productId")
private ProductEntity product;
Service to save data:
public void addProduct(Product product) {
ProductEntity productEntity = new ProductEntity();
BeanUtils.copyProperties(product, productEntity);
productRepository.save(productEntity);
}
When i try to post I get this error:
"message": "JSON parse error: Cannot construct instance of
eteosf.hexagonal.domain.model.Product (although at least one Creator
exists): no String-argument constructor/factory method to deserialize
from String value ('string'); nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
construct instance of eteosf.hexagonal.domain.model.Product
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('string')\n at [Source: (PushbackInputStream); line: 9, column: 18]
(through reference chain:
eteosf.hexagonal.domain.model.Product["productList"]->java.util.ArrayList[0]->eteosf.hexagonal.domain.model.Product$ProductList["product"])",
"path": "/product"
JSON request body:
{
"productId": 0,
"name": "string",
"description": "string",
"productList": [
{
"productListId": 0,
"productListName": "string",
"product": "string"
}
]
}

in your json_request, inside of productList you are sending "product" as a "string". But Deserializer can not turn that string into a Product object. It has to be sent as object {}. You can leave that object empty - just not send it if all it does is point at itself.
You basically have made a mistake of confusing different principles - the whole bidirectional relationship is only to be applied at persistence level. Json requests are being sent at controller/view level and therefore you can't display the bidirectional nature in the same way. This is why you don't use Entities as controller params but use DTOs.
In your case just don't send the "product" field for the controller:
{
"productId": 0,
"name": "string",
"description": "string",
"productList": [
{
"productListId": 0,
"productListName": "string",
}
]
}
and just add it in the controller method right after receiving the parameter:
//the receiving controller method which got Parameter `ProductEntity product`
product.getProductList().forEach(productList -> productList.setProduct(product);
Like I said you shouldn't use entities in Controller method, and it should be a DTO class in order to avoid exactly this kind of issues

Related

Spring boot & JSON: How to add foreign key when using POST request

I can't seem to figure out how to add an entity that has a foreign key, though JSON.
I have a user model, and a post model. A user can make different posts on a website.
This is a many-to-one relationship. A user can have several posts, while a post can only have one user (the poster). The post as a foreign key representing the id of the user that made the post.
This is the User model:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "user")
public class User {
//ID
#Id
#SequenceGenerator(
name = "user_sequence",
sequenceName = "user_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.IDENTITY,
generator = "user_generator"
)
#Column(name = "id",nullable = false)
private int id;
private String username;
private String password;
private String email;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
#Column(name = "creation_date")
private Date creationDate;
//RELATIONSHIP
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<Post> posts = new ArrayList<>();
/* =========== GETTERS AND SETTERS ===========*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
}
This is the Post model:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "post")
public class Post {
//ID
#Id
#SequenceGenerator(
name = "post_sequence",
sequenceName = "post_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.IDENTITY,
generator = "post_generator"
)
#Column(name = "id", nullable = false)
private int id;
#Column(name = "post_content")
private String postContent;
private String title;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
#Column(name = "creation_date")
private Date creationDate;
//RELATIONSHIP
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
/* ======== GETTERS AND SETTERS ======== */
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPostContent() {
return postContent;
}
public void setPostContent(String postContent) {
this.postContent = postContent;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
This is the postController:
#RestController
public class PostController {
#Autowired
private PostService postService;
#PostMapping("/savePost")
public Post getPost(#Validated #RequestBody Post post) {
return postService.savePost(post);
}
#GetMapping("getPost/{id}")
public Post getPost(#PathVariable int id) {
return postService.getPost(id);
}
#PutMapping("/deletePost/{id}")
public void deletePost(int id) {
postService.deletePost(id);
}
}
This is the JSON I send in to add a post. Request to: http://localhost:8080/savePost JSON body:
{
"postContent": "some content",
"creationDate": "2022-07-31",
"title": "my title",
"user": 1
}
But in postMan i get this error:
{
"timestamp": "2022-08-02T10:40:11.794+00:00",
"status": 400,
"error": "Bad Request",
"path": "/savePost"
}
And in spring i get this error: JSON parse error: Cannot construct instance of x.model.User (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1);
If i send in a JSON where I call the user for "user_id" or "uderId", then Im able to send the request, but then the foreign key turns into null
{
"creationDate": "2022-07-31",
"postContent": "some content",
"title": "my title",
"user_id": 1
}
what gets sent in:
{
"id": 2,
"postContent": "some content",
"title": "my title",
"creationDate": "2022-07-31",
"user": null
}
Does anyone know what im doing wrong?
Firstly, your APIs are not correct in terms of REST concept.
Here is a nice explanation.
You better should rework it to deal with Post entities:
add a userId param to the controller's value and remove it from models.
Use different classes for transport and business processes. It'll give you at least two prefenecies: first one is ability to pass any extra data through model objects, or wider, control which properties can be passed to be inserted/updated (as well as possibility of a separated validation for post/put operations), and the second one is guarantee that you won't face an Open session in View problem.
#RestController("/user/{userId}/post")
public class PostController {
#Autowired
private PostService postService;
#PostMapping("/save")
public PostResponseDTO addPost(#Validated #RequestBody PostAddDTO postModel, #PathVariable Long userId) {
return postService.savePost(userId, postModel);
}
#PutMapping("/{id}")
public PostResponseDTO updatePost(#Validated #RequestBody PostUpdateDTO postModel, #PathVariable Long userId, #PathVariable Long id) {
return postService.updatePost(userId, postModel);
}
#GetMapping("/{id}")
public PostResponseDTO getPost(#PathVariable Long userId, #PathVariable Long id) {
return postService.getPost(userId, id);
}
#DeleteMapping("/{id}")
public void deletePost(Long userId, Long id) {
postService.deletePost(userId, id);
}
}
You must:
add parameter fetch = FetchType.LAZY to both of #OneToMany and #ManyToOne declarations;
add a userId property to the PostUpdateDTO (if changing of post's owner is allowed)
At the service layer you can:
if POST: find a User by userId, validate weither exists it and probably raise some exception if doesn't or create and persist a new Post entity:
#Transactional
public PostResponseDTO addPost(PostAddDTO postModel, Long userId) {
User user = getValidUser(userId);
Post post = new Post(postModel);
UserDTO userDTO = new UserDTO(user); // here copy only simple properties, not the list of user's posts
post.setUser(user);
postRepository.save(post);
PostAddDTO result = new PostAddDTO(post);
result.setUser(userDTO);
return result;
}
/**
- will be used in both post and put operations
- #param userId user id from a controller
- #return {#link User} entity if found
- #throws RuntimeException if the entity has not been found
*/
private User getValidUser(Long userId) {
Optional<User> userOpt = userRepository.findById(userId);
if (!userOpt.isPresent()) {
log.WARN("addPost. user with id={userId} not found!", userId);
throw RuntimeException("some exception"); //!!!do not place any business info such as "user with id={userId} not found" because of a scam risc reasons
}
return userOpt.get();
}
if PUT: find a Post entity by userId and id, validate weither exists it or not and implement preferable logic. I don't know is it allowed to reassign a user? If so, check new user's existance first.
If DELETE, you may raise an exceptions in case of entities absence, but many not and just do nothing to success response be sent
One more reason why we do use transport objects. If you let it be as it is, it'll lead to an infinite loop while serialization: post.user -> (post: user.posts) {post.user -> ...}.
Of course all of this stuff is not the only way of solving of this problem and it doesn't answer all the questions about Java Persistance API, but this is the way that I went for some time ago within a concrete project.
Here is a REST guide made by a Spring team

Deleted item keeps showing in JSON

I'm working on a simple todo app in which you can add dependencies between todo items which means you cannot change status to true(completed) if dependency item is not completed. The problem is when I delete an item which another item is dependent to, json still shows dependency between two items. I'll try to explain with an example; say you have item 1 and item 2. Item 1 is dependent to item 2 and you can't mark item 1 "completed" if item 2 is not completed. But if you delete item 2 then dependency between items is also gone. So after deleting item 2 I can change item 1's status to true but when I make a get request for item 1, json still shows dependency to item 2.
Here is my TodoItem class;
package com.erdemkara.todoapp.data.entity;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.Set;
#Entity
#Table(name = "todo_items")
public class TodoItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
private String description;
#Column(nullable = false)
private LocalDate deadline;
#Column(nullable = false)
private boolean status;
#Column(name = "todo_list_id", nullable = false)
private int todoListId;
#OneToMany(mappedBy = "todoItem", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private Set<Dependency> dependencies;
public TodoItem()
{}
public TodoItem(int id, String name, String description, LocalDate deadline,
boolean status, int todoListId, Set<Dependency> dependencies)
{
this.id = id;
this.name = name;
this.description = description;
this.deadline = deadline;
this.status = status;
this.todoListId = todoListId;
this.dependencies = dependencies;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public LocalDate getDeadline() {
return deadline;
}
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
#JsonGetter("todo_list_id")
public int getTodoListId() {
return todoListId;
}
public void setTodoListId(int todoListId) {
this.todoListId = todoListId;
}
public Set<Dependency> getDependencies() {
return dependencies;
}
public void setDependencies(Set<Dependency> dependencies) {
this.dependencies = dependencies;
}
}
Dependency class;
package com.erdemkara.todoapp.data.entity;
import com.fasterxml.jackson.annotation.*;
import javax.persistence.*;
#Entity
#Table(name = "dependencies")
public class Dependency {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "todo_item_id", nullable = false)
#JsonBackReference
private TodoItem todoItem;
#Column(name = "dependency_item_id", nullable = false)
private int dependencyItemId;
public Dependency()
{}
public Dependency(int id, TodoItem todoItem, int dependencyItemId)
{
this.id = id;
this.todoItem = todoItem;
this.dependencyItemId = dependencyItemId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public TodoItem getTodoItem() {
return todoItem;
}
public void setTodoItem(TodoItem todoItem) {
this.todoItem = todoItem;
}
public int getDependencyItemId() {
return dependencyItemId;
}
public void setDependencyItemId(int dependencyItemId) {
this.dependencyItemId = dependencyItemId;
}
}
Response for item 1 get request(item 1 is dependent to item 2 and 3);
{
"id": 1,
"name": "Item 1",
"description": "Study Collections",
"deadline": "2023-01-09",
"status": false,
"dependencies": [
{
"dependencyItemId": 3
},
{
"dependencyItemId": 2
}
],
"todo_list_id": 1
}
I get the same response before and after deleting item 2. But I want to get a response like this;
{
"id": 1,
"name": "Item 1",
"description": "Study Collections",
"deadline": "2023-01-09",
"status": false,
"dependencies": [
{
"dependencyItemId": 3
}
],
"todo_list_id": 1
}
How can I fix this?
EDIT: #Zychoo I use 2 different delete methods on Service layer. One is for deleting all dependencies for an item. The other one is to delete a specific dependency;
public void deleteDependencyByDependencyItemId(int todoItemId, int dependencyItemId) {
dependencyRepository.deleteByDependencyItemId(todoItemId, dependencyItemId);
}
public void deleteAllDependenciesByTodoItemId(int todoItemId) {
dependencyRepository.deleteAll(dependencyRepository.findAllByTodoItemId(todoItemId));
}
And this is the Repository Layer;
public interface IDependencyRepository extends CrudRepository<Dependency, Integer> {
#Modifying
#Transactional
#Query(value = "delete from dependencies d where d.todo_item_id=? and d.dependency_item_id =?", nativeQuery = true)
void deleteByDependencyItemId(int todoItemId, int dependencyItemId);
}
You could changed
public Set<Dependency> getDependencies() {
return dependencies;
}
to
public Set<Dependency> getDependencies() {
return dependencies.stream().filter(dependency -> "your condition for completion").collect(Collectors.toSet());
}
The ObjectMapper from spring-boot uses the getters to create a JSON. If your Dependency does not show up in the return value of your getter, it will not show up in the JSON response.
I reorganized delete method in TodoItem service layer from this:
public void deleteItemById(int id) {
todoItemRepository.deleteById(id);
}
to this:
public void deleteItemById(int id) {
todoItemRepository.deleteById(id);
dependencyService.deleteAllDependenciesByTodoItemId(id);
}
It deletes every dependency along with the item. Now it works as I expected. Thank you for the answers.

SpringBoot JSON not deserializing into my request model

I am using SpringBoot and trying to deserialize JSON like:
{
"userId": "Dave",
"queryResults": {
"id": "ABC",
"carData": {.....},
"carId": "Honda",
"status": 0,
"model": "X"
}
}
, into MyRequestModel clas:
public class MyRequestModel {
private String userId;
private String: queryResults;
}
, that is received as #RequestBody parameter in my #PostMapping method that looks like:
#PostMapping
public String postDate(#RequestBody MyRequestModel data) {
...
return "posted";
}
The above queryResults field is supposed to be stored as a CLOB in a database.
Problem I am having is that if I send this JSON to hit my endpoint (PostMapping) method, it cannot deserialize it into MyRequestModel and I get this error:
Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 3, column: 18] (through reference chain: MyRequestModel["queryResults"])]
I guess the real answer to your question is: if you NEED the queryResults property to be a String, then implement a custom deserializer.
If not, then, use one of the alternatives that Jonatan and Montaser proposed in the other answers.
Implementing a custom deserializer within Spring Boot is fairly straightforward, since Jackson is its default serializer / deserializer and it provides a easy way to write our own deserializer.
First, create a class that implements the StdDeserializer<T>:
MyRequestModelDeserializer.java
public class MyRequestModelDeserializer extends StdDeserializer<MyRequestModel> {
public MyRequestModelDeserializer() {
this(null);
}
public MyRequestModelDeserializer(Class<?> vc) {
super(vc);
}
#Override
public MyRequestModel deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String userId = node.get("userId").asText();
String queryResults = node.get("queryResults").toString();
MyRequestModel model = new MyRequestModel();
model.setQueryResults(queryResults);
model.setUserId(userId);
return model;
}
}
Second, mark your class to be deserialized using your custom deserializer by using the #JsonDeserialize annotation:
MyRequestModel.java
#JsonDeserialize(using = MyRequestModelDeserializer.class)
public class MyRequestModel {
private String userId;
private String queryResults;
}
It's done.
queryResults is a String on Java side but it is an Object on JSON side.
You will be able to deserialize it if you send it in as a String:
{
"userId": "Dave",
"queryResults": "foo"
}
or if you create classes that maps to the fields:
public class MyRequestModel {
private String userId;
private QueryResults queryResults;
}
public class QueryResults {
private String id;
private CarData carData;
private String carId;
private Integer status;
private String model;
}
or if you serialize it into something generic (not recommended):
public class MyRequestModel {
private String userId;
private Object queryResults;
}
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
public class MyRequestModel {
private String userId;
private JsonNode queryResults;
}
You have two options to deserialize this request:-
change the type of queryResults to Map<String, Object>, it will accepts everything as an object of key and value. (Not recommended)
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
You have to create a class that wraps the results of queryResults as an object.
class QueryResult {
private String id;
private Map<String, Object> carData;
private String carId;
private Integer status;
private String model;
public QueryResult() {}
public QueryResult(String id, Map<String, Object> carData, String carId, Integer status, String model) {
this.id = id;
this.carData = carData;
this.carId = carId;
this.status = status;
this.model = model;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getCarData() {
return carData;
}
public void setCarData(Map<String, Object> carData) {
this.carData = carData;
}
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
and make the type of queryResult as shown:-
public class MyRequestModel {
private String userId;
private QueryResult queryResults;
}

SpringBoot Rest response not deserialiazed with jackson

I am a running a project with SpringBoot. In this project I am calling an external Rest Service. I have modeled the response items into bean.
But when I get the response back the data are not serialised into the beans.
I guess there must be some configuration missing but I cannot find what.
I have added onfiguration spring-boot-starter-test to the configuration of Maven:
The rest client:
#RunWith(SpringRunner.class)
#SpringBootTest
public class RestClientTest {
#Autowired
private RestTemplateBuilder restTemplate;
#Test
public void sayHello() {
System.out.println("Hello");
assert(true);
}
#Test
public void testGetEmployee() {
RestTemplate template = restTemplate.build();;
HttpHeaders headers = new HttpHeaders();
List<MediaType> types = new ArrayList<MediaType>();
types.add(MediaType.APPLICATION_JSON);
types.add(MediaType.APPLICATION_XML);
headers.setAccept(types);
headers.set("Authorization", "Bearer gWRdGO7sUhAXHXBnjlBCtTP");
HttpEntity<Items> entity = new HttpEntity<Items>(headers);
String uri = "https://mytest.com/employees";
//ResponseEntity<String> rec = template.exchange(uri, HttpMethod.GET, entity, String.class);
//System.out.println("Received: " + rec);
ResponseEntity<Items> rec = template.exchange(uri, HttpMethod.GET, entity, Items.class);
System.out.println("Received: " + rec);
}
}
When I inspect the elements of the response it, I get a list, all the items are with null values
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Item implements Serializable {
#JsonProperty
private String id;
#JsonProperty
private String name;
#JsonProperty
private String email;
#JsonProperty
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Items implements Serializable {
#JsonProperty
private List<Item> items = new ArrayList<Item>();
public List<Item> getItems() {
return items;
}
}
Do you see what I am missing here?
The response is like this:
{
"items": [
{
"item": {
"id": 0,
"name": "string",
"email": "string",
"phone": "string",
Do you see what I am missing here?
Thanks
Gilles
The way you have implemented will try to deserialize data into Items class. But it doesn't have the required properties to deserialize. When you need to get a list of data through rest template exchange, you can get them as follows.
Get data as an array and convert it into arrayList.
Item[] itemArray = template.exchange(uri, HttpMethod.GET, entity, Item[].class).getBody();
List<Item> itemList = Arrays,asList(itemArray);
or
Use ParameterizedTypeReference to get data as a list
ResponseEntity<List<Item>> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {});
List<Item> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {}).getBody(); // access list directly
You might need to add this to your ObjectMapper:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
And on your entity add #JsonRootName("item")

Why JPA Hibernate changes the name of the field?

In model class I have isActive field with is boolean, that represent the is_active field in MySql DB. Here is whole model class:
package ca.gatin.model;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "Account")
public class Account {
#Id
#GeneratedValue
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(nullable = false, unique = true)
private String email;
#Column(nullable = false)
private String password;
#Column(name = "is_active", nullable = false)
private boolean isActive;
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "date_last_modified")
private Date dateLastModified;
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 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 boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public Date getDateLastModified() {
return dateLastModified;
}
public void setDateLastModified(Date dateLastModified) {
this.dateLastModified = dateLastModified;
}
}
But when I fetch account let's say through REST API like:
#RequestMapping(
value = "/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ServiceResponse<Account> getAll(#PathVariable("id") Long id) {
ServiceResponse<Account> serviceResponse = accountService.getAccountById(id);
return serviceResponse;
}
In a reply object I get isActive field renamed by Hibernate to "active" like this:
{
"id": 19,
"firstName": "Julia",
"lastName": "Sarandi",
"email": "julia#gatin.ca",
"password": "111111",
"dateCreated": 1451293826000,
"dateLastModified": null,
"active": true
}
Why? Why all other field's names stay same as in Account class, but isActive is renamed?
That is one question, and another question is:
I am new in Hibernate, and I do no understand why in logs of Hibernate DB requests is shows some weird queries:
Hibernate: select account0_.id as id1_0_0_, account0_.date_created as date_cre2_0_0_, account0_.date_last_modified as date_las3_0_0_, account0_.email as email4_0_0_, account0_.first_name as first_na5_0_0_, account0_.is_active as is_activ6_0_0_, account0_.last_name as last_nam7_0_0_, account0_.password as password8_0_0_ from Account account0_ where account0_.id=?
What query language is it? What are symbols: "0_", "0_0_". Can I switch logs to show MySQL queries to make it more understandable?
FYI
In my application.properties file I have following configuration:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultNamingStrategy
Change getter and setter method name for isActive field as:
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
Then it return isActive in response.
That has nothing to do with Hibernate, and everything to do with your JSON marshaller. Spring uses Jackson, and Jackson uses bean properties (i.e. getters) to access the data and transform them to JSON fields. Your getter is named isActive(), and thus corresponds to a bean property named active, hence the name of the attribute in the JSON.
If you want the JSON field to be named isActive, then your getter should be isIsActive(). Or much better, you should annotate it with #JsonProperty("isActive").
To answer your second question, the query is a SQL query, generated by Hibernate. It changes the name of tables and assigns aliases to columns mainly to disambiguate tables, and fields of different tables that could have the same name, AFAIK.