I have one table Message in which I keep both incoming and outgoing messages. A column "category" is kept to differentiate, that can have 1 = INCOMING, 2 = OUTGOING. Outgoing messages can have attachments. The following is my Message entity class:
#Entity("MESSAGE")
class Message{
Long id; //primary key
String additionalStringId;
private Long senderId;
private Long receiverId;
Category messageCategory;
#OneToMany(
mappedBy = "message",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<AttachmentEntity> outgoingAttachments= new ArrayList<>();
}
The problem is that AttachmentEntity is linked with the message via the "additionalStringId" and I can not impact the database schema.
class AttachmentEntity{
#ManyToOne
#JoinColumn(name = "additionalStringId", referencedColumnName = "additionalStringId")
private Message message;
}
I know it's not working snipping code but my problem appears when sender = receiver, when I try to send a message to myself. In that case in my database I will have two messages, with the same additionalStringId but with category = 1 and the other with category = 2. My attachment entity list tries to link to message but two messages are visible. What can I do to fix this problem?
I tried separating the two categories into separate entities with #Where and #DiscriminatorFormula and #DiscriminatorValue but I could not get it to work. What can I do?
Table DDLs:
CREATE TABLE "MESSAGE"
( "ID" NUMBER NOT NULL ENABLE,
"MESSAGE_ID" VARCHAR2(60) NOT NULL ENABLE,
"CATEGORY" NUMBER,
"SENDER_ID" NUMBER,
"RECEIVER_ID" NUMBER )
CREATE TABLE "OUTBOX_ATTACHMENT"
( "ID" NUMBER NOT NULL ENABLE,
"MESSAGE_ID" VARCHAR2(60) NOT NULL ENABLE,
)
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "additionalStringId", referencedColumnName = "id", nullable = false)
private Message message;
#OneToMany(mappedBy = "message", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<AttachmentEntity> outgoingAttachments = new ArrayList<>();
something like this might work.
I think the problem you are facing is not at the JPA level, but at the table definition level (DDL).
If you change the table definition as shown below, the FK relationship becomes easy to establish:
CREATE TABLE "MESSAGE"
( "ID" NUMBER NOT NULL,
"MESSAGE_ID" VARCHAR2(60) NOT NULL,
"CATEGORY" NUMBER,
"SENDER_ID" NUMBER,
"RECEIVER_ID" NUMBER,
constraint uq1 unique (category, message_id)
);
CREATE TABLE "OUTBOX_ATTACHMENT"
( "ID" NUMBER NOT NULL ,
category number not null constraint chk1 check (category = 2),
"MESSAGE_ID" VARCHAR2(60) NOT NULL ,
constraint fk1 foreign key (category, message_id)
references message (category, message_id)
);
As you can see the FK includes two columns: category, and message_id. But, as per requirements, in the attachement table category can only accept attachment to outgoing messages (value 2).
Related
I have two tables. I want to join both but there is no joint column between them. To generate joint column, i added new field on my cell entity. Field name is cityCode, which is created by lac column substring index 1 to 3 then converted to int. So actually when you check DB you cant see cityCode column. I didnt store it on DB.
I have city entity also and it has id column which is name cityCode also.
I want to join tables with cityCode columns.
Here is my json list
cell entity : https://jsoneditoronline.org/?id=a5ed0c0ab0c249f89c9360ecca3f0750
city entity : https://jsoneditoronline.org/?id=205565db82014c75b292122f141239c8
I code non-existing column by using code below. it works well as u see it on my cell entity json.
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
#Column(name = "LAC", insertable = false, updatable = false)
private Integer cityCode;
public Integer getCityCode()
{
return Integer.parseInt(lac.substring(1,3));
}
I try to use code below it throws error.
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cityCode")
private City city;
oracle.jdbc.OracleDatabaseException: ORA-00904: "GCELL6900X0_"."CITY_CODE": invalid identifier
its looking CITY_CODE column on DB so i know why it throws error but i couldnt found any solution.
Using deleteById in spring boot with a one to many relationship the query being generated tries to set the foreign key to null in the referenced entities instead of deleting them. I am using the default repository deleteById
I've set the Cascadetype to ALL and OrpahnRemoval to true on the definition of the foreign key in the entity and I've set ON DELETE CASCADE in the DDL that created the table.
Here is the delete operation in the controller class
#Transactional
#DeleteMapping("transferImage/{imageId}")
public void deleteTransferImage(#PathVariable int imageId) {
repository.deleteById(imageId);
}
Here is the reference from the parent to the child entity
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
#JoinColumn(name = "TRANSFER_IMAGE_ID")
private List<TransferPartition> partitions = new ArrayList<>();
Here is the definition of the foreign key in the child entity
#JsonIgnore
#ManyToOne
#JoinColumn(name = "TRANSFER_IMAGE_ID", referencedColumnName = "TRANSFER_IMAGE_ID")
private TransferImage image;
Here is the DDL used to create the two tables
CREATE TABLE TRANSFER_IMAGE (
TRANSFER_IMAGE_ID SERIAL CONSTRAINT TRANSFER_IMAGE_PK PRIMARY KEY,
IMAGE_NAME VARCHAR(50) CONSTRAINT TRANSFER_IMAGE_NAME_UK UNIQUE NOT NULL ,
REQUESTED_PART_SIZE_MB INTEGER NOT NULL,
SIZE_BYTES INTEGER NOT NULL,
IMAGE_MD5_HASH VARCHAR(100),
NUMBER_PARTITIONS INTEGER,
DELETED BOOLEAN NOT NULL
);
CREATE TABLE TRANSFER_PARTITION (
TRANSFER_PARTITION_ID SERIAL CONSTRAINT TRANSFER_PARTITION_PK PRIMARY KEY,
TRANSFER_IMAGE_ID INTEGER NOT NULL CONSTRAINT TRANSFER_PARTITION_IMAGE_FK REFERENCES TRANSFER_IMAGE ON DELETE CASCADE ON UPDATE CASCADE,
PARTITION_NUMBER INTEGER NOT NULL,
PARTITION_MD5_HASH VARCHAR(100) NOT NULL,
SIZE_BYTES INTEGER NOT NULL
);
Here is the query that appears in the log
Hibernate:
select
transferim0_.transfer_image_id as transfer1_13_0_,
transferim0_.deleted as deleted2_13_0_,
transferim0_.image_md5_hash as image_md3_13_0_,
transferim0_.image_name as image_na4_13_0_,
transferim0_.number_partitions as number_p5_13_0_,
transferim0_.requested_part_size_mb as requeste6_13_0_,
transferim0_.size_bytes as size_byt7_13_0_,
partitions1_.transfer_image_id as transfer5_14_1_,
partitions1_.transfer_partition_id as transfer1_14_1_,
partitions1_.transfer_partition_id as transfer1_14_2_,
partitions1_.transfer_image_id as transfer5_14_2_,
partitions1_.partition_number as partitio2_14_2_,
partitions1_.partition_md5_hash as partitio3_14_2_,
partitions1_.size_bytes as size_byt4_14_2_
from
transfer_image transferim0_
left outer join
transfer_partition partitions1_
on transferim0_.transfer_image_id=partitions1_.transfer_image_id
where
transferim0_.transfer_image_id=?
Hibernate:
update
transfer_partition
set
transfer_image_id=null
where
transfer_image_id=?
I was expecting that all the child entities(TransferPartition) that reference the parent (TransferImage) to be deleted when I delete the parent by its primary key. Instead I get a null constraint error referring to the foreign key column. It looks to me like the generated SQL is setting the foreign key column to null instead of deleting the row.
ERROR: null value in column "transfer_image_id" violates not-null constraint
Detail: Failing row contains (1, null, 1, asdfaa1-1, 20000000).
If I delete the image from the transfer_image table from the psql prompt the delete cascades properly and the referenced partitions are removed.
delete from transfer_image i where i.transfer_image_id = 1
Your problem is in the definition of the foreign key relation in the TransferImage class.
Instead of
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
#JoinColumn(name = "TRANSFER_IMAGE_ID")
private List<TransferPartition> partitions = new ArrayList<>();
you should use
#OneToMany(mappedBy="image", cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
private List<TransferPartition> partitions = new ArrayList<>();
See also https://www.baeldung.com/hibernate-one-to-many
I'm retrieving data from a legacy database, thus have no control at all over the schema.
I frequently need to check for a static value in a separate column to remove false matches.
create table mySource (
id int,
...
)
create table aRelation (
srcId int,
myFK int,
relationLimit varchar
)
create table aTarget (
id int,
...
myFK int,
aLimit varchar,
...
)
"relationLimit" and/or "aLimit" above must match a static value.
I can only find the standard annotations:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "aRelation",
joinColumns = {
#JoinColumn(name = "srcId") },
#inverseJoinColumns = {
#JoinColumn(name = "myFK", referencedColumnName = "myFK")
})
private List<ATarget> targets;
But I can't find any way to annotate the requirement(s)
aLimit = "Something"
relationLimit="SomethingElse"
If this is obvious in the documentation please tell me how and where to read it.
If you use Hibernate you can try #Filter annotation on entity.
#Filter(name="betweenLength", condition="aLimit = 'Something' and relationLimit='SomethingElse'")
More on this topic you can find here
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.