How to create relationship between two tables mysql - java

I have two tables:
topics table:
CREATE TABLE `topics` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`topicId` varchar(45) NOT NULL,
`title` text NOT NULL,
`details` text,
`dayPosted` varchar(45) DEFAULT NULL,
`username` varchar(45) DEFAULT NULL,
`userImage` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8;
comments table:
CREATE TABLE `comments` (
`commentId` int(11) NOT NULL,
`topicId` varchar(45) NOT NULL,
`comments` text,
`commentDate` varchar(45) DEFAULT NULL,
PRIMARY KEY (`commentId`),
KEY `topicId_idx` (`topicId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
topicId column is common in both the tables and I want to create relationship between both the tables. The problem is topicId is not a primary key(I can make it unique though)
Or do I create a third table like topics_comments and have both:
topicId as foreign key relationship to topicId in topics with
cascade on update and delete
commentId as foreign key relationship to commentId in comments with
cascade on update and delete
Actual scenario is I have blog posting site where users will comment on that blog where each blog topic has a topicId and each user comment has commentId and now I have create tables based on condition:
Each blog topic can have multiple comments
when blog topic is deleted, all the comments on that blog topic must
be deleted
Users who posted comments can later delete them or edit them
I am using mysql with spring boot and spring jpa
Entity class:
#Entity
#Table(name = "topics")
public class TopicBean implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "topicId")
private String topicId;
#Column(name = "title")
private String title;
#Column(name = "details")
private String details;
#Column(name = "username")
private String username;
#Column(name = "userImage")
private String userImage;
#Column(name = "dayPosted")
private String dayPosted;
//Getters and setters
}

You can stick to your current schema and still be able to define working mappings with JPA. Here is the set-up that worked for me in local environement(only the important bits here):
Topics
#Entity
#Table(name = "topics")
public class Topic implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "topicId")
private Integer topicId;
#OneToMany(mappedBy = "topic", cascade = {CascadeType.REMOVE, CascadeType.MERGE})
private List<Comment> comments;
Comments
#Entity
#Table(name = "comments")
public class Comment implements Serializable{
#Id
#Column(name = "commentId")
private Integer commentId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "topicId", referencedColumnName = "topicId")
private Topic topic;
Just remember to add unique index on TOPICS.topicsId.
Regarding the linking table scenario, well i would use it if you are really have as:
Your schema design seems to be explicitly uni-directional where Topics have comments and comment has only one owning topic. So this would be kind of a logical flaw.
IF you would be querying from comments and joining to topics, you would need additional join, which could slow down on performance
Getting the Topic for a Comment using JPA would be a bit awkward because you would be getting a List of Topics and then you would need to explicitly get the first entry.
You would end up with obsolete / redundant topicId column in your Topics table.
The plus is of course that you would be joining properly, that is using primary keys, so the decision is up to you. Here are the mappings:
Topics
#Entity
#Table(name = "topics")
public class Topic implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#ManyToMany
#JoinTable(name="COMMON",
joinColumns=#JoinColumn(name="TOPIC_ID"),
inverseJoinColumns=#JoinColumn(name="COMMENT_ID"))
private List<Comment> comments;
Comments
#Entity
#Table(name = "comments")
public class Comment implements Serializable{
#Id
#Column(name = "commentId")
private Integer commentId;
#ManyToMany
#JoinTable(name="COMMON",
joinColumns=#JoinColumn(name="COMMENT_ID"),
inverseJoinColumns=#JoinColumn(name="TOPIC_ID"))
private Topic topic;

Related

JPA mappedBy reference an unknown target entity

I am writing a simple inventory database that contains tables for products, orders and customers. The database definition can be found here:
CREATE TABLE public.customers
(
id integer NOT NULL DEFAULT nextval('customers_id_seq'::regclass),
title character varying(10) COLLATE pg_catalog."default" NOT NULL,
first_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
middle_names character varying(50) COLLATE pg_catalog."default",
last_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
email character varying(50) COLLATE pg_catalog."default" NOT NULL,
phone_number character varying(50) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT customers_pkey PRIMARY KEY (id)
)
CREATE TABLE public.products
(
id integer NOT NULL DEFAULT nextval('products_id_seq'::regclass),
name character varying(100) COLLATE pg_catalog."default" NOT NULL,
sku integer NOT NULL,
inventory_on_hand integer NOT NULL,
reorder_threshold integer NOT NULL,
price numeric(5,2),
inventory_to_be_shipped integer NOT NULL,
CONSTRAINT products_pkey PRIMARY KEY (id)
)
CREATE TABLE public.order_items
(
id integer NOT NULL DEFAULT nextval('order_items_id_seq'::regclass),
product_id integer NOT NULL,
order_id integer NOT NULL,
CONSTRAINT order_items_pkey PRIMARY KEY (id),
CONSTRAINT order_items_order_id_fkey FOREIGN KEY (order_id)
REFERENCES public.orders (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT order_items_product_id_fkey FOREIGN KEY (product_id)
REFERENCES public.products (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
CREATE TABLE public.orders
(
id integer NOT NULL DEFAULT nextval('orders_id_seq'::regclass),
customer_id integer,
order_date date NOT NULL DEFAULT now(),
arrival_date date,
CONSTRAINT orders_pkey PRIMARY KEY (id),
CONSTRAINT orders_customer_id_fkey FOREIGN KEY (customer_id)
REFERENCES public.customers (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I am trying to implement a Spring Security Resource server to perform CRUD operations on the database. I have implemented entity classes for each table in the database but when try to start the server I get a
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: edu.finalyearproject.imsresourceserver.models.Order.customers in edu.finalyearproject.imsresourceserver.models.Customer.orders
My entity and repository classes can be found below:
Product.java:
#Entity
#Table(name = "products")
#Data
public class Product
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer sku;
private Float price;
private Integer inventory_on_hand;
private Integer reorder_threshold;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
Customer.java
#Entity
#Table(name = "customers")
#Data
public class Customer
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String title;
private String first_name;
private String middle_names;
private String last_name;
private String email;
private String phone_number;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
Order.java
#Entity
#Table(name = "orders")
#Data
public class Order
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
private Date order_date;
private Date arrival_date;
#ManyToMany(mappedBy = "orders", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Product> products = new HashSet<>();
}
I know the problem is related to the relationships between the entities, but I haven't been able to find a solution. Any help would be greatly appreciated.
Try to correct this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "orders", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
to this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
See additional explanation in the documentation.
And you should correct also your Product-Order #ManyToMany association. Only one side of this association should use #JoinTable other side should use mappedBy property of the #ManyToMany annotation. Something like this:
#Entity
public class Product
{
// ...
#ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY
)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
#Entity
public class Order
{
// ...
#ManyToMany(
mappedBy = "orders",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY)
private Set<Product> products = new HashSet<>();
}
As it is stated in the documentation:
For #ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
Also as this is explained in this section of the documentation:
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those which, in turn, can lead to N+1 query issues.
For this reason, you should prefer LAZY associations.

#PrimaryKeyJoinColumn: Joining tables by primary key not working

I have below two tables:
`message` (
`id` varchar(150),
`Message` varchar(1000) ,
PRIMARY KEY (`id`)
)
fallback_status (
message_key varchar(150),
fallback_delivere` tinyint(1),
fallback_read tinyint(1),
fallback_answered tinyint(1),
PRIMARY KEY (message_key)
)
I don't have any foreign key relationship at database level. I have created two entity for these tables
#Entity
#Table(name = "message")
public class Message {
#Id
#Column(name = "id", unique = true)
private String key;
#Column(name = "message")
String message
#OneToOne
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "message_key")
private MessageFallbackStatus messageFallbackStatus;
}
and
#Entity
#Table(name = "fallback_status")
public class MessageFallbackStatus {
#Id
#Column(name="message_key")
private String messageKey;
#Column(name="fallback_delivered")
private boolean fallbackDelivered;
#Column(name="fallback_read")
private boolean fallbackRead;
#Column(name="fallback_answered")
private boolean fallbackAnswered;
}
With this configuration Hibernate not able to persist data in fallback_status table.
my questions are:
Is the foreign key relationship is mandatory at database level?
what would be the proper mapping for this scenario?
thanks for any help.

