How to save multiple entities FK to single attribute in Hibernate? - java

I have object Car with nested object User that can not be null.
class Car {
#Id
int id;
#NotNull
User user;
}
But I would like to upgrade Vehicle to also have option of being owned by Company. Car can not be owned by both at the same time. Is there an option in Hibernate to have FK in Car that can be used as User and Company? Here is example:
class Car {
#Id
int id;
#NotNull
Owner owner; //Can be object type User or Comapny
}
I have idea to make extra object UserRole which would have option to nest User and Company, but I'm searching for better solution/optimization that won't require extra object if it is possible in Hibernate:
class Car {
#Id
int id;
#NotNull
UserRole owner; //Can be object type User or Comapny
}
class UserRole{
#Id
int id; // PK, that will be used in Car
User user;
Company company;
int type; // determinates what if owner is User or Company
}
Implementation will be used to store entites on API.
Please suggest it there is easier way. Thanks.

First of all, you're saying that User is not nullable. Then you say that Car cannot be owned by both User and Company at the same time - means User is nullable.
You can't use the same field to both Company and User. In Hibernate each class is associated with a single entity. So my suggestion is to do
class Car {
#Id
int id;
#ManyToOne
User user;
#ManyToOne
Company company;
}
without the #NotNull annotation, and then just check if null or not.
Second option is to use inheritance (if possible). you can create a #MappedSuperClass called CarHolder and then make User and Owner inherit from it. then you can hold just one field of CarHolder inside Car.

You can use both of them:
class Car {
#Id
int id;
#NotNull
User user;
#NotNull
Company company;
}
And just checks the user property and the company property for null when you need to get an owner of a car.

Related

What is the difference between findById() and find<classname>ById()?

In my spring data project, there is an entity that looks like:
public class Employee {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="category")
private Category category;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="element_id")
private Department department;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="agency_id")
private Agency agency;
public Employee() {}
// routine getters and setters
}
In my EmployeeRepository, I find that I can derive a method, findEmployeeById() that seems to work in exactly the same way as the usual findById() (but I'm not sure). Can someone explain the difference between these two methods?
Optional<Employee> findEmployeeById (Integer id);
vs
Optional<Employee> findById (Integer id);
I "discovered" this by accident via autocomplete in my side (IntelliJ).
The difference is that while findEmployeeById() searches - as it states - employee by its field named id, findById searches by field annotated as #Id disregarding what is the name of the entity's id field.
In your case - as with many else's - the thing is that the #Id field happens to be named id so the result is the same.

How to represent and query from a three entity relationship in spring boot?

I am developing a mall system where a User can have one or more shops. If you create a shop, you have the role ADMIN for that shop, else: you create an account then you are assigned a shop as a MANAGER by the ADMIN of that shop. A user can have a shop that they are an admin, but as well be a manager to a different shop assigned to you by an owner of a that shop. Thus, i have come up with three entities: User, Role and Shop.
User Entity
#Entity
#Table(name = "us_users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank
private String uuid;
#Email
private String us_email;
//other fields
//getters/setters
}
Role Entity.
#Entity
#Table(name = "ro_roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String rn_role_name;
//setter getter
}
Shop Entity.
#Entity
#Table(name = "sh_shops")
public class Shop {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Shop name can not be left blank")
private String sh_name;
#NotBlank(message = "please provide shop icon")
private String sh_icon;
#NotBlank(message = "please fill the shop description")
private String sh_description;
//other fields.. getters and setters
}
I need a relationship between these three entities: something like a table having userId,shopId,RoleId in which a user can
Log in the system and the system is able to determine the roles under this user and for which shop.(Using Spring security GrantedAuthorities).
Show the user shops under his account and the user can only operate a shop with correct role.
Kindly assist on how to model this use case. Thank you
First, do you really need flexibility with the roles? Maybe having a role as a simple enum would be enough, at least if you don't plan on creating new roles at runtime. This would simplify the data model.
Second, this sounds like a map relationship for the user entity:
#Entity
#Table(name = "us_users")
public class User {
#ManyToMany
#MapKeyJoinColumn(name = "shop_id")
#JoinTable(name = "user_shop_role",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Map<Shop, Role> shopRoles = new HashMap<>();
}
Here, you create a three-way relationship using a user_shop_role table containing fields user_id, shop_id and role_id, meaning what you'd expect.
Then adding a shop role for the user is just a matter of adding a key-value relationship to this map:
user.getShopRoles().put(shop, role);
Showing the list of shops is just iterating through the map entries and showing a shop (key) with a corresponding role (value).
Since you use Shop as a key, you need to make sure that the Shop entity implements equals and hashCode methods correctly (based on its id).

