I'm new in Hibernate, and I've started to do a little project.
I wanted to test add and remove for all kind of relashionship.
I do not understant how remove is managed ... (for ManyToMany it seems to works well)
(I use an H2 database)
I have a ManyToOne relashionship :
WORK have 0 or many STEPS so : (java and sql at the end)
I have created some entities, and then I just want de remove a Work from database ...
Details
I have create a Junit to Test, and I display my database :
- save new work : "workOne"
- save new work : "workTwo"
- save new file : "fileone"
- save new step : "stepOne"
- stepOne.setWork(workOne)
- save new step : "stepTwo"
- stepOne.setWork(workOne)
- stepOne.setFile(fileOne)
To save :
entityManager.getTransaction().begin();
entityManager.save(object);
entityManager.getTransaction().commit();
Display (by sql request on database) >>> OK all my entity are displayed with the good associations :
workOne - ID = 10
workTwo - ID = 11
fileOne - ID = 20
stepOne - ID = 40 / ID_WORK = 10 / ID_DOS = null
stepTwo - ID = 41 / ID_WORK = 11 / ID_DOS = 20
Now I want to test remove a work (in the same test)
// Remove a work
entityManager.getTransaction().begin();
entityManager.remove(workOne);
entityManager.getTransaction().commit();
// Display BDD
>> The workOne has not been deleted from database ... (no hibernate delete on logs)
I Expected that :
workOne (#10) would be deleted
stepOne too ... (because we cannot have a step with no work)
Thanks,
java :
#Entity
#Table(name = "T_WORK")
public class Work {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id = 0L;
#Column(name = "LABEL")
String label;
#Entity
#Table(name = "T_STEP")
public class Step{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id = 0L;
#ManyToOne(cascade = {CascadeType.ALL })
#JoinColumn(name = "ID_WORK")
Work work;
#OneToOne(cascade = {CascadeType.ALL })
#JoinColumn(name = "ID_FILE")
File file;
#Column(name = "LABEL")
private String label;
#Column(name = "STATUS")
private String status;
h2.sql :
create table T_WORK (
ID number(10) not null,
LABEL varchar2(64) not null,
constraint PK_WORK primary key (ID)
);
create table T_STEP (
ID number(10) not null,
ID_WORK number(10) not null,
ID_FILE number(10),
LABEL varchar2(64) not null,
STATUS varchar2(64),
constraint PK_STEP primary key (ID),
CONSTRAINT FK_STEPS_WORK FOREIGN KEY (ID_TRA) REFERENCES T_WORK(ID),
CONSTRAINT FK_STEP_FILE FOREIGN KEY (ID_DOS) REFERENCES T_FILE(ID)
);
Related
Info: Oracle DB 19. Hibernate is v5.5.4 with org.hibernate.dialect.Oracle12cDialect. JDBC driver is v12.2.0.1.
Question:
I want to save an entity with JPA/Hibernate (DB is Oracle 11g), that has an autogenerated ID column (here: PROT_ID).
CREATE TABLE SOME_PROTOCOL(
PROT_ID NUMBER(18) GENERATED ALWAYS AS IDENTITY (START WITH 123 MAXVALUE 99999) NOT NULL,
MORE_COLS VARCHAR2(500 CHAR) NOT NULL);
To add a new record, I have to skip the ID column, like that:
insert into SOME_PROTOCOL (MORE_COLS) values ('Some Value');
This is my Entity class:
#Entity
#Table(name = "SOME_PROTOCOL")
public class SomeProtocol {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROT_ID", insertable = false)
private Long id;
// Getter, Setter, other columns
}
Saving the entity with
SomeProtocol s = new SomeProtocol();
s.setMoreCols("whatever");
hibernateSession.save(s);
leads to this error:
ERROR: Invalid argument(s) in call
Hibernate: insert into APPL_PROTOCOL (PROT_ID, MORE_COLS) values (default, ?)
Ok, JPA doesn't skip the ID column, but sets default as a value.
I tried some more with #Column(insertable=false) or GenerationType.AUTO, but to no avail.
How can I save an entity class with an autogenerated ID column?
Solution:
We changed the ID generation for that table, we now use an external sequence (previously it was auto-generated). Hibernate can save the entity now via hibernateSession.save.
#SequenceGenerator(name = "SEQ_APPL_PROT_GENERATOR", sequenceName = "SEQ_APPL_PROTOCOL_ID", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_APPL_PROT_GENERATOR")
#Column(name = "PROT_ID")
private Long id;
I think the insertable = false in your #Column annotation might be the problem. Please try without that and let us know how that works.
You can read more about this attribute on Please explain about insertable=false and updatable=false in reference to the JPA #Column annotation.
When adding an entity that stores other entities in itself, they are added without checking their existence in the database. That is, if I add a leakSource that includes CompEmails that already exist in the table, they are duplicated, and the existing ones are non-linked to the leakSource. Tell me how to fix it?
#Entity
#Table(name = "comp_emails")
class CompEmails (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val address: String? = null,
#ManyToMany(cascade = [CascadeType.PERSIST, CascadeType.MERGE], mappedBy = "compEmails")
val leakSources: Set<LeakSource>? = null
)
#Entity
#Table(name = "leak_source")
class LeakSource (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val source: String? = null,
#ManyToMany(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinTable(
name = "comp_emails_sites_alert",
joinColumns = [JoinColumn(name = "source_id")],
inverseJoinColumns = [JoinColumn(name = "email_id")]
)
val compEmails: Set<CompEmails>? = null
)
#PostMapping("/add-leak-source")
fun addLeakSource(#RequestBody leakSource: LeakSource) : LeakSource {
leakSourceRepo.save(leakSource)
return leakSourceRepo.getById(leakSource.id!!)
}
LeakSource is the owner side of the relationship. Whenever you try adding a CompEmail to a LeakSource, you should do the following steps given order.
1- If CompEmail exists in db: fetch it
if not: create it, save to db and fetch the saved entity
compEmail = getById(ceId);
// or
... create compEmail
compEmail = save(compEmail);
2- If LeakSource exists in db: fetch it
if not: create it, save to db and fetch the saved entity
leakSource = getById(lsId);
// or
... create leakSource
leakSource = save(leakSource);
3- Add CompEmail to LeakSource's CompEmail list. It should look like this:
...
leakSource.getCompEmails().add(compEmail);
4- Save the LeakSource entity (which we just added the CompEmail)
Additional notes:
cascade = [CascadeType.PERSIST, CascadeType.MERGE]
This property should be removed from CompEmails
I'm Developing small MicroService Using Spring boot.
My database Is Postgres and I have a pojo in my service mapping the table.
Initially my Table is created with one Primary Key NOT NULL serial.
Now I want to add One more column with serial number starting from 6000 and map with my pojo.
#JsonProperty("id")
#Column(name = "id", nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "order_seq_gen")
#SequenceGenerator(name = "order_seq_gen", sequenceName ="order_id_seq")
private Integer id;
#JsonProperty("state")
private String state;
#JsonProperty("user_Id")
#Column(name = "user_Id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "user_seq_gen")
#SequenceGenerator(name = "user_seq_gen", sequenceName ="user_id_seq")
private Integer userId;
This is My Pojo.
The first Id Is creating but the userId Is not creating.
Bellow is my Table structure and sequences.
This is my create table statement
CREATE TABLE public.user
(
id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass),
created timestamp without time zone,
modified timestamp without time zone,
state character varying(255),
name text,
user_id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass), CONSTRAINT user_pkey
PRIMARY KEY (id)
)
Below is my sequence.
CREATE SEQUENCE public.user_id_seq INCREMENT 1 MINVALUE 6001
MAXVALUE 9223372036854775807 START 6000 CACHE 1;
ALTER TABLE public.user_id_seq OWNER TO postgres;
What I'm trying is in my table the column user_id should generate Auto from Base as 6000 and increment each time by 1.
Whenever I call API from my service it is creating new record. But the user_id is not showing anything.
The 3 tables are "analyticalgroups", "labinstructions", "observedproperties". Each table has an "id" primary key column.
I'd like to use a 4th table ("analyticalgroups_observedproperties_labinstructions") to store the OneToMany relationship. Ultimately I'd like the output to be structured something like this:
analyticalGroup: {
id: "...",
observedPropertyLabInstructions: [
{observedProperty, labInstruction},
{observedProperty, labInstruction},
{observedProperty, labInstruction},
...etc...
]
}
I've followed some examples online, but can't get this to work. The problem is when I try this I get the following error:
"message" : "Error occurred at repository: PSQLException: ERROR: column observedpr0_.observedpropertyentitylabinstructionentitymap_id does not exist\n Position: 6550",
"errorCode" : "gaia.domain.exceptions.RepositoryException",
Here's the structure for the join table.
CREATE TABLE analyticalgroups_observedproperties_labinstructions
(
analyticalgroupid character varying(36) NOT NULL,
labinstructionid character varying(36) NOT NULL,
observedpropertyid character varying(36) NOT NULL,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_groupid FOREIGN KEY (analyticalgroupid)
REFERENCES analyticalgroups (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_labinstr FOREIGN KEY (labinstructionid)
REFERENCES labinstructions (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_observed FOREIGN KEY (observedpropertyid)
REFERENCES observedproperties (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
#Entity
#Data
public class AnalyticalGroupEntity {
public static final String ENTITY_NAME = "analyticalGroups";
public static final String JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME =
ENTITY_NAME +
IDomainEntity.UNDERSCORE +
ObservedPropertyEntity.ENTITY_NAME +
IDomainEntity.UNDERSCORE +
LabInstructionEntity.ENTITY_NAME;
#Id
#Column(name = IDomainEntity.ID_KEY, nullable = false, columnDefinition = IDomainEntity.COLUMN_TYPE_UUID)
private String id;
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyLabInstructions;
}
Hopefully I've laid this all out as clearly as necessary.
Your help is much appreciated. Thanks for reading!
edit Actually... it turns out this doesn't work. It successfully gets the data I want, buuuuut it also deletes every row in the join table whenever I make a GET request *flip table*
So bizarre!
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = "analyticalgroupid", referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions"),
inverseJoinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyEntityLabInstructionEntityMap;
I have a table containing customer data in an oracle database. Here is a simplified definition:
CUSTOMER (CUSTOMER_ID NUMBER NOT NULL,
SOURCE_SYSTEM VARCHAR2(30),
FULL_NAME VARCHAR2(360),
PHONE_NUMBER VARCHAR2(240)
)
The primary key for this table is (CUSTOMER_ID, SOURCE_SYSTEM).
The table has numerous rows for which SOURCE_SYSTEM is null. At the database level, there is no issue, however when I try to access any of these rows via JPA Entity, it causes a number of issues:
1: Using em.find() to fetch a row with a null SOURCE_SYSTEM always results in a null being returned.
2: Using em.merge() to upsert a row with a null SOURCE_SYSTEM succeeds if the record does not exist in the table, but fails on subsequent updates because the merge ALWAYS results in an insert being run.
3: Using em.createQuery() to explicitly query for a row with a null causes the following exception:
Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.1.v20111018-r10243):
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
CUSTOMER.CUSTOMER_ID => 1
CUSTOMER.FULL_NAME => GUY PERSON
CUSTOMER.PHONE_NUMBER => 555-555-1234
CUSTOMER.SOURCE_SYSTEM => null)] during the execution of the query was detected to be null.
Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Customer sql="SELECT CUSTOMER_ID, FULL_NAME, PHONE_NUMBER, SOURCE_SYSTEM FROM CUSTOMER WHERE ((CUSTOMER_ID = ?) AND (SOURCE_SYSTEM IS NULL))")
Unfortunately, "Primary keys must not contain null" seems pretty final. I was unable to find too much information on workarounds for this error, which makes it seem like there is no solution.
THE QUESTION: I would like to know if anyone has any Java code-based solution that don't involve making changes to the database. My current workaround is to use ROW_ID as the #Id of the table, but this means I can no longer use em.merge() or em.find().
Here are my Java classes:
Customer.java:
#Entity
#Table(name = "CUSTOMER")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private Customer_Id key;
#Column(name = "CUSTOMER_ID", nullable = false, insertable = false, updatable = false)
private Long customerId;
#Column(name = "SOURCE_SYSTEM", length = 30, insertable = false, updatable = false)
private String sourceSystem;
#Column(name = "FULL_NAME", length = 360)
private String fullName;
#Column(name = "PHONE_NUMBER", length = 240)
private String phoneNumber;
//Setters, Getters, etc
...
}
Customer_Id.java
#Embeddable
public class Customer_Id implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "CUSTOMER_ID", nullable = false)
private Long customerId;
#Column(name = "SOURCE_SYSTEM", length = 30)
private String sourceSystem;
//Setters, Getters, etc
...
}
Primary keys cannot contain null (in JPA or in databases). Use a different value such as "" or " ".
Is the customer_id unique? if so then just remove the sourceSystem from the Id.
Otherwise, you could try logging a bug to have support for null ids added.