What is the best way to map data of junction table in Java entity?

I have three tables which presents my very simple project.
CREATE TABLE company (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(250) NOT NULL
);
CREATE TABLE employee (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(250) NOT NULL
);
CREATE TABLE company_employee (
company_id INT NOT NULL,
employee_id INT NOT NULL,
hire_date DATE DEFAULT NULL,
resign_date DATE DEFAULT NULL,
FOREIGN KEY (company_id) REFERENCES company (id),
FOREIGN KEY (employee_id) REFERENCES employee (id)
);
And I have Java representation
#Entity
#Table(name = "employee")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Integer id;
#Column(name = "name")
String name;
#ManyToMany(mappedBy = "employees")
private List<Company> companies;
#Entity
#Table(name = "company")
public class Company implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "name")
private String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "company_employee",
joinColumns = {#JoinColumn(name = "company_id")},
inverseJoinColumns = {#JoinColumn(name = "employee_id")})
private Collection<Employee> employees;
I can't figured out where I can store history data. In history data I need to store hire_date, resign_date of emplyee and to store all companies of each emplyee as well. So my question is haw can I manage such infortation and what is the best way to store all that history info?
That is a many to many relationship with attributes. I am sure you will be able to understand who to deal with it with one basic example:
http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/
You have already an intersection table "company_employee".
You can store here employee1 1.1.2013 to 31.12.2013 # company1. And an other time 1.1.2014 ... 31.12.2014 # company2.
You have to order by hire_date desc. Per defenition the first value in the list is the current or last employment and all other are consindert as history.
It's not a matter of where to store but how to read the data.

Mapping extended entities with multiple ID's in Hibernate

I have implemented a db which is consisted of Article and Fruit tables, with the following specifications:
create table Fruit
(
ART_ID bigint,
FRU_ID bigint not null auto_increment,
FRU_FROZEN varchar(15),
primary key(FRU_ID)
);
# Implemented
create table Article
(
ART_ID bigint,
ART_NAME varchar(10) not null,
ART_COST varchar(10) not null,
primary key(ART_ID)
);
alter table Fruits add constraint FK_FRUIT_ARTICLE foreign key (ART_ID)
references Article (ART_ID) on delete restrict on update restrict;
and with following class entities :
Article.java
#Entity
#Table(name = "Article")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Article implements Serializable
{
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "ART_ID")
private Long id;
#Basic(optional = false)
#Column(name = "ART_NAME")
private String name;
#Basic(optional = true)
#Column(name = "ART_COST")
private String cost;
// constructors, getters and setters
Fruit.java
#Entity
#Table(name="Fruit")
public class Fruit extends Article{
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "FRU_ID")
private long fruitID;
#Basic(optional = false)
#Column(name = "FRU_FROZEN")
private String fruitFrozen;
// constructors, getters and setters
Now, my problem is how to have ID in Article and in Fruit, if I keep it this way, it throws me exception that sub-class must not contain IDClass because it will result with multiple ID's. Any help is appreciated. Thanks in advance.
It means what it says, an Entity cannot have two IDs.
Consider the operation
entityManager.find(Article.class, 1l);
How is it supposed to know which one to return if there are two articles (an article and a fruit) that both have id 1?
If your tables need to each have IDs, entity inheritance is not an appropriate solution. Consider putting the common elements in an #MappedSuperclass instead.

JPA #OneToOne with Shared ID -- Can I do this Better?

I’m working with an existing schema that I’d rather not change. The schema has a one-to-one relationship between tables Person and VitalStats, where Person has a primary key and VitalStats uses the same field as both its primary key and its foreign key to Person, meaning its value is the value of the corresponding PK of Person.
These records are created by external processes, and my JPA code never needs to update VitalStats.
For my object model I’d like my Person class to contain a VitalStats member, BUT:
When I try
#Entity
public class Person{
private long id;
#Id
public long getId(){ return id; }
private VitalStats vs;
#OneToOne(mappedBy = “person”)
public VitalStats getVs() { return vs; }
}
#Entity
public class VitalStats{
private Person person;
#OneToOne
public Person getPerson() { return person; }
}
I have the problem that VitalStats lacks an #Id, which doesn’t work for an #Entity.\
If I try
#Id #OneToOne
public Person getPerson() { return person; }
that solves the #Id problem but requires that Person be Serializable. We’ll get back to that.
I could make VitalStats #Embeddable and connect it to Person via an #ElementCollection, but then it would have to be accessed as a collection, even though I know that there’s only one element. Doable, but both a little bit annoying and a little bit confusing.
So what’s preventing me from just saying that Person implements Serializable? Nothing, really, except that I like everything in my code to be there for a reason, and I can’t see any logic to this, which makes my code less readable.
In the meantime I just replaced the Person field in VitalStats with a long personId and made that VitalStats’s #Id, so now the #OneToOne works.
All of these solutions to what seems (to me) like a straightforward issue are a bit clunky, so I’m wondering whether I’m missing anything, or whether someone can at least explain to me why Person has to be Serializable.
TIA
To map one-to-one association using shared primary keys use #PrimaryKeyJoinColumn and #MapsId annotation.
Relevant sections of the Hibernate Reference Documentation:
PrimaryKeyJoinColumn
The PrimaryKeyJoinColumn annotation does say that the primary key of
the entity is used as the foreign key value to the associated entity.
MapsId
The MapsId annotation ask Hibernate to copy the identifier from
another associated entity. In the Hibernate jargon, it is known as a
foreign generator but the JPA mapping reads better and is encouraged
Person.java
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "person_id")
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private VitalStats vitalStats;
}
VitalStats.java
#Entity
public class VitalStats
{
#Id #Column(name="vitalstats_id") Long id;
#MapsId
#OneToOne(mappedBy = "vitalStats")
#JoinColumn(name = "vitalstats_id") //same name as id #Column
private Person person;
private String stats;
}
Person Database Table
CREATE TABLE person (
person_id bigint(20) NOT NULL auto_increment,
name varchar(255) default NULL,
PRIMARY KEY (`person_id`)
)
VitalStats Database Table
CREATE TABLE vitalstats
(
vitalstats_id bigint(20) NOT NULL,
stats varchar(255) default NULL,
PRIMARY KEY (`vitalstats_id`)
)
In my case this made the trick:
Parent class:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/** auto generated id (primary key) */
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
/** user settings */
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private Setting setting;
}
Child class:
public class Setting implements Serializable {
private static final long serialVersionUID = 1L;
/** setting id = user id */
#Id
#Column(unique = true, nullable = false)
private Long id;
/** user with this associated settings */
#MapsId
#OneToOne
#JoinColumn(name = "id")
private User user;
}

Categories