JPA entity resultset duplication with more than one collection - java

The Contact entity defines relationships to two collections of entity of type email and nickname which exist in two MySQL tables.
My issue is that the result set this returns has duplicated email and nicknames.
{
"contactId": 1,
"givenName": "toast",
"middleName": "brown",
"familyName": "jam",
"dob": "2014-11-19",
"contactEmailAddress": [
{
"emailAddressId": 1,
"emailAddress": "donald.duck#disney.com",
"contactId": 1
},
{
"emailAddressId": 1,
"emailAddress": "donald.duck#disney.com",
"contactId": 1
},
{
"emailAddressId": 2,
"emailAddress": "mickey.mouse#disney.com",
"contactId": 1
},
{
"emailAddressId": 2,
"emailAddress": "mickey.mouse#disney.com",
"contactId": 1
}
],
"contactNickname": [
{
"contactNicknameId": 1,
"nickname": "mm",
"contactId": 1
},
{
"contactNicknameId": 2,
"nickname": "mouse",
"contactId": 1
},
{
"contactNicknameId": 1,
"nickname": "mm",
"contactId": 1
},
{
"contactNicknameId": 2,
"nickname": "mouse",
"contactId": 1
}
]
}
If I remove the ContactNickname collection from the Contact entity the result set is as follows.
{
"contactId": 1,
"givenName": "toast",
"middleName": "brown",
"familyName": "jam",
"dob": "2014-11-19",
"contactEmailAddress": [
{
"emailAddressId": 1,
"emailAddress": "donald.duck#disney.com",
"contactId": 1
},
{
"emailAddressId": 2,
"emailAddress": "mickey.mouse#disney.com",
"contactId": 1
}
]
}
I expected a distinct collection of email address and a distinct collection of nicknames but this is not the case. What am I doing incorrectly.
Using JPA I have mapped the the class as follows.
#Entity
#Table(name="contact")
public class Contact implements Serializable {
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name="contact_id")
private Collection<ContactEmailAddress> contactEmailAddress;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name="contact_id")
private Collection<ContactNickname> contactNickname;
public Collection<ContactEmailAddress> getContactEmailAddress(){
return this.contactEmailAddress;
}
public void setContactEmailAddress(Collection<ContactEmailAddress> contactEmailAddress){
this.contactEmailAddress=contactEmailAddress;
}
public Collection<ContactNickname> getContactNickname(){
return this.contactNickname;
}
public void setContactNickname(final Collection<ContactNickname> contactNickname){
this.contactNickname=contactNickname;
}
}
#Entity
#Table(name="contact_email_address")
public class ContactEmailAddress implements Serializable {
#Id
#GeneratedValue
#Column(name="email_address_id")
private int emailAddressId;
#Column(name="email_address")
private String emailAddress;
#Column(name="contact_id")
private int contactId;
public void setContactId(int contactId){
this.contactId=contactId;
}
public int getContactId(){
return this.contactId;
}.
public int getEmailAddressId(){
return emailAddressId;
}
public void setEmailAddressId(final int emailAddressId){
this.emailAddressId=emailAddressId;
}
public String getEmailAddress(){
return emailAddress;
}
public void setEmailAddress(String emailAddress){
this.emailAddress=emailAddress;
}
}
#Entity
#Table(name="contact_nickname")
public class ContactNickname implements Serializable {
#Id
#GeneratedValue
#Column(name="contact_nickname_id")
private int contactNicknameId;
#Column(name="nickname")
private String nickname;
#Column(name="contact_id")
private int contactId;
public int getContactNicknameId(){
return contactNicknameId;
}
public void setContactNicknameId(final int contactNicknameId){
this.contactNicknameId=contactNicknameId;
}
public String getNickname(){
return this.nickname;
}
public void setNickname(String nickname){
this.nickname=nickname;
}
public void setContactId(int contactId){
this.contactId=contactId;
}
public int getContactId(){
return this.contactId;
}
}
Hibernate is running the following when the duplication happens.
SELECT contact0_.contact_id AS contact_1_0_0_,
contact0_.dob AS dob2_0_0_,
contact0_.family_name AS family_n3_0_0_,
contact0_.given_name AS given_na4_0_0_,
contact0_.middle_name AS middle_n5_0_0_,
contactema1_.contact_id AS contact_2_0_1_,
contactema1_.email_address_id AS email_ad1_1_1_,
contactema1_.email_address_id AS email_ad1_1_2_,
contactema1_.contact_id AS contact_2_1_2_,
contactema1_.email_address AS email_ad3_1_2_,
contactnic2_.contact_id AS contact_2_0_3_,
contactnic2_.contact_nickname_id AS contact_1_3_3_,
contactnic2_.contact_nickname_id AS contact_1_3_4_,
contactnic2_.contact_id AS contact_2_3_4_,
contactnic2_.nickname AS nickname3_3_4_
FROM contact contact0_
LEFT OUTER JOIN contact_email_address contactema1_
ON contact0_.contact_id = contactema1_.contact_id
LEFT OUTER JOIN contact_nickname contactnic2_
ON contact0_.contact_id = contactnic2_.contact_id
WHERE contact0_.contact_id =?
Kind regards,
Ian.

