Hibernate unidirectional one-to-one mapping - sql

I have the following table structure.
(The unique constraint is to avoid multiple 'details' to the same employee - I can't change the db structure).
https://i.ibb.co/r3pYQFj/fk.png
create table employee (
employee_id number(19),
salary number(10),
constraint pk_employee primary key (employee_id)
);
create table employee_details (
employee_details_id number(19),
employee_id number(19) not null,
address varchar2(256),
gender char(1),
constraint fk_employee foreign key (employee_id) references employee (employee_id),
constraint fk_employee_unq unique (employee_id)
);
Model class:
#Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "empgen")
#SequenceGenerator(name = "empgen", sequenceName = "SEQ_EMP", allocationSize = 1)
#Column(name = "EMPLOYEE_ID")
private Long id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="EMPLOYEE_ID")
private EmployeeDetail empDetail;
...
#Entity
#Table(name = "EMPLOYEE_DETAIL")
public class EmployeeDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "empdetgen")
#SequenceGenerator(name = "empdetgen", sequenceName = "SEQ_EMP_DET", allocationSize = 1)
#Column(name = "EMPLOYEE_DETAILS_ID")
private Long id;
#Column(name = "EMPLOYEE_ID")
private Long employeeId;
...
I have a rest controller that receive a JSON like this:
{
"employee": {
"salary": 80000,
"empDetail": {
"adress": "ST EXAMPLE",
"gender": "M"
}
}
}
I'm trying to persist all the entities via hibernate (5.4.28) saving Employee as the first entity, then with its primary key, EmployeeDetails using it's parent primary key but I get this:
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DCC"."EMPLOYEE_DETAILS"."EMPLOYEE_ID")
Why it's trying to save the child before the parent?
How the the class should be mapped?

I guess it's because Hibernate thinks that by putting #JoinColumn on Employee.empDetail, EMPLOYEE_ID belongs to the employee_details table.
Try putting creating a property EmployeeDetail.employee and put #JoinColumn on that one. If you need a reference from Employee, use #OneToOne(mappedBy=...).
How to use #JoinColumn
Activate logging to see which statements are executed

Related

Micronaut Data JDBC: How to map a one-to-many relation using enums?

I would like to map a unidirectional one-to-many relation, using Micronaut Data annotations. Considering the object and database models described below, how to map that using Micronaut annotations?
Object model:
import io.micronaut.serde.annotation.Serdeable
#Serdeable
enum class Role {
Admin, Manager
}
import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.serde.annotation.Serdeable
#Serdeable
#MappedEntity
data class User(
#field:Id
#field:GeneratedValue
var id: Long? = null,
val username: String,
// How to map?
var roles: Set<Role>?
)
Database model with many-to-many (DDLs for PostgreSQL):
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE
);
CREATE TABLE "role"
(
id INTEGER PRIMARY KEY,
name VARCHAR(40) NOT NULL
);
CREATE TABLE "user_role"
(
user_id INTEGER REFERENCES "user" (id),
role_id INTEGER REFERENCES "role" (id)
);
INSERT INTO "role" (id, name) VALUES (1, 'Admin'), (2, 'Manager');
Or another option, a simpler database model using one-to-many (PostgeSQL):
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE
);
CREATE TYPE role_name AS ENUM ('Admin', 'Manager');
CREATE TABLE "user_role"
(
user_id INTEGER NOT NULL REFERENCES "user" (id),
role_id INTEGER NOT NULL REFERENCES "role" (id),
CONSTRAINT user_role_uk UNIQUE (user_id, role_id)
);
You may suggest modifications to any database model to allow the one-to-many relation in the object model.
Here are two out of the multiple ways that it could be done. I combined them into one example out of laziness.
#Serdeable
enum class Role {
Admin, Manager;
}
#Serdeable
#MappedEntity
data class UserRole(
#field:Id
#GeneratedValue
var id: Long? = null,
#Nullable
#Relation(value = Relation.Kind.MANY_TO_ONE)
val user: User?,
val role: Role
)
#Serdeable
#MappedEntity
class User(
#field:Id
#field:GeneratedValue
var id: Long? = null,
val username: String,
#MappedProperty(definition = "VARCHAR(30)[]", type = DataType.STRING_ARRAY)
var roles: Set<Role>?,
#Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "user", cascade = [Cascade.ALL])
var userRoles: Set<UserRole>?
)
#JdbcRepository(dialect = Dialect.POSTGRES)
#Join(value = "userRoles", type = Join.Type.LEFT_FETCH)
interface UserRepository : CrudRepository<User, Long> {
}
CREATE TYPE role_name AS ENUM ('Admin', 'Manager');
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE,
roles VARCHAR(15)[]
);
CREATE TABLE "user_role"
(
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id INTEGER REFERENCES "user" (id),
role role_name NOT NULL,
CONSTRAINT user_role_uk UNIQUE (user_id, role)
);

