Map parent to Child with different column names JPA - spring-boot

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

Related

Automatic JPA refresh ManyToOne objects with #Version feature

I'm getting an exception:
org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance
- save the transient instance before flushing :
com.example.jpamapstruct.entity.Member.club ->
com.example.jpamapstruct.entity.Club
while saving the member entity:
#Transactional
public MemberDto save(MemberDto memberDto){
Member entity = memberMapper.toEntity(memberDto);
return memberMapper.toDto(repository.save(entity));
}
How to fix this case in a proper way?
Possible solution:
I can get and set a club object before saving a member but is it only one and the best approach in such scenario?
Member entity = memberMapper.toEntity(memberDto);
clubRepository.getReferencedById(memberDto.getClubId()).ifPresent(entity::setClub);
return memberMapper.toDto(repository.save(entity));
Questions:
Should I put this getReferencedById code explicity? I mean what if we have several child objects (unidirectional ManyToOne), for each we need to get data from DB.
Is there any way to handle this by JPA (Spring Data/JPA) "automatically"?
Maybe it is possible to hit DB only one time with f.e join fetch somehow for all childs (with using custom #Query or querydsl or criteria/specification)?
Next, hoow to handle collections (unidirectional manyToMany)? In my case set of events in member object. Also need to loop thru and get all objects one by one before saving member?
Where should I put such logic in a service or maybe better in a mapstuct mapper?
If so, how to use repositories in such mapper?
#Mapper(componentModel = "spring")
public interface MemberMapper extends EntityMapper<MemberDto, Member> {
#AfterMapping
default void afterMemberMapping(#MappingTarget Member m, MemberDto dto) {
var club = clubRepo.findById(m.getClub().getId())
m.setClub(club)
}
Source code:
#Entity
public class Club extends AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
}
public class ClubDto extends AbstractDto {
private Long id;
}
#Entity
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
// commented out as don't want to save child object as it should already exist
// #ManyToOne(cascade = CascadeType.ALL)
#ManyToOne
Club club;
#ManyToMany
#JoinTable(name = "member_events",
joinColumns = #JoinColumn(name = "member_id"),
inverseJoinColumns = #JoinColumn(name = "event_id")
)
List<Event> events = new ArrayList<>();
}
public class MemberDto {
private Long id;
private ClubDto club;
}
#MappedSuperclass
public abstract class AbstractEntity {
#Version
private Integer version;
}
public abstract class AbstractDto {
private Integer version;
}
//MemberMapper above

#OnetoMany entity in #ElementCollection

I have 2 entities and 1 embeddable object :
#Entity
class CourseDetails extends Course {
#Id
Integer id;
#ElementCollection
#CollectionTable(name = "course_section", joinColumns = #JoinColumn(name = "courseId"), foreignKey = #ForeignKey(name = "course_section_fk"))
private List<CourseSection> courseSection;
}
#Embeddable
public class CourseSection extends BaseBo {
#OneToMany
#JoinColumn(name="contentId")
private Set<CourseContent> courseContent = new HashSet<>();
}
#Entity
public class CourseContent {
private static final long serialVersionUID = 1856738483334146418L;
#Id
private Integer contentId;
private String contentSummary;
}
I want to store coursesection as an embedded object of course and course_section should contain reference of course_content. I tried the above structure but it gives error :
#ElementCollection cannot be used inside an #Embeddable that is also contained within an #ElementCollection
How to achieve this in spring boot-jpa ?

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;

Save Entity Using Foreign Key

I have a question that I hope I can describe clearly. I have the following classes:
#Entity
public class Filter {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,orphanRemoval = true)
#JoinColumn(name = "filter_id", nullable = false)
private Set<FilterMedication> medications;
//setter and getters are not show
...}
.
#Entity
public class FilterMedication {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name = "medication_id", nullable = false)
private Medication medication;
// Setters and getters are not shown
.....}
.
#Entity
#Table(name = "medication")
public class Medication {
#Column(name = "generic_name")
private String genericname;
private String name;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// Setters and getters are not shown
.....}
Basically Filters one-to-many relationship with FilterMedicaton, and FilterMedication has many-to-one relationship with Medication.
I created a repository to query for Filters
public interface FilterRepository extends JpaRepository <Filter, Long> {}
I can add a new filter by sending the following JSON object to the save() function
{
"id": 1,
"name": "Test1",
"medications": [
{
"id": 2,
"medication": {
"genericname": "Oxymetazoline HCl Nasal Soln 0.05%",
"name": "12 HOUR NASAL SPRAY 0.05 % NA SOLN",
"strength": "0.05%",
"form": "Solution",
"route": "Nasal"
}
}
]
}
Now time for the question: Is there a way to pass the Medication Foreign Key instead of the complete Medication object, Spring JPA will convert the foreign key to the proper object? The JSON code will be something like this
{
"id": 1,
"name": "Test1",
"medications": [
{
"id": 2,
"FORIGEN KEY": 1
}
]
}
Technically, I can write a function to do so; however, I feel that there is a better and cleaner way to do it.
//convert json to java obj
Filter filter = new Gson().fromjson(yourjson, Filter.class);
//get the fiterMedication (id = 2)
int id = filter.getMedications().getId();
FilterMedication filterMedication = filterMedicationRepository.get(id);
Filter newFilter = new Filter();
newFilter.setId(filter.getId());
....//set name
newFilter.getMedications.add(filterMedication);//get the set of medications and add the element filterMedication
//save newFilter

Spring Data Rest - Creating the parent and embedded object in one request

I have a parent object that looks like this:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(nullable = false)
private String title
#Column(nullable = false)
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Child> sentenceList;
}
And a child object that looks like this:
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(nullable = false)
private String title
#ManyToOne
#JoinColumn(nullable=false)
private Parent parent;
}
What I want to do is POST to the parent's repository to create the parent and also create the embedded children. The POSTed JSON would look something like this:
{
"title": "Parent"
[
{
"title": "Child 1"
},
{
"title": "Child 2"
},
]
}
Is this possible with Spring Data REST? I currently get an error stating that the parent PK can't be null.
Thanks for any help!

Resources