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;
Related
I can not save Data. When I saved post request got error?
#Entity
#Table(name = "ALKP")
public class ALKP {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#Column(name = "KEYWORD")
private String keyword;
private String code;
private Long slNo;
private String fullName;
private boolean isActive;
#CreationTimestamp
#Column(name = "created_at",updatable = false)
private LocalDate createDate;
#UpdateTimestamp
#Column(name = "updated_at")
private LocalDateTime updateDateTime;
#ManyToOne
#JoinColumn(name="parentId")
public ALKP parentId;
#OneToMany(mappedBy="parentId")
public Set<ALKP> subALKP = new HashSet<>();
Data ::
PostMan Body Request Data
{
"title": "FeMale",
"keyword": "GENDER_FEMALE",
"slNo": 2,
"active": true,
"code": "MC-00209",
"fullName": "FEMALE",
"parentId":700
}
"message": "detached entity passed to persist error in Spring Boot when consuming a rest service",
When I saved data it can not be catch Parent ALKP . I think It can be
parentId:{
"id":700
}
seems your ALKP enity have same parentId. or your defined the association mab be wrong.
#ManyToOne
#JoinColumn(name="parentId")
public ALKP parentId;
#OneToMany(mappedBy="parentId")
public Set<ALKP> subALKP = new HashSet<>();
Above both are pointng same ALKP and its Id then how can you get one and many object in same enity?
I am using Springboot 2.5.2 with JPA
I am trying to map a parent child table but they have different names in the actual DB.
My parent Entity class looks ass follows
#Entity
#Table(name="PARENTTABLE")
public class ParentEntity implements Serializable
{
private static final long serialVersionUID = -8610605997974967804L;
#Id
#Column(name = "PID")
private int pId;
#Column(name = "COL_ID_0")
private String colId_0;
#Column(name = "DATA1")
private String data1_0;
#Column(name = "DATA2")
private String data2_0;
//Joining Entity/Table
#OneToMany(targetEntity = ChildEntity.class,
cascade = CascadeType.ALL)
#JoinColumn(name = "COL_IDD",
referencedColumnName = "COL_ID_0")
private List<ChildEntity> childenity;
// ..getters and Setters
and my Child Entity/Table
#Entity
#Table(name="CHILDRENTABLE")
public class ChildEntity implements Serializable
{
private static final long serialVersionUID = -2781104466439391315L;
#Id
#Column(name="CID")
private int cId;
#Column(name="COL_IDD")
private String colIdd;
#Column(name = "DETIALS1")
private String datai1_1;
#Column(name = "DETIALS2")
private String datai1_2;
// ..getters and setters
How do you link these two entity classes with each other to display the Parent and details?
With the above configurations I get the Parent data back but the child records does not display only containing null in the Api call.
#JensSchauder Sorry meant to say the endpoint from the controller class. The database is exactly setup as the above example (not the real names) the Parent class contains the header data and the child class contains the details of the parent.
I need to call the end point for the parent entity and then it should list the child data along with it. as per the below example
JSON result I am expecting
'[
{
"pId": 2,
"colId_0": "5555",
"data1_0": "6001363000007",
"data2_0": "6001001392709",
"childenity": [{"cId": 222,
"colIdd": "5555",
"datai1_1": "Data222",
"datai1_2": "Data222"
},
{"cId": 333,
"colIdd": "5555",
"datai1_1": "Data333",
"datai1_2": "Data333"
}
]
}
]'
But I get the following
[
{
"pId": 2,
"colId_0": "5555",
"data1_0": "6001363000007",
"data2_0": "6001001392709",
"childenity": "null"
}
]
I'm using Spring to develop APIs along with JPA. I'm handling a POST request that accepts #RequestBody as a JSON object that looks like this-
{
"id": "323",
"name": "Sam",
"gpsLocation": {
"latitude": 66.7492558,
"longitude": 97.133258
}
}
And an SQL User Table that has the following columns-
id | name | latitude | longitude
Is there a way in Spring to map this nested json object directly to these table columns?
This is what my User.java and GpsLocation.java entity classes look like right now-
#Table(name = "user")
#Entity
public class UnderObservation {
#Column(name = "name", nullable = false)
private String name;
#Id
#Column(name = "id", nullable = false)
private String userID;
private GpsLocation location;
}
#Entity
public class GpsLocation {
#Column(name = "Latitude", nullable = false)
private Double Latitude;
#Column(name = "Longitude", nullable = false)
private Double Longitude;
}
I'm looking for a way to "flatten/unwrap" GpsLocation class so that it directly fits into the User table instead of having a separate table for GpsLocation.
I can not change the JSON Structure because some other No SQL Databases are using this. Also, I'm new to Spring!
The best practice here is using DTO data transfer object that hold the request body
and map it to the user object using external library like mapstruct, ObjectMapper or even do it manually
the DTO is a pojo Object carries data between processes
Try This way with a Constructor:
#Getter
#Setter
#Table(name = "user")
#Entity
public class UnderObservation {
#Column(name = "name", nullable = false)
private String name;
#Id
#Column(name = "id", nullable = false)
private String userID;
#Column(name = "latitude", nullable = false)
private Double latitude;
#Column(name = "longitude", nullable = false)
private Double longitude;
private GpsLocation location;
UnderObservation(String name, String userID, GpsLocation location) {
this.name = name;
this.userID = userID;
this.location = location;
this.latitude = this.location.getLatitude();
this.longitude = this.location.getLongitude();
}
}
We have an Entity called Customers that has a OneToOne relationship to the Entity Address.
The Customer's PK should be manually defined. The Address' PK should be automatically defined.
So, in Customer I omitted the #GeneratedValue and I'm providing is value manually. But, when trying to save I'm getting the following error:
2018-11-07 10:42:17.810 ERROR 1257 --- [nio-8080-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [Validation failed for classes [br.com.customers.entity.Address] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='não pode ser nulo', propertyPath=street, rootBeanClass=class br.com.customers.entity.Address, messageTemplate='{javax.validation.constraints.NotNull.message}'}
The problem is that the address.street is being provided and I can't realize why JPA is complaining that it's null...
Here are the JSON body that I'm trying to save. (It's being deserialized correctly, as, Address is not NULL)
{
"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",
"number": "NUMBER",
"complement": "COMPLEMENT",
"zip_code": "ZIP_CODE",
"neighborhood": "NEIGHBORHOOD",
"city": "CITY",
"state": "STATE"
}
}
Here are the Customer Entity:
#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(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn(name = "address_id")
private Address address;
}
And here, Address Entity:
#Data
#Entity(name = "X_ADDRESS")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int address_Id;
#NotNull
private String street;
private String number;
private String complement;
private String zip_code;
private String neighborhood;
private String city;
private String state;
}
What Am I doing wrong?
Thanks!!!
Adding the code do persist the entities:
Customer Repository:
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
}
To persist:
#RestController
#RequestMapping("/customers")
public class CustomersController {
private CustomerRepository customerRepository;
public CustomersController(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.save(customer);
}
}
From reading the Hibernate documentation, the save operation only persist entities with auto generated ids. So, if you intend to set the id yourself, then what you need, is to change your insert method for persist. And since you customer has an id that is not auto generated, maybe this could be the issue. You can read more in this blog.
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.persist(customer);
}
Hope it helps.
If you add CascadeType.MERGE, it will work
#OneToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "address_id")
private Address address;
you set the customer id(50) so the following line of SimpleJpaRepository will be executed.
return this.em.merge(entity);
I have the following entities defined in my project:
Country
#Entity
#Data
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Column(nullable = false)
String name;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
List<City> cities = new ArrayList<City>();
}
City
#Entity
#Data
public class City {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Column(nullable = false)
String name;
#ManyToOne
Country country;
}
Person
#Entity
#Data
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Column
String name;
#Embedded
Address address = new Address();
}
Address
#Data
public class Address {
#Column
String line;
#ManyToOne
Country country;
#ManyToOne
City city;
}
I have also repositories defined for Person, Country and City.
When I make a GET request to /persons/1 I get the following result:
{
"name":null,
"address":{
"line":"Address1"
},
"_links":{
"self":{
"href":"http://localhost:8080/persons/1"
},
"city":{
"href":"http://localhost:8080/persons/1/city"
},
"country":{
"href":"http://localhost:8080/persons/1/country"
}
}
}
I suspect that since address is an embedded object, the generated links to country and city are wrong. They don't return anything although city and country values are present. What should the correct links be?
Are embedded objects not supported by Spring Data Rest?
Possible solutions:
move associations to the parent entity
promote the embeddable into a separate entity resource
add ResourceProcessor to remove those links
add a custom controller to handle those links
UPDATE: This seems to be already fixed in Spring-DATA-REST v2.1. See DATAREST-262.