Referential integrity constraint violation (CAST( AS BIGINT))

I am creating a database of items and have been trying to assign multiple database entities to a single item however I am struggling to get past an error I keep having. I am unsure what I am doing wrong can someone help?
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation: "FKKPIB0GWXM6UFS3SJJ2NCI64AR: PUBLIC.FEATURES FOREIGN KEY(ITEM_ID) REFERENCES PUBLIC.ITEM(ITEM_ID) (CAST(1020 AS BIGINT))"; SQL statement: INSERT INTO FEATURES(FEATURE_ID, ITEM_ID) VALUES(101, 1020), (102,1021), (103,1021), (104,1021)
Item.java
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name = "item_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Enumerated(EnumType.STRING)
private ItemType itemtype;
private int weight;
private int recovers;
private int priority;
private String desc;
#OneToMany(mappedBy = "item")
private List<Feature> features;
}
Feature.java
#Entity
#Table(name = "features")
public class Feature {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long feature_id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "item_id", insertable = false, updatable = false)
#JsonIgnore
private Item item;
}
data.sql
INSERT INTO SPELLS(SPELL_ID, ITEM_ID)
VALUES(101, 1020),
(102, 1021),
(103, 1021),
(104, 1021)
;
INSERT INTO ITEM(ITEM_ID, NAME, ITEMTYPE, WEIGHT, RECOVERS, PRIORITY, DESC)
VALUES (1010,'Hunting Knife','DAGGER',1,5,3,''),
(1011,'Relic Sword','SWORD',3,10,3,''),
(1012,'Relic Spear','SPEAR',3,8,3, ''),
(1013,'Relic Axe','AXE',4,12,3, ''),
(1014,'Old Club','MACE',4,10,3,''),
(1015,'Crooked Stick','STAFF',2,3,3,''),
(1016,'Training Bow','BOW',2,20,4,''),
(1017,'Training Crossbow','CROSSBOW',2,20,4,''),
(1018,'Grass Sling','SLING',2,20,4,''),
(1019,'Wooden Shield','SHIELD',4,20,4,''),
(1020,'Poison wand','WAND', 1,0,4,''),
(1021,'Mushroom staff','STAFF',2,3,3,'')
;

Hibernate Postgres Delete self referencing unidirectional entity

I will try to explain my issue:
I am using Spring Boot/Hibernate and the parent entity is
getters/setters and other fields are omitted
public class BaseEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Valid
#JsonManagedReference
#OneToMany(mappedBy = "contract", fetch = FetchType.EAGER, orphanRemoval = true, cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REMOVE})
#OnDelete(action = OnDeleteAction.CASCADE)
private List<ChildEntity> myList = new ArrayList<>();
public void addChildEntityChildEntity list) {
myList.add(list);
list.setChildEntity(this);
}
public void removeChildEntity(ChildEntity entity) {
myList.entity(entity);
list.setChildEntity(null);
}
public List<ChildEntity> getChildEntitys(){
return this.myList;
}
public class ChildEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Valid
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "baseentity_id", referencedColumnName = "id",nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private BaseEntity baseEntity;
//here is the self reference in the child
#Nullable
#Valid
#ManyToOne(fetch = FetchType.EAGER, cascade={CascadeType.REMOVE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "reference_child_id", referencedColumnName = "id")
#OnDelete(action = OnDeleteAction.CASCADE)
private ChildEntity referenceChildEntity;
public void addChildEntity(ChildEntity childEntity) {
this.referenceChildEntity = childEntity;
referenceChildEntity.setChildEntity(this);
}
public void removeChildEntity() {
referenceChildEntity.setChildEntity(null);
referenceChildEntity = null;
}
public void addToParentEntity(BaseEntity baseEntity) {
baseEntity.addChildEntity(this);
this.setChildEntity(baseEntity);
}
public void removeBaseEntity() {
this.baseEntity.removeChildEntity(this);
this.baseEntity = null;
}
These are the entities that are important:
However when I try to delete the the BaseEntity: AND there is a relationship with ChildEntity that has other ChildEntity relationships I get a constraint error
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "base_entity" violates foreign key constraint "reference_child_id" on table "base_entity"
Detail: Key (id)=() is still referenced from table "base_entity".
Where: SQL statement "delete from base_entity where id = id.id"
this is the constraint from the psql:
CONSTRAINT_id_fkey FOREIGN KEY (reference_child_id)
REFERENCES base_entity (id) MATCH SIMPLE
Finally, the delete happens in sql function that looks like:
create or replace function delete_base_entity_by_id(
base_id int
)
returns void as
$BODY$
declare
loop_base base_entity;
loop_child child_entity;
begin
for loop_base in (select * from base_entity where original_id = (select original_id from base_entity where id = base_id) order by start_date, id desc) loop
for loop_child in (select * from child_entity where base_id = loop_base.id order by id desc) loop
delete from child2 where child_id = loop_child.id;
delete from child3 where child_id = loop_child.id;
delete from child4 where child_id = loop_child.id;
delete from child5 where loan_child_id = loop_child.id;
delete from child6 where loan_child_id = loop_child.id;
delete from child7 where child_id = loop_child.id;
delete from child8 where child_id = loop_child.id;
delete from child9 where id = loop_child.id;
end loop;
delete from base_entity where id = loop_base.id;
end loop;
delete from child_entity where id = loop_base.loan_id;
end
$BODY$
language plpgsql;
I have tried to add on delete cascade on the tables but there is problem with the children that i try to delete with the sql function
alter table child_entity
drop constraint id_fkey,
add constraint id_fkey
foreign key (reference_child_id)
REFERENCES child_entity (id)
on delete cascade;
Any ideas and suggestions on this really apriciated:
To fix this, you can use a deferred foreign key constraint.
alter table child_entity
drop constraint id_fkey,
add constraint id_fkey
foreign key (reference_child_id)
REFERENCES child_entity (id)
deferrable initially deferred;
This will cause the database to enforce constraints at transaction commit rather than after the statement.

