I have following entities, I am in doubt about the design design like should DRIVING_LICENSE table contain foreign key PERSON_ID or PERSON table should have LICENSE_NUMBER as foreign key from DRIVING_LICENSE table?
If PERSON table has LICENSE_NUMBER then PERSON table will be child table and DRIVING_LICENSE will be parent table, so it implies that when a driving license is deleted then the person should be deleted.
On the other way if DRIVING_LICENSE will have a PERSON_ID then in uni directional one to one relationship in hibernate we will not be able to have a reference of DrivingLicense instead we will have a reference of Person in DrivingLicense, but most of the time it requires that we access Person not DrivingLicense.
Above two are my main doubts? What is the correct choice and what are its pros and cons?
DrivingLicense.java
#Entity
#Table(name = "DRIVING_LICENSE")
public class DrivingLicense {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "LICENSE_NUMBER")
private int licenseNumber;
#Column(name = "DATE_OF_ISSUE")
private Date dateOfIssue;
#OneToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
and
Person.java
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PERSON_ID")
private int personId;
#Column(name = "PERSON_NAME", nullable = false, length = 30)
private String personName;
#OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
private DrivingLicense drivingLicense;
}
Drivers License should contain a NOT NULL foreign key to person with a unique constraint and here's why:
Every License must be associated with a person.
A person may have zero or one license associated with them.
Since the license must be associated with a person but a person does not need to have a license, the foreign key should be held by the license table.
The unique constraint on the foreign key will enforce the one-to-one relationship. Without it you'd have a one-to-many relationship.
Related
I have read about the use of #MapsId and #PrimaryKeyJoinColumn annotations, which sounds like a great options. I have two tables (UserList and UserInformation) which have a child, parent relationship, respectively; both classes below are abbreviated to just include the relevant columns. UserInformation's primary key value is always null and does not take the value of its parent column.
User Class
#Entity
#Data
#Table(name = "user_list")
public class UserList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// List of foreign keys connecting different entities
#OneToOne(mappedBy = "user")
#MapsId("id")
private UserInformation userInfo;
}
UserInformation Class
#Entity
#Data
#Table(name = "user_information")
public class UserInformation implements Serializable {
#Id
private Integer userId;
#OneToOne
private UserList user;
}
I would prefer to not use an intermediary class if possible. I'm not tied to MapsId or even this implementation if there is a better solution.
Thanks!
The question is not very clear to me, but I think you could improve the following in the modeling of the entity:
The #column annotation can only be omitted when the class parameter is called exactly the same as the database column, taking into account the table name nomenclature, could it be that the column is user_id ?, if so the id parameter should be :
#Id
#column(name="USER_ID")
private Integer userId;
In the user entity being id, it will match the DB ID field so the #column annotation is not necessary
using spring data, I created User 1:N UserDog N:1 Dog relation. Both 1:N relations are unidirectional #ManyToOne with UserDog being the custom relation table.
User entity:
#Entity
public class User {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
}
Dog entity:
#Entity
public class Dog {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
}
User dog relation table:
#Entity
public class UserDog {
#Id
#GeneratedValue
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
#OnDelete(action = OnDeleteAction.CASCADE)
private Dog dog;
#Column(nullable = false)
private Instant createdOn = Instant.now();
#Column
private Instant disabledOn;
}
Use case
Use case is to store history of User-Dog bindings, where the concrete Dog can be bound only to one User at the time. That's why I added createdOn and disabledOn columns to UserDog. disabledOn being null indicates that the relation is active and the Dog can't be assigned another User. If disabledOn is not null, then the record is stored only for evidence purposes and the Dog can be assigned to the same or another User again.
Question
How to ensure that the combination of Dog's id and disabledOn being null is unique in UserDog table?
In pseudo code I want something like this:
#Entity
#UniqueConstraint({#UniqueConstraint(this.dog.id), #NullConstraint(this.disabledOn)})
public class UserDog {...}
You can simply create a unique constraint for dogId and disabledOn.
It does add the limitation that no two relationships may end at the same time but this seems to fit your use case.
There is a class Consultant. A consultant can have many kind of experience like salaried, self-employed, freelancer. For each type of experience there are different data to save in database.
Salaried:
Total Experience
Company Name
Experience Time in years
Offer/Reveling letter Link
Self Employed:
Company Name
Total Experience
CIN_Number
GST_Number
CompanyCertificateLinkUrl
FreeLancer:
Total Experience
A user can have experience in more than one occupation type like a consultant is both salaried and freelancer, or self employed plus salaried and freelancer. So i am confused how to make the #Entity class for this type of use case.
My Solution
#Entity
class Consultant{
#Id
int id;
#OneToOne
Salaried semp;
#OneToOne
SelfEmployed selfemp;
#OneToOne
Freelancer femp;
}
But i think this is not good practice as it will lead to many null field in the database.
ANY BETTER SOLUTION
I think your approach is fine. #OneToOne fields are optional by default, so can be null. That means there wouldn't be a row in the corresponding tables, so you'd only have up to two null values per row in the Consultant table.
If you're really concerned about nulls in the database, then you can map the relationships the other way, so:
#Entity
class Consultant{
#Id
int id;
#OneToOne(mappedBy = "consultant")
Salaried semp;
#OneToOne(mappedBy = "consultant")
SelfEmployed selfemp;
#OneToOne(mappedBy = "consultant")
Freelancer femp;
}
This way, if there's no row in the Salaried table that's related to the Consultant, the semp field will be null in the Consultant object.
you can do with two classes consultant and profession(id,name) and the relation OneToMany,ManyToOne
Consultant's entity
#Entity
class Consultant{
#Id
private int id;
#OneToMany(mappedBy = "consultant",cascade = CascadeType.All)
List<ConsultantProfession> cp;
}
Profession's entity
#Entity
class Profession{
#Id
private int id;
private String name;
#OneToMany(mappedBy = "profession", cascade = CascadeType.All)
private List<ConsultantProfession> cp;
}
ConsultantProfession's entity
#Entity
#Table(name="consultant_profession")
public class ConsultantProfession{
#Id
private int id;
// this is the link to the consultant class
#ManyToOne
#JoinColumn(name="consultant_id")
private Consultant consultant; // this name is specified in the class seeing patients as value of parameter `mappedBy`
// this is the link to the profession class
#ManyToOne
#JoinColumn(name="profession_id")
private Profession profession;
}
I have a postgres database and I am trying to make a simple REST service with Spring Boot. I have a problem with jpa ManytoMany relationship.
Person Entity:
#Entity
#Table(name = "person", schema = "persons")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column
private Integer age;
#ManyToOne
#JoinColumn(name = "country_id", referencedColumnName = "id")
private Country countryOfBirth;
#ManyToMany
#JoinTable(
name="persons_countries_residence",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
// getters and setters and to String method overriden
}
Country Entity:
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
// getters and setters and to String method overriden
}
The postgres schema is the following:
Person Table:
CREATE TABLE persons.person
(
id serial NOT NULL,
name character varying(50) NOT NULL,
email character varying(40) NOT NULL,
age integer,
country_id serial NOT NULL,
CONSTRAINT id PRIMARY KEY (id),
CONSTRAINT country_id FOREIGN KEY (id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Country table:
CREATE TABLE persons.country
(
id serial NOT NULL,
country_name character varying(45) NOT NULL,
country_code character varying(10) NOT NULL,
CONSTRAINT country_id PRIMARY KEY (id)
)
Join table:
CREATE TABLE persons.persons_countries_residence
(
person_id integer NOT NULL,
country_id integer NOT NULL,
CONSTRAINT person_country_id PRIMARY KEY (person_id, country_id),
CONSTRAINT persons_countries_residence_country_id_fkey FOREIGN KEY (country_id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION,
CONSTRAINT persons_countries_residence_person_id_fkey FOREIGN KEY (person_id)
REFERENCES persons.person (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
When i make an HTTP method call, I don't get the Countries of residence.
Service code:
#RequestMapping(method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Person> getAllPersons() {
retutn jpaPersonRepository.findAll();
}
Any help appreciated.
Maybe, you need to specify a schema name in the join table name:
#JoinTable(
name="persons_countries_residence", schema="persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
Update your Country class code like :
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
#ManyToMany(mappedBy = "countriesOfResidence")
private List<Person> persons;
// getters and setters and to String method overriden
}
Although a ManyToMany relationship is always bi-directional on the
database, the object model can choose if it will be mapped in both
directions, and in which direction it will be mapped in. If you choose
to map the relationship in both directions, then one direction must be
defined as the owner and the other must use the mappedBy attribute to
define its mapping. This also avoids having to duplicate the JoinTable
information in both places.
Do you mean that the country list is null? #ManyToMany associations are lazily loaded by default, you need to enable eager-fetching for it to work straight away.
#ManyToMany(fetch = FetchType.EAGER)
The solution is this:
#ManyToMany
#JoinTable(
name="persons_countries_residence", schema = "persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
The schema had to be specified at the #JoinTable annotation as well.
Is it possible that oracle table has a composite primary key oracle sequenceid +createtimestamp but the #Entity class we have just the #id (sequenceid) primary key ? Timestamp we are adding for the purpose of table partitions which we use for purging later. At the time of storing the entity we will add the timestamp value all the time. From the data point of view id alone is primary key for the record in this case. Can I create entity with primary key as id alone?
In this case you have to create composite primary key in # Entity class. You can as per below example
#Entity
#Table(name="RELEASE_PERSON")
#IdClass(ReleasePersonPK.class)
public class ReleasePerson implements Serializable {
#Column(name="ORDER_NO", nullable=false, precision=2)
private Integer orderNo;
#Id
#Column(name="RELEASE_ID", insertable=false,updatable=false)
private long releaseId;
#Id
#Column(name="PERSON_ID", insertable=false,updatable=false)
private long personId;
#ManyToOne
#JoinColumn(name = "PERSON_ID", referencedColumnName = "PERSON_ID")
private Person person;
#ManyToOne
#JoinColumn(name = "RELEASE_ID", referencedColumnName = "ID")
private Release release;
And your Id Class will be look like below
#Embeddable
public class ReleasePersonPK implements Serializable {
/**
*
*/
private static final long serialVersionUID = -6286499269052596345L;
private Person person;
private Release release;
In my example in ReleasePersone table we will have composite primary key from rRelease(ID) and Person (Person_id).
Let me know if you need anything else.
The answer is yes. as you said that the id will be unique in the table, it's definitely ok that just put sequenceId as PK in the entity. If you don't use hibernate to create the table or update the columns of table, it doesn't care what actually in the database.