Different bidirectional #ManyToOne parent for 2 Entities extending some base Entity, but keeping base entity data in one table

This title is closest I could get to what I am trying to do. :)
Let's start with how it should look in database (tables with columns):
Email
- id : PK
- email : String
CompanyEmail
- email_id : FK
- company_id : FK
PersonEmail
- email_id : FK
- person_id : FK
Company
- id : PK
Person
- id : PK
Now let's look at model:
#Entity
public class Company
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Person
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<PersonEmail> emails = new ArrayList<>();
}
// Should this be #Entity? Maybe #MappedSuperclass? What strategy to use to be able to extend it?
public class Email // This is base email class/entity
{
#Id
#GeneratedValue
private Long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Person person;
}
#Entity
public class ComanyEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Company company;
}
Now my question is - is it possible in Hibernate (latest) to achieve such structure?
Key points when designing above (what drove me):
Keep ALL emails in one table, for sanity checks (uniqueness).
Have smallest database footprint - above gives just one table for Email and then 2 join tables with 2 FK.
Still be able to keep the model design Hibernate-friendly (basically don't use any special queries, just JPA annotations). This means that both Company and Person can easily LAZY load their specific emails (subclasses), and also those subclassed emails can correspond to them (PersonEmail to Person, and CompanyEmail to Company) - making model bidirectional.
Note: I've also considered creating something like Contactable base class for Comapny and Person which would have Email list, but that doesn't suit my needs (neither class per table, nor same table designs).
My question is - is it possible? Even if I don't get answer with example based on classes I gave, just the fact will give me hope and I will find it.
Edit 1
Should I maybe use discriminator in Email table and then keep FK there?
Email
- id : PK
- email : String
- companyOrPerson : FK
- discriminator : Decides what FK points at
Company
- id : PK
Person
- id : PK
Here I am grabbing straws - I have no idea if such thing is possible, can discriminator decide about parent table (companyOrPerson)? Does bidirectional work here (like I mentioned, maybe I should make some base class Contactable)?
I am open to suggestions on how to do it well.
Favour composition over inheritence. It tends to simplify things.
If you had main entities of
Person
Company
Email
Then a couple of composites
PersonEmail (contains a Person and an Email)
CompanyEmail (contains a Company and an Email)
Would that not fit what you need?
e.g.
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany
#JoinColumn(name="person_id")
private List<PersonEmail> emails = new ArrayList<>();
}
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany()
#JoinColumn(name="company_id")
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Email {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="person_id", referencedColumnName="id")
private Person person;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}
#Entity
public class CompanyEmail
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="company_id", referencedColumnName="id")
private Company company;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}

How to insert to subclass without add another instance to superclass?

I use standard JpaRepository interfaces with the classes.
I have this classes:
User:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.JOINED)
public class User extends BaseEntity {
private Long id;
//Other Attr
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
//Getters & Setters...
}
Doctor:
#Entity
#Table(name = "doctor")
#PrimaryKeyJoinColumn(name = "id", referencedColumnName="id")
public class Doctor extends User {
private Long regNumber;
//Getters & Setters
}
I was searching and it seems that jpa/hibernate have no function/procedure for inserting a Doctor with info from an existing User.
I've tried to save a Doctor with info from a User query, it made a new User entry for the User table.
In theory, one can be an user without being a doctor, later one can become doctor.
In theory, one can be an user without being a doctor, later one can become doctor.
That's true in real life, but not with Java inheritance: an object of a given type can not become an object of another type. If you want to model that situation, inheritance is the bad tool.
You should use composition instead. A User can have a Doctor role. That's a OneToOne association, that can be mapped with the same tables as the ones you used to map your inheritance, and which allows a user to become a doctor.
It's also better design, because it allows for multiple roles: a user is a doctor AND a musician, for example. Something that would also not be possible with inheritance.

One to one using JPA annotations Hibernate

I'm facing several problems setting up a One to one associations.
Can you help me set up the relationship using annotations for the following table.
Login -> (id, username, password)
Members -> (member_id, name, address)
Here member_id is same as id in login.
Here's the example that the Hibernate documentation provides:
#Entity
class MedicalHistory implements Serializable {
#Id Integer id;
#MapsId #OneToOne
#JoinColumn(name = "patient_id")
Person patient;
}
#Entity
class Person {
#Id #GeneratedValue Integer id;
}
Replace Person with Login, and MedicalHistory with Member, add the fields you need, and you have your answer.

Categories

Resources