Load data from two OneToMany relationships - jpa

I want to display data from a database with JPA on the frontend, i.e. display the Person's details. Details can be 0..n Adresses and 0..m Phones. The entities are shown below:
#Entity
public class Person implements Serializable {
#Id #GeneratedValue
private int id;
private String name;
#OneToMany(mappedBy="person")
private List<Address> addresses = new ArrayList<Address>();
#OneToMany(mappedBy="person")
private List<Phone> phones = new ArrayList<Phone>();
// plus getter and setter
}
#Entity
public class Address implements Serializable {
#Id #GeneratedValue
private int id;
#ManyToOne
private Person person;
private String onestring; // plus getter and setter
}
#Entity
public class Phone implements Serializable {
#Id #GeneratedValue
private int id;
#ManyToOne
private Person person;
private String anotherstring; // plus getter and setter
}
As lazy loading is activated, ...
#PersistenceContext
private EntityManager em;
public Person getPerson(int id) {
return em.find(Person .class, id);
}
... would only provide proxies on adresses and phones.
Questions:
What is good way displaying the all data on the frontend, i.e. the person and all its addesses and phones? (Except for setting FetchType to EAGER).
Is there a way to fetch both addresses and phones into the same instance of Person or do I have to fetch the Person twice (one time with addresses, one time with phones) to omit a cartesian product?

Related

Polymorphic association in JPA

I like to have a single table ADDRESS in the Database and multiple unrelated entities for example User, Office will have one to many relations with the Address entity. In a nutshell my Entities would looke like
#Entity
public class Address {
#Id
private long id;
#Column
private String entityType; // served as discriminator e-g User,Office ...
#Column
private long entityId; // served as foreign key
... other address columns
}
User Class
public class User {
#Id
private long id;
#OneToMany
private List<Adress> addresses;t
}
Office class
#Entity
public class Office {
#Id
private long id;
#OneToMany
private List<Adress> addresses;
}
I tried #Any of Hibernate but that's not helping with One-to-Many relations. It has either Many-to-One and Many-to-Many relationships. What is the best way to achieve this in JPA.

Set vs List in REST One To Many

I have one to many relationship. If in class Customer I write List:
private List<Orders> order;
my GetMapping will work fine.
But I want to use best practices and I write Set instead of List:
private Set<Orders> order;
In result I have error:
Could not write JSON: Infinite recursion (StackOverflowError); nested
exception is com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)
Why I have this error? What's wrong with Set?
My entities:
#Entity
public class Customer {
#Id
#GeneratedValue
private int id;
private String firstName;
private String lastName;
#OneToMany(cascade=ALL, mappedBy="customer", orphanRemoval=true)
private Set<Orders> order;
//private List<Orders> order;
}
#Entity
public class Orders {
#Id
#GeneratedValue
private int id;
#JsonIgnore
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
}
And GetMapping:
#GetMapping("/customer/{id}")
public ResponseEntity get(#PathVariable Long id) {
Optional<Customer> customer = customerRepository.findById(id);
return new ResponseEntity<>(new ResponseObject(customer));
}
UPD. I see question Infinite Recursion with Jackson JSON and Hibernate JPA issue. But it's other question. I talk about difference in use List and Set. I am not interesting in #JsonIgnore and I don't ask about it (and I use it in my code). I want to understand why I have an error when I use Set and don't have error with List

JPA query method to find by elements in a List?

I have the following two entities (Contact and Participation, linked by a ManyToMany relation) :
#Entity
public class Contact {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String lastName;
#ManyToOne
private Company company;
#ManyToMany(fetch=FetchType.EAGER)
private List<Participation> participations;
}
#Entity
public class Participation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Company company;
private Status status;
}
I can't figure out how to get Contacts who have a specific Participation in their list. Should I look via Contacts with a specific JPA repository method (findBy...) ? Or would i have to look via the table which was created with both Contact and Participation IDs (ManyToMany) ?
Thanks!

Join Table and Spring Data Repository

