How to pass the ID of a foreign field when using Rest API in Spring/JPA - spring

I'm making a RestAPI using Spring/JPA, and I'd like to insert a new object into the database.
My object is designed like this:
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String street;
private Integer number;
#Column(name = "postal_code")
private String postalCode;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference
#JoinColumn(name = "city_id")
private City city;
}
So when I pass the ID through the body of the request using the RequestBody annotation, it does not get mapped to the field city, since it is not an Integer, rather of type City.
So when passing the following input:
{
"city": "1",
"street": "dobrota",
"number": "2",
"postalCode": "85330"
}
It returns JSON parse error: Cannot construct instance of com.ct.academy.entities.City (although at least one Creator exists) no String-argument constructor/factory method to deserialize from String value
Is there a way of getting the ID from the body, without changing the structure of my entity, or using a DTO, since I'm planning to set the City inside of the method in the service class?

Related

Hibernate entity - join with condition

I have table/entity which has varchar(255) field that can store values of multiple data types and type field which indicates what kind of value type it is. I want to perform join only if data type is of certain value ie. document.
Example:
#Entity
#Table(name = "ACT_HI_DETAIL")
public class TaskDetailsVariable implements Serializable {
#Id
#Column(name = "ID_")
private String id;
#Column(name = "TEXT_")
private String value;
#Column(name = "VAR_TYPE_")
private String type;
#ManyToOne
#JoinColumn(name = "TEXT_")
#WhereJoinTable(clause = "VAR_TYPE_ = 'document'") // this doesn't work
private Document document; // this should be joined only if type is document
}
When I try the example above, I get the error because it tries to join all LONG_ values. I have also tried #JoinFormula and #Where.

JPA Hibernate Split data between two tables

I have a REST API that will receive some customer data on the following format:
{
"customer_Id": 50,
"name": "name",
"company_name": "company_name",
"email": "email#provider.com",
"business_phone": "(00) 1111-2222",
"mobile_phone": "(00) 1111-2222",
"document": "123456789",
"state_registration_number": "ISENTO",
"state_registration_type": "NO_CONTRIBUTOR",
"city_registration_number": "ISENTO",
"classification": "AUTO",
"address": {
"street": "STREET NAME XXX",
"number": "NUMBER XX",
"complement": "COMPLEMENT",
"zip_code": "ZIP_CODE",
"neighborhood": "NEIGHBORHOOD",
"city": "CITY",
"state": "STATE"
}
}
I'd like to save this data on two tables: One table should contains the "main" customer data, and the other one should contais the customer's "address" data.
So, I defined the Customer entity as below:
#Data
#Entity(name = "X_CUSTOMERS")
public class Customer {
#Id
private int customer_Id;
#NotNull
private String name;
private String company_name;
private String email;
private String business_phone;
private String mobile_phone;
#NotNull
private String document;
private String state_registration_number;
private String state_registration_type;
private String city_registration_number;
#NotNull
private String classification;
#OneToOne(cascade = CascadeType.ALL)
private Address address;
}
And the Address entity as
#Data
#Entity(name = "X_ADDRESS")
public class Address {
#NotNull
private String street;
private String number;
private String complement;
private String zip_code;
private String neighborhood;
private String city;
private String state;
}
But, I couldn't realize how to create a relationship between them. Should I create a customer_id attribute on the Address entity? Should I define some additional Tags on Customer's address attribute? Note that I don't have a customer on the JSON data that is posted by the REST Client and, if a Customer is Update ou Deleted, the Address data should be Updated / Deleted also.
Sorry if this is a such trivial question. I'm learning the basics of JPA/Hibernate these days and your answer will guides me to the right direction to avoid things such 'reinventing the wheel'.
Thanks a lot!
If we consider Address to be a Value Object rather than entity then it can be mapped as below. In your case, it probably is correct to model it as a VO: if you were building a database of addresses then it could be considered an entity. See further here:
Value vs Entity objects (Domain Driven Design)
We can then make the address class an #Embeddable rather than an entity: it will not then have any identity of its own. To have the customer and address details stored in separate tables we can also use JPAs #SecondaryTable funtionality:
https://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTable.html
We have then the model classes as below. With these mappings your JSON updates will work as expected.
Customer:
#Data
#Table(name = "customers")
#SecondaryTable(name = "customer_addresses", pkJoinColumns={
#PrimaryKeyJoinColumn(name="customer_id",
referencedColumnName="customer_id")})
public class Customer {
protected static final String ADDRESS_TABLE_NAME = "customer_addresses";
// other fields
#Embedded
private Address address;
}
Address:
#Data
#Embeddable
public class Address {
#NotNull
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String street;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String number;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String complement;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String zip_code;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String neighborhood;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String city;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String state;
}
This is how i do it :
#OneToOne (fetch=FetchType.EAGER, cascade = CascadeType.ALL, optional = false)
#NotNull(message = "L'addresse du domicile est requise!", groups = Seventh.class)
#Getter
#Setter
private Address homeAddress;
No need for any inverse mapping and this lets me save a customer and his address in one fell swoop!
You need an ID for your address entity as well, something like :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
#Getter
#Setter
private Long id;

Use unique id to deserialize object via json

