Overlapping relationship represented by spring JPA - database

I'm building a webpage backend from scratch as my first practice, using Spring boot, JPA and oracle database. Inside my database, there is a User table containing some information. And there are three other tables, Worker, Customer and Admin. A User can have several roles, for example, User can be both Worker and Admin. (I think it's a overlapping relationship as stated in EER diagram, correct me if I'm wrong)
Now I want to implement this relationship using JPA in Spring boot, but I don't know how exactly should I design the beans. Could someone give me a hint or similar example?
If spring JPA is not a good way to implement this kind of relationship, or if it's too complicate for me to understand. is there any better way to restructure my database table?
I've looked into JPA inheritance such as this post Spring Data Repository with Inheritance.JOINED but I didn't manage to make my code work, since I don't quite understand the mechanism behind
Here is a example of my database tables:
User(Id, username, password, ...)
Worker(Id, user_id, salary, ...)
Admin(Id, user_id, team_name, ...)
Customer(Id, user_id, account_balance, ...)
ps: As the homework requirement stated, I'm not suppose to use primary key or foreign key in the database since JPA can handle relationships.

user-> worker is one-one relationship as a user can be only one worker. the same applies to user-> admin and user-> customer. you can find mock-up code as below.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private Worker worker;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private Admin admin;
// ... getters and setters
}
#Entity
#Table(name = "worker")
public class Worker {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne (mappedBy = "worker")
private User user;
}

Related

How to Enforce Foreign Key Constraint on Delete with JPA and H2 Database

I have a Spring Boot application which has 2 tables in a h2 database: Computer and a Processor.
There is a unidirectional, many-to-one relationship between Computer and Processor such that many Computers can have one Processor.
#Entity
#Table(name = "Computer", schema = "CS")
public class Computer extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "computer_id")
#Getter
#Setter
private long id;
...
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "processor_id")
#OnDelete(action= OnDeleteAction.NO_ACTION)
#Getter
#Setter
private Processor processor;
...
}
#Entity
#Table(name = "Processor", schema = "CS")
public class Processor extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "processor_id")
#Getter#Setter
private long id;
...
}
I want to enforce at a database level that when I try to delete a Processor that is referenced by at least one Computer, JPA does not allow the delete to be completed due to a foreign key constraint.
I understand that this functionality can be completed programmatically, such as finding all of the Computers associated with a certain Processor, however this seems like a code smell and it would be ideal to have JPA annotations take care of this for me, if it is possible.
Thank you!
Try with the #ForeignKey annotation, I haven't used it before but from the docs it should do what you want:
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/ForeignKey.html
An alternative, use a the entity life cycle callbacks (see https://www.baeldung.com/jpa-entity-lifecycle-events). A #PreRemove method where you check if there is any entity depenent on the one you are deleting and throw a custom runtime exception... But the first approach should be enough
Thank you for the help; I have resolved the issue.
There was an error in the foreign key constraint in my database schema. The annotations worked correctly, and the #OnDelete(action= OnDeleteAction.NO_ACTION) annotation is unnecessary.

Explain the JPA term "owner"

Given the following JPA model:
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer")
private ShippingAddress shippingAddress;
//...
}
#Entity
public class ShippingAddress {
#Id
private long id;
#OneToOne
private Customer customer;
//...
}
This will create a foreign key to the customer table in the shipping_address table i.e. shipping_address.customer_id.
From a database point-of-view, one would say that customer is the parent table and shipping_address is the child table, because
An address cannot exist without a customer
If a customer is deleted, their address must also be deleted
However, according to JPA, the owner of this relationship is ShippingAddress because the JavaDoc for mappedBy states
This element is only specified on the inverse (non-owning) side of the association.
I find it extremely confusing that the owner of the relationship according to JPA is what (from the database point-of-view) would be considered the child table. Is there a way to reconcile this apparent contradiction?
In short: as posted before here and here, the owning side of the relation is the one that holds the FK in the DB table.
You can read this excellent answer for a full explanation why.

When do you store ids and when do you store keys in gae datastore?

