Hibernate Many-To-Many mapping with 3 level subclass - java

I have the following UML diagram.
Since it is a manyToMany relation i need three tables to store the data:
CREATE TABLE Users(
idObj BIGINT NOT NULL PRIMARY KEY,
nome VARCHAR(100),
nomeUtente VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL
);
CREATE TABLE Works(
idObj BIGINT NOT NULL PRIMARY KEY,
nrIncarico int,
annoIncarico int,
...
);
CREATE TABLE UsersWorksRel(
idGiudice BIGINT NOT NULL,
idIncarico BIGINT NOT NULL,
FOREIGN KEY (idGiudice) REFERENCES Users(idObj),
FOREIGN KEY (idIncarico) REFERENCES Works(idObj)
);
In the AbstractPO i used the #MappedSuperclass annotation, the Users also is ok, but i'm not able to annotate the Judge class...
My idea was to use the Judge class to have the manyToMany relation with the Works, like this:
public class Judge extends UserPO{
private List<Works> incarichi;
public Judge () {
setTipo(TipologiaUtente.GIUDICE);
}
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "UsersWorksRel", joinColumns = {
#JoinColumn(name = "idGiudice", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "idIncarico", nullable = false, updatable = false) })
public List<Works> getIncarichi() {
return incarichi;
}
public void setIncarichi(List<Works> incarichi) {
this.incarichi = incarichi;
}
}

Related

How to annotate Map<Entity, Entity> with JPA?

The app's stack: Spring MVC, Spring DataJPA, Hibernate. There are three entities: student, tutor, theme.
Theme:
#Entity
#Table(name = "themes")
public class Theme {
// fields omitted
}
Student:
#Entity
#Table(name = "students")
public class Student {
private Map<Theme, Tutor> tutors;
// other fields omitted
}
Tutor:
#Entity
#Table(name = "tutors")
public class Tutor {
private Map<Theme, Student> students;
// other fields omitted
}
For save student-tutor-theme relationships i want use this table (PostgreSQL):
CREATE TABLE themes_students_tutors
(
theme_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
tutor_id INTEGER NOT NULL,
FOREIGN KEY (theme_id) REFERENCES themes (id) ON DELETE CASCADE,
FOREIGN KEY (student_id) REFERENCES students (id) ON DELETE CASCADE,
FOREIGN KEY (tutor_id) REFERENCES tutors (id) ON DELETE CASCADE
)
How i can to annotate tutors and students fields in entities, for their content correct persists in this table?
Like #kolossus mentioned: Use the #MapKeyJoinColumn¹ annotation, so that the classes (or the map fields) look like this (you can ignore the extention of AbstractPersistable):
Student:
public class Student extends AbstractPersistable<Long> {
#ManyToMany
#JoinTable(name = "themes_students_tutors", joinColumns = {
#JoinColumn(name = "student_id", referencedColumnName = "id") }, inverseJoinColumns = {
#JoinColumn(name = "tutor_id", referencedColumnName = "id") })
#MapKeyJoinColumn(name = "theme_id")
private Map<Theme, Tutor> tutors;
}
Tutor:
public class Tutor extends AbstractPersistable<Long> {
#ManyToMany
#JoinTable(name = "themes_students_tutors", joinColumns = {
#JoinColumn(name = "tutor_id", referencedColumnName = "id") }, inverseJoinColumns = {
#JoinColumn(name = "student_id", referencedColumnName = "id") })
#MapKeyJoinColumn(name = "theme_id")
private Map<Theme, Student> students;
}
Given that, something like this would be created:
Hibernate: create table students (id bigint not null, primary key (id))
Hibernate: create table themes (id bigint not null, primary key (id))
Hibernate: create table themes_students_tutors (tutor_id bigint not null, student_id bigint not null, theme_id bigint not null, primary key (student_id, theme_id))
Hibernate: create table tutors (id bigint not null, primary key (id))
Hibernate: alter table themes_students_tutors add constraint FKm5l4is34t5gs14p4skkv3aup7 foreign key (student_id) references students
Hibernate: alter table themes_students_tutors add constraint FK8o0mm5ywi0l4hdxi4lgw4dbnu foreign key (theme_id) references themes
Hibernate: alter table themes_students_tutors add constraint FKa0n6jvie0kmk0pmikcuvtepxh foreign key (tutor_id) references tutors
¹: See the Javadoc documentation of #MapKeyJoinColumn for some other samples

Hibernate ERROR: Cannot delete or update a parent row: a foreign key constraint fails

I have the following classes:
#Entity
#Table(name = "lecture")
public class Lecture {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String name;
#OneToOne
#JoinColumn(nullable = false, name = "professorId")
#JsonIgnore
private Professor professor;
}
And:
#Entity
#IdClass(ListenTo.class)
#Table(name = "listen_to")
public class ListenTo implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#JoinColumn(nullable = false, name = "lectureId")
private Lecture lecture;
#Id
#ManyToOne(cascade = CascadeType.REMOVE)
#JoinColumn(nullable = false, name = "studentId")
private Student student;
}
And I want to delete a lecture through this function:
public void delete(Lecture lecture) {
currentSession().delete(lecture);
}
I created the table like this:
create table lecture (
id bigint primary key not null auto_increment,
name varchar(500) not null,
professorId bigint not null,
foreign key (professorId) references professorId (id)
);
create table listen_to (
lectureId BIGINT not null references lecture(id),
studentId BIGINT not null references student(id),
primary key(lectureId,studentId)
);
However, I keep getting this error:
Causing: java.sql.SQLIntegrityConstraintViolationException: (conn=10) Cannot delete or update a parent row: a foreign key constraint fails (`myDBS`.`listen_to`, CONSTRAINT `listen_to_ibfk_1` FOREIGN KEY (`lectureId`) REFERENCES `lecture` (`id`))
I tried multiple things, including using this function to delete:
public boolean deleteById(Class<?> type, Serializable id) {
Object persistentInstance = currentSession().load(type, id);
if (persistentInstance != null) {
currentSession().delete(persistentInstance);
return true;
}
return false;
}
but it still doesn't work..
You first have to delete all ListenTo entries that refer to this lecture before you can delete the lecture. Use delete from ListenTo l where l.lecture.id = :id and bind the lecture id, before you delete the lecture itself.