I have the following Spring JPA POJO:
#Entity
public class Employee {
private String firstname;
private String lastname;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
}
Without going into details, it is easy via Spring MVC to deserialize the following JSON to Employee object instance
{
"id": 1,
"firstName": "Peter",
"lastName": "Jones"
}
My questios: since each employee has a unique id, is it possible to only send only the employee id via JSON, and somehow via Spring JPA, and Jackson magic to retrieve the rest of the information
{
"id": 1
}
The function that would deserialize JSON to object would be:
#RequestMapping(method = RequestMethod.POST, path = "/update")
public void updateFilter(#RequestBody Employee emp) {
employeerepo.save(emp);
}
Technically, I can write a function to do the mapping and such, but I have a feeling that someone else had this problem before and there is clean and elegant way to do it without much coding

Using Entity Graph with DTO Projection using ResultTransformer returns null value

Hello I am new in jpa + criteria API + hibernate..
I have doubt related to use of ResultTransformer in jpa criteria API.
I am having two entity Department and Employee. one to many mapping between department and employee. i want to use entitygraph with DTO projection
1. Department
#Entity
#NamedEntityGraph(name = "departmentWithEmployee", attributeNodes = #NamedAttributeNode("setOfEmployee"))
#Table(name = "tblDepartment")
public class Department {
#Id
private String id;
private String name;
#OneToMany(mappedBy = "department")
private Set<Employee> setOfEmployee;
//....getter & setter
}
2. Employee
#Entity
#Table(name = "tblEmployee")
public class Employee {
#Id
private String id;
#ManyToOne
#JsonIgnore
private Department department;
private String firstName;
private String lastName;
//...getter & setter
}
DepartmentDTO.java
public class DepartmentDTO implements Serializable {
private String id;
private String name;
private Set<EmployeeDTO> setOfEmployee;
//... getter & setter..
}
I am executing query with entity graph and I want to get all departments from database and serialize with DepartmentDTO.java
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<DepartmentDTO> criteria = builder.createQuery(DepartmentDTO.class);
Root root = criteria.from(Department.class);
criteria.select(root);
EntityGraph graph = entityManager.getEntityGraph("departmentWithEmployee");
List<DepartmentDTO> list = entityManager.createQuery(criteria).setHint("javax.persistence.fetchgraph", graph)
.unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(DepartmentDTO.class)).list();
when i will get size of list it will give me correct result but it will give list of department with null value like
(In database i am having total 3 departments)
Output :
[
{
"id": null,
"name": null,
"setOfEmployee": null
},
{
"id": null,
"name": null,
"setOfEmployee": null
},
{
"id": null,
"name": null,
"setOfEmployee": null
}
]
I am getting all fields with null value.
So what is the issue here , is any mistake in use of ResultTransformer? Or is any better way to execute this query where I can get records using DTO ..?

JsonMappingException: Can not construct instance of

I have an entity with two columns refering same column in other table. Basically, a Transaction depends on Account: when creating a new transaction a send money from one account to another.
Account:
#Entity
#Table(name = "accounts")
public class Account implements java.io.Serializable {
private static final long serialVersionUID = 2612578813518671670L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idaccount", unique = true, nullable = false)
private Long idaccount;
#Column(name = "name", length = 50)
private String name;
#NotNull
#ManyToOne
#JoinColumn(name = "iduser")
private User user;
...
Transaction:
#Entity
#Table(name = "transactions")
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idtransaction", unique = true, nullable = false)
private Long idtransaction;
private BigDecimal amount;
#NotNull
#ManyToOne
#JoinColumn(name = "SOURCE_ACCOUNT")
private Account sourceAccount;
#NotNull
#ManyToOne
#JoinColumn(name = "TARGET_ACCOUNT")
private Account targetAccount;
...
TransactionController
#CrossOrigin
#RestController
#RequestMapping("/transaction")
public class TransactionController {
#Autowired
TransactionService transactionService;
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Transaction> addTransaction(#RequestBody Transaction Transaction) {
transactionService.save(Transaction);
return new ResponseEntity<Transaction>(Transaction, HttpStatus.CREATED);
}
...
If I try post to create a transaction (naturally I have the accounts already created):
{
"amount": 111,
"sourceAccount": 1,
"targetAccount": 2
}
I get:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not construct instance of com.mycompany.basicbank.model.Account: no int/Int-argument constructor/factory method to deserialize from Number value (1)
at [Source: java.io.PushbackInputStream#63447acf; line: 3, column: 18] (through reference chain: com.mycompany.basicbank.model.Transaction["sourceAccount"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.mycompany.basicbank.model.Account: no int/Int-argument constructor/factory method to deserialize from Number value (1)
at [Source: java.io.PushbackInputStream#63447acf; line: 3, column: 18] (through reference chain: com.mycompany.basicbank.model.Transaction["sourceAccount"])
So my question is: what should I check in order to fix "Can not construct instance of com.livingit.basicbank.model.Account: no int/Int-argument constructor/factory method to deserialize from Number"?
The problem is the json you are sending doesn't exactly match the Transaction class. hence you see the error.
But what you are trying to achieve is a valid scenario and can be done.
Some options.
Create a new class(not Transaction) which matches the json. Like
class TransactionClient {
BigDecimal amount,
Long sourceAccount,
Long targetAccount
}
And in the backend(controller or some in service) you can get the Acccounts from database with this sourceAccount and targetAccount and create a transaction object with this objects and save.
From the frontend call backend to get the Accounts(json) for these source and target accounts and then call your transaction endpoint with the json like this
{
"amount": 111,
"sourceAccount": {
"idaccount" :123123,
..... // All the Non Null fields
},
"targetAccount": {
"idaccount" :45554,
..... // All the Non Null fields
},
}

Resources