Suppose my datastore model looks like this:
#Entity
public class User {
#Id
private Long id;
#Index
private String email;
private Long dateOfBirth;
// More fields...
}
#Entity
public class Topic {
#Id
private Long id;
private String topicTitle;
private Long date;
}
#Entity
public class Comment {
#Id
private Long id;
#Parent
private Key<Topic> topicKey;
private Long commenterId;
private String text;
private Long date;
}
Where the entity Comment has a parent entity Topic. I know one should store keys when specifying the #Parent such as I did in the Comment entity, but should one also store the key of the commenterId? Or is storing the Long id of that User enough?
Just wondering what the best practice is for storing references to other entities when they are not parents - should you store the id and generate the key later or just store the key to the entity. Is there a good reason why you might do one over the other?
EDIT: Since I am using Cloud Endpoints, the responses I get from my AppEngine project are JSON. Parameterized type of Key not allowed in the client libs. So for me, id can work and also Key<?> can work. Just note that you should return a websafe version of to your client using:
myKey.getString();
Typically there is no reason to store a key as a reference. Keys take much more space - both in the datastore, and in objects that you transfer to and from the client.
Using a key may be necessary only if the same entity kind can be either by itself or a child of another entity. It is technically possible, and some data models can use this approach, although it is probably a very rare use case.
NB: I only use ID of a parent in objects - for the same reason (less space). In datastore entities parent ID can always be extracted from a child entity key. I use low-level Datastore API, however - you need to check how to correctly annotate child-parent relationship in the library that you use.

hibernate relationship mapping

So if I have users and address tables. Because each users can buy or sell things so in the users table should have return_address, shipping_address, and billing_address. Then how can I define the relationship of these two tables?
Many-to-Many: because multiples users have multiple addresses (different type of address).
One-to-One: because each user only have one address.
One-to-Many: many users can share one address.
After reading some tutorials about hibernate, I find myself so confusing because now it has Many-to-One (I understand that it is a reversion of One-to-Many, but still confusing) .
Would any one mind to give me some advises and suggestions designing the database as well as optimization the database/query performance? It would be wonderful to help a new learner like me reduce the headache.
Thank you in advance.
User table will hold the address key.
#Entity
public class Customer{
#ManyToOne
#JoinColumn(name = "return_address_fk")
private Address returnAddress;
#ManyToOne
#JoinColumn(name = "shipping_address_fk")
private Address shippingAddress;
#ManyToOne
#JoinColumn(name = "billing_address_fk")
private Address billingAddress;
}
#Entity
public class Address{
// address properties
}
** Do not get confused.
Address should be a referred entity, means it's lifecycle does not depend upon User.
1.) Many users can have same returnAddress.
2.) Many users can have same shippingAddress.
3.) Many users can have same billingAddress.
If you say, One user can have Many returnAddress, shippingAddress, billingAddress, then add Many To Many annotation. Separate table will be created.

Hibernate Annotations to create relationship

I've got 3 classes, one superclass and two subclasses. Each subclass has a collection of the other subclass and I want to map a one to many relationship between them. My superclass is person, and the subclasses are called referrer and broker. The relationship I want to represent is that one referrer can have many brokers and that one broker can have many referrers.
#Component
#Entity
#Table(name="Referrer")
#PrimaryKeyJoinColumn(name="rowID")
public class Referrer extends Person implements Serializable{
private static final long serialVersionUID = 972571370198603227L;
#Column(name="rowId")
private String referrerID;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn
private List<Broker> brokers = new ArrayList<Broker>();
And broker looks like this:
#Component
#Entity
#Table(name="Broker")
#PrimaryKeyJoinColumn(name="rowID")
public class Broker extends Person implements Serializable {
private static final long serialVersionUID = 5648239413711716027L;
#Column(name="AdminID", nullable=true)
private String adminID;
#Column
private boolean isAdmin = false;
#OneToMany(cascade=CascadeType.ALL, mappedBy="referrer")
private List<Referrer> referrers = new ArrayList<Referrer>();
The primary key for Broker should be a field called rowID that is contained within the Person superclass.
I'm really stuck, so any help would be greatly appreciated.
In the end what you have here is a ManyToMany relationship. One question aside, are you using a join table? It looks like thats the case. Basicaly what you try is this:
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(
name = "jointablename",
joinColumns = #JoinColumn(name = "FlexRowId"),
inverseJoinColumns = #JoinColumn(name = "FlexRowId")
)
private List<Referrer> referrers = new ArrayList<Referrer>();
Basically thats what you would need to add in both entities. Probably you can then ommig the #PrimaryKeyJoinColumn(name="FlexRowID"). But I have to admit that I don't know how it would work out using basicaly the same column for mapping on both sides. So please keep me posted on ho it works out.
If the primary key for Broker is in Person then my approach is this
Define one-to-one join relation ship between Person and Broker each having its own table
Use Person's PK in foreign-id generator for Broker (make it bi directional so that cascade will work fine)
Repeat the same for Referrer (it has its own table)
Introduce another intermediate table having two columns (of course with different names) composite PK of both tables (Broker and Referrer). Use many-to-many relation ship for Broker (and Referrer) and Join on respective PK columns on intermediate table.

Resources