This is my sample schema and I have generated jpa entities in eclipse.
I am using spring jpa repositories. I want to know if I need to create repository interface for student course table.
I am having doubt over addStudentCourse method of both student and course entity classes. List studentCourses will be always null for new entity, how can I fill student course table while registering student information in system i.e save method on studentRepository.
Student.java
#Entity
#NamedQuery(name="Student.findAll", query="SELECT s FROM Student s")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long studentid;
private String studentname;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="student")
private List<StudentCourse> studentCourses;
........
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setStudent(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCours.setStudent(null);
return studentCourse;
}
Course.java
#Entity
#NamedQuery(name="Course.findAll", query="SELECT c FROM Course c")
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long courseid;
private String coursename;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="course")
private List<StudentCourse> studentCourses;
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setCourse(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCourse.setCourse(null);
return studentCourse;
}
StudentCourse.java
#Entity
#Table(name="STUDENT_COURSE")
#NamedQuery(name="StudentCourse.findAll", query="SELECT s FROM StudentCourse s")
public class StudentCourse implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private StudentCoursePK id;
private String status;
//bi-directional many-to-one association to Course
#ManyToOne
#JoinColumn(name="COURSEID")
private Course course;
//bi-directional many-to-one association to Student
#ManyToOne
#JoinColumn(name="STUDENTID")
private Student student;
...
}
StudentCoursePK.java
#Embeddable
public class StudentCoursePK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(insertable=false, updatable=false)
private long studentid;
#Column(insertable=false, updatable=false)
private long courseid;
...
}
If I understood your question correctly what you want to do is to be able to save a student from the save method in StudentRepository, and that this inserts/updates the student and also inserts/updates the join table.
Since the Student entity is not the owning side (it's mapped by "student" in StudentCourse), saving a Student will not trigger a save on StudentCourse. To do so you can add a cascade property the list for insert, update... or just for everything:
#OneToMany(mappedBy="student", cascade = CascadeType.ALL)
private List<StudentCourse> studentCourses = new ArrayList<StudentCourse>();
Then you could a method on your #Service class that looks like this:
#Transactional
public void enrollInCourse(Student student, Course course) {
StudentCourse sc = new StudentCourse();
sc.setStudent(student);
sc.setCourse(course);
sc.setStatus("Enrolled");
student.getStudentCourses().add(sc);
studentRepository.save(student);
}
This will also populate the StudentCourse table.
So there's no need for a repository, although if the cascade doesn't work as expected you could create one and save the StudentCourse entity yourself manually.
If this does not work you could try changing your mappings. For n-ary relationships or join tables with extra columns I always define the #ManytoOne relationships inside the #Embeddable class, and in the entity that represents the join table I define getters as #Transient to allow access to the mapped objects which are inside the embedded composite Id.
You can see an example here, and a blog post about this approach here.

How to map existing JPA entities to PicketLink

I am trying to migrate a Seam 2 app to CDI and use PicketLink for security. After all the reading and researching, it seems like all the examples are having one to one mapping between PicketLink model and the backend entity. e.g. Account to AccountEntity, Partition to PartitionEntity. Since I already have entities in place representing identity model, I am stuck on trying to map them to PicketLink. Here is what I have:
#MappedSuperClass
public class ModelEntityBase implement Serializable {
#Id #Generated
Long id;
Date creationDate;
}
#Entity
public Account extends ModelEntityBase {
String username;
String passwordHash;
#OneToOne(mappedBy = "account")
Person person;
}
#Entity
public Person extends ModelEntityBase {
String name;
String email;
#OneToOne
#JoinColumn(name = "account_id")
Account account;
}
Two entities (plus a super class) representing a single identity model in PicketLink, e.g. stereo type User.
Based on this why IdentityType id is String not Long, I tried to add a new Entity in:
#Entity
#IdentityManaged(BaseIdentityType.class);
public class IdentityTypeEntity implement Serializble {
#Id #Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType")
#OwnerReference
private Account account;
#IdentityClass
private String typeName;
#ManyToOne #OwnerReference
private PartitionEntity partition;
}
I've tried a few different ways with the annotation and model classes. But when using IdentityManager.add(myUserModel), I just can't get it to populate all the entities. Is this even possible?
Got help from Pedro (PicketLink Dev). Post the answer here to help others.
This is the model class I ended up using.
#IdentityStereotype(USER)
public class User extends AbstractAttributedType implements Account {
#AttributeProperty
private Account accountEntity;
#AttributeProperty
#StereotypeProperty(IDENTITY_USER_NAME)
#Unique
private String username;
#AttributeProperty
private boolean enabled;
#AttributeProperty
private Date createdDate;
#AttributeProperty
private Date expiryDate;
#AttributeProperty
private Partition partition;
// getter and setter omitted
}
And created a new entity to map to this model:
public class IdentityTypeEntity implements Serializable {
#Id
#Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType",
cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#AttributeValue
// #NotNull
private HAccount accountEntity;
#IdentityClass
private String typeName;
#ManyToOne
#OwnerReference
private PartitionEntity partition;
#AttributeValue
private String username;
#AttributeValue
// #Transient
private boolean enabled;
#AttributeValue
private Date createdDate;
#AttributeValue
private Date expiryDate;
}
PL can map property with #AttributeProperty to entity property with #AttributeValue. But it can only map to one entity. Therefore there is no way to map, say User and its properties over to Account and Person. But you can have the entity (in my case accountEntity) in the model. I also have to duplicate a few fields in the new IdentityTypeEntity and my existing Account entity (username, eanbled, createdDate) because PL requires these. Use a #PrePersist and similar to sync them.