How Can I have one parent ENTITY and many types of child ENTITIES in JPA? - jpa

I am trying to design my ENTITY CLASSES using JPA annotations.
What I am trying to do is as follows :
A USER table with
id,
***email,
password,
activation_key,
active,
role***
and many TYPES OF USER table with for eg.
STUDENT_TABLE
USER_ID
FirstName
LastName
Company
Address
etc
MENTOR
USER_ID
FirstName
LastNAme
DOB
Department
etc
When USER will register depending o their ROLE they will be sotred in two tables (USER,MENTOR/STUDENT)
When they will log in , the ManagedBean will look into the UESR table to verify the authentication.
I tired to use #OneToOne but It just works with two tables.
I would really appreciate if any1 can help!!
Thanks

If I understand correctly, what you want is an inheritance relationship:
#Entity
#Inheritance(strategy = JOINED)
public abstract class User { ... }
#Entity
public class Student extends User { ... }
#Entity
public class Mentor extends User { ... }
The JOINED strategy tells JPA that the fields in the User class are stored in a table (USER), and that each subclass persists its own field in its own table (STUDENT and MENTOR), with a join column used to refer to the USER table ID.

Related

Best JPA Inheritance option to implement the following requirement

Having the following kind of table tables. What will be the good approach to persist these tables? Used inheritance strategy for this, but it didn't work as expected.
Requirement 1: Need to persist student table, it will persist the member as well as address table as well
Requirement 2: Need to persist teacher table, it will persist the member as well as address table as well
Need to perform get, update and delete option on these tables.
Member {
member_id - have one to one relation with student id and teacher id
lastupdateddate
latupdatedby
}
Student {
student id - have one to one relation with member id
student name
lastupdateddate
latupdatedby
}
teacher {
teacher id - have one to one relation ship with member
teacher name
lastupdateddate
latupdatedby
}
address {
address id
member_id - have one to one relationship with member class
lastupdateddate
latupdatedby
}
When I persist/update student details, the address related info is not properly inserted or updated.When I check insert queries fired on member, then student table after on address table. But, in the insert query to address table, the member_id value is coming as null.Because of this only address table is not populated.
Entity structure is is as given below
public abstract class Member implements Serializable {
}
public class Student extends member implements Serializable {
}
public class Teacher extends member implements Serializable {
}
public class Address implements Serializable {
}
The mapping is mentioned as given below. Tried out various available options.
In member entity class
#Access(AccessType.PROPERTY)
#OneToOne(mappedBy="member", cascade=CascadeType.ALL)
public Address getAddress() {
return postalAddress;
}
In address entity class
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "MEMBER_ID")
private Address address;
It looks like you have 2 options:
InheritanceType.JOINED. It's almost what you've described: common part is in one table, different parts are in different tables. On each request JOIN will occure
InheritanceType.SINGLE_TABLE. Here all the data will be stored in single table and descriminator will be used to determine if record if for student or for teacher.
Personally I would prefer second options because you have almost all fields in common, also most of operations are lighter and involve WHERE, not JOIN.

JaVers, SpringDatat, JPA: Querying for Entity Update inside a Collection

I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter

How to create association one to many and many to one between two entities in gorm?

I'm new in Grails. I have a problem with generation association many to one and one to many between two tables. I'm using postgresql database.
Employee.groovy
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
EmployeePlan.groovy
class EmployeePlan {
AcademicYear academicYear
HourType hourType
int hours
float weightOfSubject
Employee employee
static belongsTo = [SubjectPlan]
}
I'd like to have access from employee to list of employeePlans and access from EmployeePlan to Employee instance. Unfortunately GORM generates only two tables Employee and EmployeePlan with employee_id. I don't have third table which should have two columns employee_id and employee_plan_id. Could you help me ?
I think your setup is correct, as you write from Employee class you can access to a collection of EmployeePlan (take care, that if you don't explicitly define EmployeePlan like a List, it will be a Set by default) and from EmployeePlan you can access Employee.
If you need List, you can define it like that:
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
//explicitly define List
List<EmployeePlan> employeePlans
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
But back to your question. You'd like to have join table between Employee and employeePlan, but why? Its not necessary, since you have bidirectional mapping with sets (unordered), grails will not create a join table. Can you explain why do you need it? In grails the references will be auto-populated, so I don't see any issue here.
If need to preserve order of employeePlans, then define it as List, shown above, and grails will create a join table with corresponding indexes.
have you read the ref-doc? it gives you the answer immediately:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
firstName column: 'First_Name'
addresses joinTable: [name: 'Person_Addresses',
key: 'Person_Id',
column: 'Address_Id']
}
}

EJB inheritance strategy

