In springboot JPA one to one mapping response getting only values - spring-boot

Code for the controller class which will return response:
#RestController
#RequestMapping("/ProcessInfo/1.0.0")
public class RestController {
#ApiOperation(value = "getdeployments", notes = "This REST API is used to get deployments")
#GetMapping(value = "/getdeployments")
private List<ActivitiProcessDeployment> getdeployments() {
return ActivitiGetDeploymentRepository.getDeployment();
}
Below are the two entity classes having one to one mapping.
#Entity
#NamedQueries({#NamedQuery(name="ActivitiProcessDeployment.getDeployment", query="SELECT a.id,a.name,a.category,a.tenantId,a.deployTime,b.category,b.key,b.resource_name,b.version,b.deploymentId,b.diagramResourceName,b.description,b.id,b.hasStartFormKey,b.hasGraphicalNotation_,b.suspensionState,b.tenant_id_ FROM ActivitiProcessDeployment a INNER JOIN ActivitiProcessDefinition b ON a.id=b.deploymentId ORDER BY a.id")})
#Table(name="act_re_deployment")
public class ActivitiProcessDeployment implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#JsonProperty
#Column(name="id_")
private String id;
#JsonProperty
#Column(name="name_")
private String name;
#JsonProperty
#Column(name="category_")
private String category;
#JsonProperty
#Column(name="tenant_id_")
private String tenantId;
#JsonProperty
#Column(name="deploy_time_")
private Date deployTime;
#JsonProperty
#OneToOne( cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name="deploymentId", nullable=true)
private ActivitiProcessDefinition activitiProcessDefinition;
}
Another entity class
#Entity
#Table(name="act_re_procdef")
public class ActivitiProcessDefinition implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#JsonProperty("process_def")
#Id
#Column(name="id_")
private String id;
#JsonIgnore
#Column(name="rev_")
private String rev;
#JsonProperty
#Column(name="category_")
private String category;
#JsonProperty
#Column(name="name_")
private String name;
#JsonProperty
#Column(name="key_")
private String key;
#JsonProperty
#Column(name="resource_name_")
private String resource_name;
#JsonProperty
#Column(name="version_")
private String version;
#JsonProperty
#Column(name="deployment_id_")
private String deploymentId;
}
JPA repository which is extending crud repository and calling the named query which is declared in the entity class.
#Repository
public interface ActivitiGetDeploymentRepository extends JpaRepository<ActivitiProcessDeployment, Long> {
public List<ActivitiProcessDeployment> getDeployment();
}
The response I'm getting is:
[
[
"1",
"ExecutionTaskListener",
null,
"-1234",
"2018-10-29T07:31:48.373+0000",
"http://www.activiti.org/test",
"myProcess",
"ExecutionTaskListener.bpmn20.xml",
"1",
"1",
"ExecutionTaskListener.myProcess.png",
null,
"myProcess:1:4",
"f",
"t",
"1",
"-1234"
],
[
"13",
"multiinstance (1)",
null,
"-1234",
"2018-10-29T07:31:49.901+0000",
"http://www.activiti.org/test",
"multiinstance",
"multiinstance.bpmn20.xml",
"1",
"13",
"multiinstance.multiinstance.png",
null,
"multiinstance:1:16",
"f",
"t",
"1",
"-1234"
],
[
"23",
"testing",
null,
"-1234",
"2018-10-29T07:31:50.591+0000",
"http://www.activiti.org/test",
"myProcess",
"testing.bpmn20.xml",
"2",
"23",
"testing.myProcess.png",
null,
"myProcess:2:26",
"f",
"t",
"1",
"-1234"
]
]
As shown in the above response I am getting only json values I mean only table values without column names. So, how to get json response mapped with response coresponding key.

I am not quite sure what you are trying to do and what actually happens (and why it is even possible). But as you can see your JSON is not a list of ActivitiProcessDeployment but a list of string lists.
Your named query does not return ActivitiProcessDeployments but a list of column values. Without any named query and an interface like below:
public interface ActivitiGetDeploymentRepository
extends JpaRepository<ActivitiProcessDeployment, Long> {
public List<ActivitiProcessDeployment> findAllOrderById();
}
you might get better results. It would not be flat as your JSON but ActivitiProcessDefinition would be nested inside your ActivitiProcessDeployment.
And if you need to do projection see this question & answer .

Related

Spring Boot Java map Entity to DTO: array literal (strings) INSTEAD of array of objects