If you never want to have duplicates in your collection, you should use Set instead of Collection in your entity.

Related

How to implement group by with sum of multiple fields in java

Controller class:
#RestController
#RequestMapping(value = "/api/v1/")
public class TestController {
#Autowired
TestService testService;
#GetMapping(value = "test")
public List<Bean> getDetails() {
return testService.getDetails();
}
}
Service class:
#Service
public class TestService {
#Autowired
BeanRepository beanRepository;
public List<Bean> getDetails() {
List<Bean> res = new ArrayList<>();
try{
List<BeanEntity> beans = beanRepository.findAllApplications();
for(BeanEntity beanEntity : beans) {
Bean bean = new Bean();
BeanUtils.copyProperties(beanEntity, bean);
if(beanEntity.getDesc().contains("Motor") || beanEntity.getDesc().contains("Nut")){
bean.setName("A");
} else if (beanEntity.getDesc().contains("Bolt") || beanEntity.getDesc().contains("Engine")) {
bean.setName("B");
} else {
bean.setName("C");
}
res.add()
}
} catch(Exception e) {
throw new e;
}
}
}
Entity class:
#Getter
#Setter
#NoArgConstructor
#AllArgConstructor
#Entity
public class BeanEntity implements Serializable {
private static final long serialVersionID = 3269743987697633760L;
#Id
#Column(name = "ApplicationID", nullable = false, unique = true)
public String application_id;
#Column(name= "Description")
public String desc;
#Column(name = "Status")
public String status;
#Column(name ="Approved")
public int approved;
#Column(name ="Declined")
public int declined;
}
Bean class:
#Getter
#Setter
#NoArgConstructor
#AllArgConstructor
public class Bean {
public String application_id;
public String desc;
public String status;
public int approved;
public int declined;
public String name;
}
Repository class:
#Repository
public interface BeanRepository extends JpaRepository<BeanEntity, String> {
#Query(name = "select * from abc", nativeQuery = true)
List<BeanEntity> findAllApplications();
}
Below is the Current Response i am getting from above code:
[
{
"application_id" : "12345",
"desc": "Motor",
"status": "active",
"approved": 1,
"declined": 0,
"name": "A"
},
{
"application_id" : "14785",
"desc": "Nut",
"status": "active",
"approved": 0,
"declined": 1,
"name": "A"
},
{
"application_id" : "15974",
"desc": "Bolt",
"status": "not-active",
"approved": 1,
"declined": 0,
"name": "B"
},
{
"application_id" : "36985",
"desc": "Engine",
"status": "active",
"approved": 1,
"declined": 0,
"name": "B"
},
{
"application_id" : "78945",
"desc": "Filter",
"status": "active",
"approved": 1,
"declined": 0,
"name": "C"
},
{
"application_id" : "45612",
"desc": "AC",
"status": "active",
"approved": 0,
"declined": 1,
"name": "C"
}
]
Expected Response i am looking for:
[
{
"name": "A",
"approved": 1,
"declined": 1
},
{
"name": "B",
"approved": 2,
"declined": 0
},
{
"name": "C",
"approved": 1,
"declined": 1
}
]
I am in beginner phase of learning java, How to implement group by with sum of multiple fields in java.
Can someone help me how i can achieve the expected response.

