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.
Related
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;
I need help to create the correct pojo's from this database...
https://www.dropbox.com/s/j2lfu44zpqfcxb4/dbr.PNG
I have tried creating this classes...
#Entity
#Table(name="Municipio", catalog="elecciones2014", schema="")
public class Municipio implements Serializable{
#EmbeddedId
private MunicipioPk idMunicipio;
#Basic(optional=false)
#Column(name="nomb_municipio")
private String nomb_municipio;
}
With this Embedded class
#Embeddable
class MunicipioPk implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name="id_depto")
String departamento;
#Column(name="id_municipio")
String idMunicipio;
}
The problem is when i want to reference to 'Municipio' from 'JRV' y don't know how to access to field 'id_municipio'. I had this code but it doesn't work
#Entity
#Table(name = "JRV", catalog = "elecciones2014", schema = "")
public class Jrv {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_jrv")
private int id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id_municipio",referencedColumnName="idMunicipio")
private Municipio municipio;
#ManyToOne
#JoinColumn(name="DUI",referencedColumnName="dui")
private PadronElectoral dui;
}
can someone help me?
how I have to do it?
Thanks in advice!!
Here you are defining single join column, but the Municipio entity's PK has two columns. Also the referencedColumnName should be the name of the column not the entity's property.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id_municipio",referencedColumnName="idMunicipio")
private Municipio municipio;
So you could do something like this:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="id_municipio", referencedColumnName="id_municipio"),
#JoinColumn(name="id_depto", referencedColumnName="id_depto")
})
private Municipio municipio;
Which translates to this SQL (I got this by generating SQL schema from your entities after the modification mentioned above):
create table elecciones2014.JRV (
id_jrv serial not null,
id_depto varchar(255),
id_municipio varchar(255),
primary key (id_jrv)
);
alter table elecciones2014.JRV
add constraint FK_7scd8alu3nf4tsyh3hq2ryrja
foreign key (id_depto, id_municipio)
references elecciones2014.Municipio;
I'm trying to retrieve a list of entities from a table with two primary keys which are ids to a foreign key each.
MySQL tables:
Paintings table:
id - int, PK, Auto increment
name - varchar(45)
Pictures table:
id - int, PK, Auto increment
name - varchar(45)
location - varchar(45)
painting_to_picture_link table:
painting_id - int, FK to id in painting
picture_id - int, FK to id in painting
I've set primary key (painting_id, picture_id)
and set them to their foreign keys also as written above.
In Java:
Painting.java
#Entity
#Table(name = "paintings")
public class Painting {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
...
}
Picture.java
#Entity
#Table(name = "pictures")
public class Pictures {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
...
}
PaintingPictureLink.class
public class PaintingPictureLink implements Serializable {
#<SOME ANNOTATION HERE>
private Painting painting;
#<SOME ANNOTATION HERE>
private Picture picture;
...
}
I've seen many examples, but didn't work for me.
I've tried putting #Id annotations, #EmbeddedId, etc... non worked.
The errors I get are that table isn't mapped, could not determine type for the models, missing #Id annotation... :|
Would appreciate help with querying this table and getting a list of PaintingPictureLink.
Some of the examples I've followed:
https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Server/1.0/html/Hibernate_Annotations_Reference_Guide/ch03s02s06.html
Using an Entity (and their Primary Key) as another Entity's Id
Thanks,
Guy
Derp
Found the solution.
I created a primary key class:
#Embeddable
public class PicturePaintingPK implements Serializable {
#ManyToOne
private Painting painting;
#ManyToOne
private Picture picture;
public PicturePaintingPK() {}
// getters and setters //
}
In the PicturePaintingLink class:
#Entity
#Table(name = "painting_to_picture_link")
public class PaintingPictureLink implements Serializable {
#Id
private PicturePaintingPK primaryKey = new PicturePaintingPK()
...
//constructor//
...
public TTPK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(TTPK primaryKey) {
this.primaryKey = primaryKey;
}
// ... all the other getters and setters needed .... //
}
The source for this solution was from:
Example from Hibernate forum
Guy
Can someone please explain to me #MapsId in hibernate? I'm having a hard time understanding it.
It would be great if one could explain it with an example and in what kind of use cases is it most applicable?
Here is a nice explanation from Object DB.
Designates a ManyToOne or OneToOne relationship attribute that provides the mapping for an EmbeddedId primary key, an attribute within an EmbeddedId primary key, or a simple primary key of the parent entity. The value element specifies the attribute within a composite key to which the relationship attribute corresponds. If the entity's primary key is of the same Java type as the primary key of the entity referenced by the relationship, the value attribute is not specified.
// parent entity has simple primary key
#Entity
public class Employee {
#Id long empId;
String name;
...
}
// dependent entity uses EmbeddedId for composite key
#Embeddable
public class DependentId {
String name;
long empid; // corresponds to primary key type of Employee
}
#Entity
public class Dependent {
#EmbeddedId DependentId id;
...
#MapsId("empid") // maps the empid attribute of embedded id
#ManyToOne Employee emp;
}
Read the API Docs here.
I found this note also useful: #MapsId in hibernate annotation maps a column with another table's column.
It can be used also to share the same primary key between 2 tables.
Example:
#Entity
#Table(name = "TRANSACTION_CANCEL")
public class CancelledTransaction {
#Id
private Long id; // the value in this pk will be the same as the
// transaction line from transaction table to which
// this cancelled transaction is related
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_TRANSACTION", nullable = false)
#MapsId
private Transaction transaction;
....
}
#Entity
#Table(name = "TRANSACTION")
#SequenceGenerator(name = "SQ_TRAN_ID", sequenceName = "SQ_TRAN_ID")
public class Transaction {
#Id
#GeneratedValue(generator = "SQ_TRAN_ID", strategy = GenerationType.SEQUENCE)
#Column(name = "ID_TRANSACTION", nullable = false)
private Long id;
...
}
IMHO, the best way to think about #MapsId is when you need to map a composite key in a n:m entity.
For instance, a customer can have one or more consultant and a consultant can have one or more customer:
And your entites would be something like this (pseudo Java code):
#Entity
public class Customer {
#Id
private Integer id;
private String name;
}
#Entity
public class Consultant {
#Id
private Integer id;
private String name;
#OneToMany
private List<CustomerByConsultant> customerByConsultants = new ArrayList<>();
public void add(CustomerByConsultant cbc) {
cbc.setConsultant(this);
this.customerByConsultant.add(cbc);
}
}
#Embeddable
public class CustomerByConsultantPk implements Serializable {
private Integer customerId;
private Integer consultantId;
}
#Entity
public class CustomerByConsultant{
#EmbeddedId
private CustomerByConsultantPk id = new CustomerByConsultantPk();
#MapsId("customerId")
#JoinColumn(insertable = false, updatable = false)
private Customer customer;
#MapsId("consultantId")
#JoinColumn(insertable = false, updatable = false)
private Consultant consultant;
}
Mapping this way, JPA automagically inserts Customer and Consultant ids in the EmbeddableId whenever you save a consultant. So you don't need to manually create the CustomerByConsultantPk.
As he explained Vladimir in his tutorial, The best way to map a #OneToOne relationship is to use #MapsId. This way, you don’t even need a bidirectional association since you can always fetch the Child entity by using the Parent entity identifier.
MapsId lets you use the same primary key between two different entities/tables. Note: when you use MapsId, the CASCADE.ALL flag becomes useless, and you will need to make sure that your entities are saved manually.
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;
}