sample get request: http://localhost:3000/contact/1
What I got:
{
"id": 1,
"firstname": "First Name",
"lastname": "Last Name",
"emailaddresses": [
{
"emailaddress": "email#gmail.com"
},
{
"emailaddress": "email#g.c"
}
]
}
What I want:
{
"id": 1,
"firstname": "First Name",
"lastname": "Last Name",
"emailaddresses": ["email#gmail.com","email#g.c"]
}
The code below:
PersonDto
public class PersonDto {
private Long id;
private String firstname;
private String lastname;
private List<EmailAddressDto> emailaddresses;
//getters setters
}
EmailAddressDto
public class EmailAddressDto {
private String emailaddress;
//getters and setters
}
the Service class
public PersonDto getPerson(Long personId) { //this is the method inside the class
Optional<PersonEntity> p = peopleRepository.findById(personId);
var dto = modelMapper.map(p.get(), PersonDto.class);
return dto;
}
I also have a PersonEntity class mapped one-to-many to an EmailAddressesEntity class.
I'm really new to spring/java - I couldn't figure out how to get the JSON structure I want.
You can just annotate emailaddress field of EmailAddressDto with #JsonValue and leave everything as is.
public class EmailAddressDto {
#JsonValue
private String emailaddress;
//getters and setters
}
Using the above the output of a sample:
PersonDto personDto = new PersonDto();
personDto.setId(1L);
personDto.setFirstname("John");
personDto.setLastname("Doe");
personDto.setEmailaddresses(Arrays.asList(new EmailAddressDto("john#doe.com"), new EmailAddressDto("foo#bar.com")));
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(personDto);
System.out.println(json);
is:
{"id":1,"firstname":"John","lastname":"Doe","emailaddresses":["john#doe.com","foo#bar.com"]}
I'd suggest that you use a List of Strings instead of a List of EmailAddressDto's.
Following reasons:
Since you only have one attribute in your Dto, you can easily just directly use a List of Strings instead.
You get the second JSON-Layout as a response to your GET-Request.
When using variant number 1 (with the List of EmailAddressDto), you will achieve a JSON-Response with multiple objects for your different E-Mail addresses.
Otherwise when you use variant number 2 (with the List of String), you will achieve a JSON-Response which looks like what you want to have.
So don't forget to change your entities aswell.
public class PersonDto {
private Long id;
private String firstname;
private String lastname;
private List<String> emailAddresses;
//getters setters
}
If you can change your PersonDto that would be the easiest and cleanest way to do it.
public class PersonDto {
private Long id;
private String firstname;
private String lastname;
private List<String> emailaddresses;
//getters setters
}
While mapping your entities you would need to map EmailAddressesEntity to a String representing it (emailaddress).
If this is not possible you will need a custom converter for EmailAddressDto as follows:
public class ListEmailAddressDtoConverter extends StdConverter<List<EmailAddressDto>, List<String>> {
#Override
public List<String> convert(List<EmailAddressDto> emailAddresses) {
return emailAddresses.stream().map(EmailAddressDto::getEmailaddress).collect(Collectors.toList());
}
}
Then you need to tell Jackson to use it:
public class PersonDto {
private Long id;
private String firstname;
private String lastname;
#JsonSerialize(converter = ListEmailAddressDtoConverter.class)
private List<EmailAddressDto> emailaddresses;
//getters setters
}

Map parent to Child with different column names JPA

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

Spring data couchbase return null on one field

I want to do a simple getAll, but I have a null value on an embedded object.
Response :
[
{
"id": "0da716f2-6ef0-404f-9403-4d869a6b6853",
"channelName": "general",
"broadcast": {
"tenantId": "45bd5d1f-f585-44df-b37c-7882c123a492",
"broadcastName": "fix",
"id": null
}
}
]
I cant get the broadcastId, in couchbase web I can see that its present there but I cannot get it when using getAll() nor any other method.
Classes :
#Document
//lombok...
public class Broadcast implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(strategy = GenerationStrategy.UNIQUE)
//#JsonProperty(access = JsonProperty.Access.READ_ONLY)
//#Schema(accessMode = Schema.AccessMode.READ_ONLY)
private String id;
#Field
private String tenantId;
#Field
private String broadcastName;
}
#Document
//lombok...
public class Channel implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
private String channelName;
#Field
private Broadcast broadcast;
}

OneToOne ConstraintViolation while saving a new Record, PK Provided

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

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;

Resources