Saving Record in Relation Spring Boot

I am creating a project in spring boot which creates record by api call.
so I have two table ( name and phone ) below is my domain:
#Entity
#Table(name = "name")
public class Name {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "active", nullable = false)
private boolean active = true;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(mappedBy = "name")
#JsonIgnoreProperties("name")
private List<Phone> phones;
}
And
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "number", nullable = false)
private String number;
#Column(name = "active", nullable = false)
private boolean active = true;
#ManyToOne(targetEntity= Name.class)
#JoinColumn(name="name_id"))
private Name name;
}
The Code to create the name is as follows:
public Name createNewName(Name createName) {
Name newName = new Name();
newName = NameRepo.save(createName);
return newName;
}
And Repository is:
#Repository
public interface NameRepository extends PagingAndSortingRepository<Name, Long> {}
So My problem is when i send a request using api to call this, Name does not store Phone values and also does not link if exist.
My request is as follows:
{
"name": "testrretf",
"active": true,
"phones":[{
"id":1
}]
}
This request should link the new Name to existing Phone id=1
And
{
"name": "testrretf",
"active": true,
"phones":[{
"number":"Test Phone",
"active":true,
}]
}
This request should create a Phone and link to the created Name.
Please help me..
Thanks,
UPDATE# Below is the Create Table Query:
CREATE TABLE `name` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`active` bit(1) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `IDX_NAME` (`name `),
KEY `IDX_NAME_ACTIVE` (`active`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
CREATE TABLE `phone` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`active` bit(1) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`name_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_PHONE_ACTIVE` (`active`),
KEY `fk_name_phone` (`name_id`),
CONSTRAINT `fk_name_phone` FOREIGN KEY (`name_id`) REFERENCES `name` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

Many to many relation in spring framework

Currently I'm working on a Spring Framwork application to try out how it works.
I'm having trouble with a many to many relationship and inserting data into it.
As example I'm making something to store orders containing one or multiple items through a link table.
I would like to store one order at a time, including all products related to it (products are already existing). The problem is that when I try to store data, the orderId isn't stored in the product_order table and gives me the following error:
""Column 'ORDER_ID' cannot be null"".
Has anyone a clue about what i'm doing wrong? Or if it's just not possible this way.
SQL structure & Java code is at the end.
The way I try to store the data is:
curl -i -X POST -H "Content-Type:application/json" -d '{
"code" : "Test",
"statusId" : 2,
"userId" : 1,
"productOrders" : [ {
"price" : 1.0,
"amount" : 2,
"product" : {
"productId" : 1
}
}, {
"price" : 2.0,
"amount" : 3,
"product" : {
"productId" : 2
}
} ]
}' http://localhost:8080/order
This is the SQL structure i'm using:
CREATE TABLE `user` (
`USER_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`USER_NAME` VARCHAR(20) NOT NULL,
`USER_BALANCE` DOUBLE UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`USER_ID`)
);
CREATE TABLE `product` (
`PRODUCT_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`PRODUCT_NAME` VARCHAR(20) NOT NULL,
`PRODUCT_PRICE` DOUBLE UNSIGNED NOT NULL,
`PRODUCT_DESCRIPTION` VARCHAR(45),
PRIMARY KEY (`PRODUCT_ID`)
);
CREATE TABLE `order` (
`ORDER_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`ORDER_USER_ID` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`ORDER_ID`),
CONSTRAINT `FK_ORDER_USER_ID` FOREIGN KEY (`ORDER_USER_ID`)
REFERENCES `user` (`USER_ID`)
);
CREATE TABLE `product_order` (
`ORDER_ID` INT(10) UNSIGNED NOT NULL,
`PRODUCT_ID` INT(10) UNSIGNED NOT NULL,
`AMOUNT` INT(10) UNSIGNED NOT NULL,
`PRICE` DOUBLE NOT NULL,
PRIMARY KEY (`ORDER_ID`,`PRODUCT_ID`),
CONSTRAINT `FK_PRODUCT_ORDER_ORDER_ID` FOREIGN KEY (`ORDER_ID`)
REFERENCES `order` (`ORDER_ID`),
CONSTRAINT `FK_PRODUCT_ORDER_PRODUCT_ID` FOREIGN KEY (`PRODUCT_ID`)
REFERENCES `product` (`PRODUCT_ID`)
);
And this are my classes (removed the getters/setters, left the annotations)
#Entity
#Table(name = "product")
public class Product implements java.io.Serializable {
private Integer productId;
private String name;
private String description;
private Double price;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "PRODUCT_ID", unique = true, nullable = false)
#Column(name = "PRODUCT_NAME", nullable = false)
#Column(name = "PRODUCT_PRICE", nullable = false)
#Column(name = "PRODUCT_DESCRIPTION", nullable = false)
}
#Entity
#Table(name = "`order`")
public class Order implements java.io.Serializable {
private Integer orderId;
private Integer userId;
private Set<ProductOrder> productOrders = new HashSet<>(0);
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ORDER_ID", unique = true, nullable = false)
#Column(name = "ORDER_USER_ID")
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.order", cascade = CascadeType.ALL)
}
#Entity
#Table(name = "product_order")
#AssociationOverrides({
#AssociationOverride(name = "pk.order",
joinColumns = #JoinColumn(name = "ORDER_ID")),
#AssociationOverride(name = "pk.product",
joinColumns = #JoinColumn(name = "PRODUCT_ID")) })
public class ProductOrder implements java.io.Serializable {
private ProductOrderId pk = new ProductOrderId();
private Double price;
private Integer amount;
#EmbeddedId
//Getter/setter for primaryKey (pk)
#Transient
//Link to product
#Column(name = "PRICE")
#Column(name = "AMOUNT")
}
#Embeddable
public class ProductOrderId implements java.io.Serializable {
private Product product;
private Order order;
#ManyToOne
//link to Product
#ManyToOne
//Link to Order
}

How to manage collection of enumeration in an entity with a composite id?

I have an entity with a composite id and a collection of enumeration, but i am unable to set JPA annotations to configure that correctly.
Here the SQL for the tables :
create table `ReadWriteRight` (
`idProfil` bigint not null,
`idState` bigint not null,
`read` boolean,
`write` boolean,
primary key (`idProfil`, `idState`),
constraint `FK_ReadWriteRight_Profil` foreign key(`idProfil`) REFERENCES `Profil`(`idProfil`),
constraint `FK_ReadWriteRight_State` foreign key(`idState`) REFERENCES `State`(`idState`)
) engine=InnoDB default charset=utf8;
create table `AssoReadRight_Form` (
`idProfil` bigint not null,
`idState` bigint not null,
`typeForm` varchar(50) not null,
primary key (`idProfil`, `idState`, `typeForm`),
constraint `FK_AssoReadRight_Form_Profil` foreign key(`idProfil`) REFERENCES `Profil`(`idProfil`),
constraint `FK_AssoReadRight_Form_State` foreign key(`idState`) REFERENCES `State`(`idState`)
) engine=InnoDB default charset=utf8;
create table `AssoWriteRight_Form` (
`idProfil` bigint not null,
`idState` bigint` not null,
`typeForm` varchar(50) not null,
primary key (`idProfil`, `idState`, `typeForm`),
constraint `FK_AssoWriteRight_Form_Profil` foreign key(`idProfil`) REFERENCES `Profil`(`idProfil`),
constraint `FK_AssoWriteRight_Form_State` foreign key(`idState`) REFERENCES `State`(`idState`)
) engine=InnoDB default charset=utf8;
And here the Java with JPA annotations :
#Entity
#Table(name = "ReadWriteRight")
public class ReadWriteRight implements Serializable {
private static final long serialVersionUID = 1L;
public enum TypeForm {
Form1, Form2;
}
#Embeddable
public static final class ReadWriteRightId implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "idProfil", nullable = false)
private Profil profil;
#ManyToOne
#JoinColumn(name = "idState", nullable = false)
private State state;
[...]
}
#EmbeddedId
private ReadWriteRightId id;
#Column(name = "read")
private boolean read;
#Column(name = "write")
private boolean write;
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = TypeForm.class, fetch = FetchType.LAZY)
#CollectionTable(name = "AssoReadRight_Form", joinColumns = {#JoinColumn(name = "idProfil", nullable = false), #JoinColumn(name = "idState", nullable = false)})
#Column(name = "typeForm")
private Set<TypeForm> formulairesLecture;
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = TypeForm.class, fetch = FetchType.LAZY)
#CollectionTable(name = "AssoWriteRight_Form", joinColumns = {#JoinColumn(name = "idProfil", nullable = false), #JoinColumn(name = "idState", nullable = false)})
#Column(name = "typeForm")
private Set<TypeForm> formulairesEcriture;
[...]
}
I have solved my problem with some modifications :
First, I had to change the foreign key for my tables AssoWriteRight_Form and AssoReadRight_Form so that they link to the composite id from the table ReadWriteRight:
create table `AssoReadRight_Form` (
`idProfil` bigint not null,
`idState` bigint not null,
`typeForm` varchar(50) not null,
primary key (`idProfil`, `idState`, `typeForm`),
constraint `FK_AssoReadRight_Form` foreign key(`idProfil`, `idState`) REFERENCES `ReadWriteRight`(`idProfil`, `idState`)
) engine=InnoDB default charset=utf8;
create table `AssoWriteRight_Form` (
`idProfil` bigint not null,
`idState` bigint` not null,
`typeForm` varchar(50) not null,
primary key (`idProfil`, `idState`, `typeForm`),
constraint `FK_AssoWriteRight_Form` foreign key(`idProfil`, `idState`) REFERENCES `ReadWriteRight`(`idProfil`, `idState`)
) engine=InnoDB default charset=utf8;
I also had to change my JPA configuration so the #JoinColumn link correctly between the association table and the base table:
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = TypeForm.class, fetch = FetchType.LAZY)
#CollectionTable(name = "AssoReadRight_Form", joinColumns = {#JoinColumn(name = "idProfil", nullable = false, referencedColumnName = "idProfil"),
#JoinColumn(name = "idState", nullable = false, referencedColumnName = "idState")})
#Column(name = "typeForm")
private Set<TypeForm> formulairesLecture;
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = TypeForm.class, fetch = FetchType.LAZY)
#CollectionTable(name = "AssoWriteRight_Form", joinColumns = {#JoinColumn(name = "idProfil", nullable = false, referencedColumnName = "idProfil"),
#JoinColumn(name = "idState", nullable = false, referencedColumnName = "idState")})
#Column(name = "typeForm")
private Set<TypeForm> formulairesEcriture;

Categories