How to fix 'ERROR SqlExceptionHelper Column 'cardinalidadid' not found' error in jpa

I want to map an entity, which has a relationship with another table, but when mapping that relationship, it doesn't find the "Cardinality" column.
This is the code:
Entity elemento:
#Entity
#Table(name = "elemento")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="clave", discriminatorType = DiscriminatorType.STRING, length=10)
public class Elemento implements Serializable, GenericInterface {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "clave", insertable = false)
private String clave;
#Column(name = "numero")
private String numero;
#Column(name = "nombre")
private String nombre;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Proyectoid")
private Proyecto proyecto;
#Column(name = "descripcion")
private String descripcion;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "EstadoElementoid", referencedColumnName="id")
private EstadoElemento estadoElemento;
Entity actor:
#Entity
#Table(name = "actor")
#Inheritance(strategy=InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "Elementoid", referencedColumnName = "id")
#DiscriminatorValue("ACT")
public class Actor extends Elemento implements Serializable, GenericInterface, ElementoInterface {
private static final long serialVersionUID = 1L;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Cardinalidadid", referencedColumnName = "id")
private Cardinalidad cardinalidad;
This is the description of the tables:
TABLE ACTOR:
| actor | CREATE TABLE `actor` (
`otraCardinalidad` varchar(45) DEFAULT NULL,
`Elementoid` int(11) NOT NULL,
`Cardinalidadid` int(11) NOT NULL,
PRIMARY KEY (`Elementoid`),
KEY `FKActor872913` (`Cardinalidadid`),
KEY `FKActor148309` (`Elementoid`),
CONSTRAINT `FKActor148309` FOREIGN KEY (`Elementoid`) REFERENCES
`elemento` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FKActor872913` FOREIGN KEY (`Cardinalidadid`) REFERENCES
`cardinalidad` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
TABLE CARDINALIDAD
| cardinalidad | CREATE TABLE `cardinalidad` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` varchar(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniqueCardinalidad` (`nombre`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 |
This error appears
02:57:43.663 [qtp1469821799-35] DEBUG
com.mchange.v2.c3p0.impl.NewPooledConnection -
com.mchange.v2.c3p0.impl.NewPooledConnection#48c2c8ce handling a
throwable. java.sql.SQLException: Column 'cardinalidadid' not found.

JPA query for 3 tables

I want to create JPA query for configuring multiple Terminals to one Contract:
CREATE TABLE `contracts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
) ENGINE=InnoDB;
CREATE TABLE `contracts_terminals` (
`terminal_id` int(11) DEFAULT NULL,
`contract_id` int(11) DEFAULT NULL,
) ENGINE=InnoDB;
CREATE TABLE `terminals` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB;
How I can create JPA query which uses table contracts_terminals to assign multiple terminals to one contract?
I use latest MariaDB.
Entities:
Contracts:
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
private static final long serialVersionUID = 3873648042962238717L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(length = 255)
private String name;
......
}
Terminals:
#Entity
#Table(name = "terminals")
public class Terminals implements Serializable {
private static final long serialVersionUID = 5288308199642977991L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, updatable = false, nullable = false, length = 3)
private int id;
#Column(length = 255)
private String name;
....
}
ContractTerminals:
#Entity
#Table(name = "contract_terminals")
public class ContractTerminals implements Serializable {
private static final long serialVersionUID = 1191148141983861602L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(length = 4)
private Integer terminal_id;
#Column(length = 4)
private Integer contract_id;
....
}