Infinite Loop Created When I want to Create an Organization Diagram Using Java Spring Boot

I try to do a project where I can show data Like this:
{
"vp2": {
"parentName": "ceo1",
"isParent": 0,
"hasChild": 0,
"child": []
}
}
Here My data are:
[
{
"entityName": "ceo1",
"parentId": 0,
"id": 1
},
{
"entityName": "ceo2",
"parentId": 0,
"id": 2
},
{
"entityName": "vp1",
"parentId": 1,
"id": 3
},
{
"entityName": "vp2",
"parentId": 1,
"id": 4
},
{
"entityName": "vp3",
"parentId": 1,
"id": 5
},
{
"entityName": "vp4",
"parentId": 2,
"id": 6
},
{
"entityName": "ceo3",
"parentId": 0,
"id": 7
},
{
"entityName": "ceo4",
"parentId": 0,
"id": 8
}
]
I want to show the data using map<Entity Name, DataModel>. Here DataModel's code is
#Data
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class DemoModel {
private String parentName;
private int isParent;
private int hasChild;
public List<Map<String, DemoModel>> child;
}
When there is no child, I get it properly. But when there is a child my function can not work. It gives infinite recursion problems. Here is my service function code
public Map<String, DemoModel> recursiveHelperFunction(int Id, List<DemoEntity> fullEntityList, List<Map<String, DemoModel>> modelList) {
Map<String, DemoModel> map = new HashMap<>();
DemoEntity demo = demoRepository.findById((long) Id).get();
DemoModel demoModel = new DemoModel();
String entityName = demo.getEntityName();
int parentId = (int) demo.getParentId();
if (Objects.equals(parentId, 0)) {
demoModel.setIsParent(1);
demoModel.setParentName("");
} else {
demoModel.setIsParent(0);
demoModel.setHasChild(0);
demoModel.setParentName(fullEntityList.get((int) demo.getParentId() - 1).getEntityName());
}
Iterator<DemoEntity> itr = fullEntityList.iterator();
while (itr.hasNext()) {
DemoEntity demo1 = itr.next();
int demo1Id = (int) demo1.getParentId();
int Id1 = (int) Id;
if (demo1Id == Id1) {
demoModel.setHasChild(1);
String demo1Entity = demo1.getEntityName();
try {
Map<String, DemoModel> map1 = recursiveHelperFunction((int) demo1.getId(), fullEntityList, modelList);
log.info("maps :{}", map1);
modelList.add(map1); //got error here
//demoModel.child.add(map1);
log.info("maps equal:??: {}", modelList.contains(map1));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
demoModel.setChild(modelList);
map.put(entityName, demoModel);
return map;
}
I call this helper recursive function here:
public Map<String, DemoModel> getDataById(int Id) {
List<DemoEntity> list = demoRepository.findAll();
List<Map<String, DemoModel>> modelList = new ArrayList<>();
return recursiveHelperFunction(Id, list, modelList);
}
Here, the DemoEntity code is:
#Entity
#Data
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class DemoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long Id;
#Column(unique = true)
private String entityName;
private long parentId;
}
How can I stop this infinite recursive problem?

Spring REST API, custom entity fields in the response

I'm working in a REST API (Java, SpringBoot, QueryDsl...) and I would like to customize the fields that I have in the result. By default I'm obtaining all fields of the entity but the fields that I need depends on the request. This should be something dynamic.
As far as I know, using projections I can obtain something like this but I have to declare previously the projection and I would like to work with something dynamic not static. On the other hand I have seen than something like GraphQL allows this behaviour but I would have to refactor everything.
Has anyone had this problem before?
This is my code:
BaseRestCRUDController
public abstract class BaseRestCRUDController<T extends EntityBase, V extends BaseDTO> {
#GetMapping("/list")
public ResponseEntity<List<V>> findAll(Predicate predicate, Pageable pageable) {
log.info("FindAll");
return new ResponseEntity(getCRUDService().findAll(predicate, pageable), HttpStatus.OK);
}
}
ExampleController
#RestController
#RequestMapping("/api/example")
public class ExampleController
extends BaseRestCRUDController<Example, ExampleDTO> {
#Autowired
private ExampleService ExampleService;
#Override
public ResponseEntity<List<ExampleDTO>> findAll(
#QuerydslPredicate(root = Example.class) Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
#Override
protected CRUDService<Example, ExampleDTO> getCRUDService() {
return ExampleService;
}
}
Example (Entity)
public class Example {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "creation_date")
private Instant creationDate;
#Column(name = "last_update")
private Instant lastUpdate;
#Column(name = "erasure_date")
private Instant erasureDate;
}
http://localhost:8080/api/example/list?name=test&page=0&size=5&sort=id,desc
http://localhost:8080/api/example/list?name=foo&page=1&size=10&sort=id,desc
http://localhost:8080/api/example/list?page=0&size=2&sort=id,asc
[
{
"id": 1,
"name": "e1",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 2,
"name": "e2",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 3,
"name": "e3",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
}
]
How can I obtain something like this without use projections?
http://localhost:8080/api/example/list?fields=id,name&page=1&size=10&sort=id,desc
[
{
"id": 1,
"name": "e1"
},
{
"id": 2,
"name": "e2"
},
{
"id": 3,
"name": "e3"
}
]
http://localhost:8080/api/example/list?fields=name&page=1&size=10&sort=id,desc
[
{
"name": "e1",
},
{
"name": "e2",
},
{
"name": "e3",
}
]
#Ignore
private Instant creationDate;
Try this.
You can use #Ignore on getter,setter or fields.

How map List to an Object with list in mapstructs

How can I use MapStruct to create a mapper that maps a list (my source) to a object with a list (destination)?
My source classes looks like this:
class SourceB {
private String name;
private String lastname;
}
class SourceA {
private Integer id;
private List<SourceB> bs;
}
so I need to transform it to this:
class DestinationA {
private Integer id;
private DestinationAB bs;
}
class DestinationAB {
private List<DestinationB> b;
}
class DestinationB {
private String name;
private String lastname;
}
Expected sample json:
source:
{
"id": 1,
"bs": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
destination:
{
"id": 1,
"bs": {
"b": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
}
It's quite simple. Just put #Mapping annotation with specified source and destination on top of the mapping method.
#Mapper
public interface SourceMapper {
#Mapping(source = "bs", target = "bs.b")
DestinationA sourceAToDestinationA(SourceA sourceA);
}

Remove Element in Nested List with Condition From another List - Java 8

I want to remove items from a list with a conditional value that comes from another List. Here are the objects
public class Student{
private String name;
private String age;
private List<Course> listCourses;
//Setters and getters
}
public Class Course{
private String courseName;
private List<CourseDetail> listCoursesDetail;
//Setters and getters
}
public Class CourseDetail{
private String id;
private String grade;
private String professor;
//Setters and getters
}
So as you can see the object Student has a list, inside that list there is another list from the object CourseDetail. What I want to achieve is to filter or remove elements from private List<CourseDetail> listCoursesDetail where ID is not equal to id from this other object.
public class Process{
private String name;
private List<ProcessDetail> listProcessDetail;
//Setters and getters
}
public class ProcessDetail{
private String id;
//Setters and getters
}
Assume the object Process is populated as follows
{
"name": "process1",
"listProcessDetail": [
{
"id": "456"
},
{
"id": "666"
},
{
"id": "555"
}
]
}
Student is populated as follows.
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "Math",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "144",
"grade": "90",
"professor": "Marcus"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The expected result will be:
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "456",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The element from listCoursesDetail with ID 144 was removed since there is no such value in the object Process.
So far I have these:
Set<String> ids = Process.getListProcessDetail().stream().map(ProcessDetail::id).collect(Collectors.toSet());
In these I stored all the ID's on a Set.
Then my attempt to remove the items:
List<Course> courses = Student.getListCourses().stream().filter(c -> c.getListCoursesDetail().stream().anyMatch(d -> ids.contains(d.getId()))).collect(Collectors.toList());
With these lines of code I get the same Student object as nothing happened.
Assuming you want to modify the existing objects, not create a clone of them with shorter lists, you'd do it like this:
student.getListCourses().forEach(c ->
c.getListCoursesDetail().removeIf(d -> ! ids.contains(d.getId())));

Categories

Resources