BIG UPDATE:
Ok, I see my problem is much more complicated than I thought. I have tables like this:
Patient:
id
bloodtype
level (FK to level table)
doctor (FK to doctor table)
person (FK to person table)
Doctor:
id
person (FK)
speciality
level (FK to level)
Paramedic:
id
person (FK)
Person:
id
name
surname
Account:
id
login
pass
Level:
id
account (FK)
name (one of: 'patient' , 'paramedic' , 'doctor')
In entity class I'm using now #Inheritance(strategy= InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="name") in class Level. To check if someone is for ex. patient I have function:
public boolean ifPatient(String login) {
account = accountfacade.getByLogin(login);
for (Level l : account.getLevelCollection()) {
if (l instanceof Patient) {
return true;
}
}
return false;
}
Now I have situation: I'm logged in as a doctor. I want to add a patient. I have something like this:
public Doctor findDoctor(String login) {
account = accountfacade.getByLogin(login);
for (Doctor d : doctorfacade.findAll()) {
if (d.getLevel().getAccount().getLogin().equals(login)) {
doctor = d;
return doctor;
}
}
}
#Override
public void addPatient(Account acc, Patient pat, Person per, String login) {
Doctor d = findDoctor(login);
accountfacade.create(acc);
personfacade.create(per);
Patient p = new Patient();
p.setAccount(acc);
p.setBlood(pat.getBlood());
// etc
p.setPerson(per);
d.getPatientCollection().add(p);
acc.getLevelCollection().add(p);
}
But it doesn't work. Always totally weird errors like duplicate value of primary key in table Account (but I use TableGenerator...) or NULL value in field Account but for INSERT INTO Doctor (how?! I'm creating new Patient, NOT Doctor...). I'm totally lost now, so I think most important for me now is to know if actually I can use InheritanceType.JOINED in this case.
UPDATE:
You are using the field nazwa as discriminator
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="nazwa")
The framework stores there the name of the class that it has to use to deserialize the object (it is the name of the PoziomDostepu class).
As far as I can see you are using a different table for each class, so the Strategy.JOINED would make little sense.
More update:
Where I said class it meant entity. You can check the effect by changing the entity name (say to "CacaCuloPedoPis") of PoziomDostepu and seeing which is the new value being inserted.
Looks like all that is missing is a #DiscriminatorValue annotation on the PoziomDostepu class to use one of the values in your constraint, otherwise it defaults to the class name. The insert of PoziomDostepu is coming from the this.poziom - if you don't want PoziomDostepu instances inserted you might make the class abstract and make sure this instance is a subclass instead. You shouldn't need to change the inheritance type, as doing so will require changing the database tables to each contain the same fields.
Ok, I've done what I wanted. It's not elegant, but efficient. Tables and inheritance strategy are the same. In endpoint I create account and person entities, then I create Patient entity but using EntityManager persist() method. I also need to set id of level manually ((Integer)poziomfacade.getEntityManager().createQuery("SELECT max(p.idpoziom) FROM PoziomDostepu p").getSingleResult())+1 because Generator in Level entity class doesn't work. Nevertheless the problem is solved, thanks for every constructive comment or answer.

JPA, duplicate entry on persist in join table when using #ManyToMany relationship

I want to create custom JAAS authentication where my users and principals relationships are defined in JPA as shown:
class AuthUser
public class AuthUser implements Serializable {
// own properties
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int uid;
#Column(name="NAME",unique=true)
private String name;
// Join classes
#ManyToMany(fetch=FetchType.EAGER,cascade={CascadeType.MERGE})
#JoinColumn(name="PRINCIPALS_PRINCIPAL")
private Set<AuthPrincipal> principals;
}
class AuthPrincipal
public class AuthPrincipal implements Serializable {
// Defining roles
public enum Principal {
AUTHUSER, STUDENT, ADMINISTRATOR, TEACHER
}
#Id
#Enumerated(EnumType.STRING)
#Column(name="PRINCIPAL")
private Principal principal;
#ManyToMany(mappedBy = "principals")
#JoinColumn(name="USERS_USER")
private Set<AuthUser> users;
}
Maps to the following Table definition
Table authprincipal
===================
PRINCIPAL varchar(255) PK
Table authuser
==============
UID int(11) PK
EMAIL varchar(255)
NAME varchar(255)
PASSWORD varchar(255)
Table authuser_authprincipal
============================
users_UID int(11) PK
principals_PRINCIPAL varchar(255) PK
Now, I created a JSF file from which I call an action method that calls this one:
public void createUser(AuthUser newUser) throws UserNameExistsException, UserEmailExistsException {
AuthPrincipal role = authRoleFacade.find(AuthPrincipal.Principal.AUTHUSER);
if( role == null ){
role = new AuthPrincipal();
role.setPrincipal(AuthPrincipal.Principal.AUTHUSER);
authRoleFacade.create(role);
}
authUserFacade.create(newUser);
addPrincipalToUser(newUser, role);
}
The actual problem
I can create the first user. But I can't create the second user . Notice that at the second user I use the existing role object, and only cascade a merge operation.
The strangest thing is that it says it duplicates the 2-AUTHUSER key where 2 is the id of the new user so cannot already be in the database. What is wrong with it or with Eclipselink or with Me?
The error what eclipselink throws
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-AUTHUSER' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="principals" sql="INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)")
This was all My fault. I was not careful enough :)
1) The problem occured because of my fault, though, I don't know why the error log talked about user id 2 when I only had one user, but i'm gonna tell a possible reason
The problem was: I wanted to persist an already persisted user. In my #SessionScoped #ManagedBean I created an AuthUser in the constructor. In the first registration it got persisted but I did not create a fresh one. When I wanted to register the next one what my program actually did was: changed the username, email and password of the already persisted AuthUser and wanted to persist it again.
Back to 1) I can imagine when I called persist the second time, Eclipselink actually persisted my Entity updating the user id to 2 in both the AuthUser table and the join table. Afterwards because I defined the Merge operation on AuthUser.principals, it wanted to update again the join table and that's when it messed up. If I had looked closely to the generated Queries in the log file, I think I could have figured it out myself.
I got the hint here: http://www.eclipse.org/forums/index.php/m/692056/#msg_692056