How to make JPA enforce the right FK constraint among many of them? - java-8

I have 4 tables : TABLE_A, TABLE_B, TABLE_C, TABLE_RESULT
Constraints :
FK_TABLE_A_ID is a foreign key refrerences IDA primary key in TABLE_A
FK_TABLE_B_ID is a foreign key refrerences IDB primary key in TABLE_B
FK_TABLE_C_ID is a foreign key refrerences IDC primary key in TABLE_C
Only one foreign column is set at the same time
Table_A :
IDA
Field_A1
Field_AN
1
dataA11
dataA12
2
dataA21
dataA22
3
dataA32
dataA32
Table_B :
IDB
Field_B1
Field_BN
1
dataB11
dataB12
2
dataB21
dataB22
3
dataB32
dataB32
Table_C :
IDC
Field_C1
Field_CN
1
dataB11
dataB12
2
dataB21
dataB22
3
dataB32
dataB32
TABLE_RESULT:
ID
FK_TABLE_A_ID
FK_TABLE_B_ID
FK_TABLE_C_ID
1
1
NULL
NULL
2
NULL
2
NULL
3
NULL
NULL
3
Not allowed cases :
ID
FK_TABLE_A_ID
FK_TABLE_B_ID
FK_TABLE_C_ID
1
NULL
NULL
NULL
2
1
2
NULL
3
1
NULL
3
4
NULL
2
3
5
1
2
3
Some tables are in production environnement, and this design is imposed i don't have other options like refactoring the existing code or modifying the db schema.
I'm wondering how to map those relations in order to enforce the 4th constraint. Any suggestions and hints will be appreciated
Is it possible to make FK_TABLE_A_OR_B_OR_C_ID reference multiple columns on multiple tables and add a new TYPE column to know which table is referenced.
ID
FK_TABLE_A_OR_B_OR_C_ID
TYPE
1
1
A
2
2
B
3
3
C

Related

How to make foreign key from two different table in third table?

I have three tables named as :
Staff(id,name,address);
Members(id,name,address);
Plans(id,Userid,type);
I want to create Userid from Plans foreign key for both Staff and Members table
How can i do it?
Umm ... not like that.
Staff and members should be stored in the same table, with additional column (and table to be referenced) which says whether someone is "staff" or "member". Then plans table doesn't have a problem any more.
SQL> create table type_sm
2 (id_sm number constraint pk_tsm primary key,
3 name varchar2(20) constraint ch_typ check (name in ('staff', 'member'))
4 );
Table created.
SQL> create table staff_members
2 (id_sta_mem number constraint pk_stamem primary key,
3 id_sm number constraint fk_sm_typ references type_sm (id_sm),
4 name varchar2(20),
5 address varchar2(30)
6 );
Table created.
SQL> create table plans
2 (id_pla number constraint pk_pla primary key,
3 id_sta_mem number constraint fk_plastamem references staff_members (id_sta_mem)
4 );
Table created.
SQL>

Conditional Foreign Key implementation in oracle

I have a requirement as mentioned below:
table1
------------------------
A B C D E
------------------------
1 2 * P Q
1 2 A Q P
1 3 B W U
-----------------------
Column A B C are the primary key on table1
table2
------------------------
A B C
------------------------
1 2 1
1 2 2
1 2 A
------------------------
column A B C needs to be implemented as composite foreign key on table2
for column A B C on table 1, the only exception is ,if column C in parent table (table 1) is *
then any value can appear in column C of child table (table2) provided column A and column B
are same in both tables.
We are looking for an implementation with minimal trigger .Currently we are not able to create foreign key because of this weird requirement.
Please suggest any alternate approach and make my day :)
This issue can be addressed without need of any new structure.
just by introducing a new_column (NEW_C) in child table (table2)
and using this column in foreign key contraint instead of 'column C'.
Steps are below:
1>alter table2 add (new_c varchar2(1));
2>update table2 set new_c= c;
3>Use newly introduced column NEW_C instead of colum C in FK
alter table table2
add constraint
fk_ref_table1 FOREIGN KEY (A,B,**NEW_C**)
references table1 (A,B,C);
4>Create a simple insert/update trigger on child table to maintain new column NEW_C.
CREATE OR REPLACE maintain_new_c
BEFORE
INSERT OR UPDATE
ON TABLE2 REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
v_count pls_integer;
BEGIN
SELECT COUNT(1)
INTO v_count
FROM table1 t1
WHERE t1.c='*'
AND t1.a= :NEW.a
AND t1.b= :NEW.b;
IF v_count=0 THEN
new_c := :NEW.c ;
ELSE
new_c := '*';
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR..........
END;
Alternative is to introduce a new virtual column (oracle 11g) on child table,
Use deterministic pl/sql function to
derive this virtual coulmn and then create foreign key constraint
using Virtual column.
But I will prefer trigger based approach as any udate/delete operation on parent table
will suffer because need to derive and check Virtual column each time.
I suspect this is a problem with the model. Both tables seem to represent two different types of things, which is why an ordinary FK constraint won't work.
I think you should split both tables into two, e.g.:
table1_noC
------------------------
A B D E
------------------------
1 2 P Q
table2_noC (with a FK to table1_noC)
------------------------
A B C
------------------------
1 2 1
1 2 2
------------------------
table1_C
------------------------
A B C D E
------------------------
1 2 A Q P
1 3 B W U
-----------------------
table2_C (with a FK to table1_C)
------------------------
A B C
------------------------
1 2 A
------------------------
You can then recreate your original design with a view that combines table1_noC + table1_C and another view that combines table2_noC + table2_C. If necessary you can even add "instead of" triggers to the views to translate DML on the views into the necessary DML on the underlying tables.

Duplicate Hierarchical Data to the same table - Oracle

How to duplicate hierarchical data to insert them at the same table, generating new Ids but keeping parent-child relationship
Re-insert the data but add the same large number to every ID.
Update:
If I understand your problem correctly, you want to copy data like this:
EMPLOYEE_ID MANAGER_ID
1 <null>
2 1
3 1
4 3
In this case, just adding 4 to every parent and child ID will create new rows but have the same relationships:
EMPLOYEE_ID MANAGER_ID
5 <null>
6 5
7 5
8 8

Oracle Sql Update with values using foreign key?

Table 1:
Name, x1-X2, fk1, fk2.
Table 2:
K1(parent for table 1),X
How to update table 1 whose second column x1-x2 depands on fk1,fk2 from Table 2
Table1:
a,1.0,1,2
b,-3.0,2,3
Table 2
1,4.0
2,5.0
3,2.0
With this setup:
CREATE TABLE table2 (k NUMBER PRIMARY KEY, x NUMBER);
CREATE TABLE table1 (
NAME VARCHAR2(10) PRIMARY KEY,
diff NUMBER,
fk1 NUMBER REFERENCES table2,
fk2 NUMBER REFERENCES table2);
the following update will "refresh" the colum table1.diff with the values of table2:
SQL> UPDATE (SELECT child.diff old_diff, parent2.x - parent1.x new_diff
2 FROM table1 child
3 JOIN table2 parent1 ON (child.fk1 = parent1.k)
4 JOIN table2 parent2 ON (child.fk2 = parent2.k))
5 SET old_diff = new_diff
6 WHERE old_diff != new_diff
7 OR (old_diff IS NULL AND new_diff IS NOT NULL)
8 OR (old_diff IS NOT NULL AND new_diff IS NULL);
Only the rows that need updating will be refreshed (thanks to the where clause).
Not quite sure I understand the question, referential integrity constraints do not prevent you from updating a table. If you need to do a lot of work in a transaction you can look into Deferred Constraints. Is there any chance you could clarify what you mean?
Not sure exactly what the problem is, maybe you need to rephrase the question a little.
In general, the foreign key constraint makes sure that there exists a corresponding row in the referenced table.
When you update a row with a foreign key, and try to set a value that does not point to such a master row, you will get an error, either immediately or when you commit (depending on when the constraint is enforced, it can be deferred).
Other than that, the update is no different than any other update.
Since the data in table 1 is dependent on the data in table 2 you won't be able to "Update" table 1 directly.
You will have to perform the update on table 2 and then recalculate table 1.
You could do it inside a transaction or perhaps a trigger on table 2.
Another option may be to have table one only hold the foreign keys and the name and then create a view that calculates the X1-X2 value.
EDIT
After looking at the sample data, I don't think you can unambiguously have table 2 be updates as a result of an update on table 1.
For example if you update the second column of table 1 to be 43, how would you know what values to set the specific rows of table 2 (it could be 40 and 3, 20 and 23, etc.)

Modeling One-to-Constant Relationship

Can a One-To-Constant Relationship be completely modeled in Oracle with constraints? In other words, the PARENT entity ALWAYS has EXACTLY n-CHILDREN of the child entity, and each child only has one parent.
Consider n to be a database constant.
Doing this so that it is sound and correct even when multiple sessions are doing updates is not easy. You will get yourself in a mess if you try this with triggers, and Oracle's declarative constraints are not powerful enough to express this.
It can be done as follows:-
Create a materialized view log on both the parent and the child tables
Create a materialized join view that joins them together and counts the number of children grouped by the parent. This must be REFRESH FAST ON COMMIT
Put a constraint on the materialized join view that the count of child records must equal "n" (your database constant)
You can then do a series of insert/update/delete statements. When you commit, the materialized view will refresh and if the condition is not met you will get a constraint violation error at that point.
A bonus bit of trickery is to only include rows that fail the constraint into the materialized view (HAVING count(ChildId) <> 5) so you do not waste any storage space.
Building upon the earler "chicken + egg" points, you can create deferrable constraints which aren't validated until commit time... these might help?
e.g.
ALTER TABLE AGREEMENTS ADD CONSTRAINT name FOREIGN KEY (column) REFERENCES table (column) DEFERRABLE INITIALLY DEFERRED;
I don't see how. It is the old question "which came first, the chicken or the egg?". How can you constrain the parent when no children have been added yet, and how can you add children without a parent?
you could create a new table, called something like "ValidParents" that only has the parent IDs that have N children, and keep it in sync with triggers.
There is an alternative solution to force each parent to have exactly either 0 or n children without materialized views using just check, foreign key and uniqueness constraints. For this, one has to number the children and add a field containing the number of the next sibling. Here an example for n=5 that works in PostgreSQL, for other DBS one has to adapt probably the type serial:
create table Tree(
id serial,
parent_id integer not null references Tree(id),
child_nr integer check(child_nr between 1 and 5),
next_sibling_nr integer,
unique (parent_id, child_nr),
check(next_sibling_nr in (child_nr+1, child_nr-4)),
check(((parent_id is null) and (child_nr is null) and
(next_sibling_nr is null)) or ((parent_id is not null)
and (child_nr is not null) and (next_sibling_nr is not null))),
foreign key (parent_id, next_sibling_nr) references Tree(parent_id, child_nr),
primary key (id)
);
The last (long) check constraint ensures that the fields parent_id, child_nr and next_sibling_nr are all null or all not null. The uniqueness constraint and the check for the child_nr field take care that a parent has at most 5 children. The other check constraint and the foreign key constraint on the pair (parent_id, next_sibling_nr) ensure that there are not less than 5 children.
After inserting a root with automatically generated id 1 with the command
insert into Tree (parent_id)
values (null);
one can add children always in packs of 5:
insert into Tree (parent_id, child_nr, next_sibling_nr)
values (1, 1, 2),
(1, 2, 3),
(1, 3, 4),
(1, 4, 5),
(1, 5, 1);
This solution is derived from the answers to a similar question I asked some weeks ago.
This may not be what you want, but I do have one method that does something similar.
The usual arrangement for one-to-many is something like this:
Primary Table:
primary_id (PK)
primary_stuff
Secondary Table:
secondary_id (PK)
primary_id (FK)
secondary_stuff
The alternative, to model a strict one-to-one would be:
Primary Table:
primary_id (PK)
secondary_id (FK, non-null)
primary_stuff
Secondary Table:
secondary_id (PK)
secondary_stuff
It might be a bit strange, but it works. A variation of this may be useful where there's a one-to-many with a one-to-one in it, such as having multiple addresses for a customer, but exactly one billing address.
An alternative solutionb to the chicken and egg problem is to use INSERT ALL. Because it is a single statement it obviates the need for deferrable foreign key constraints. It also provides a mechanism for inserting an exact number of dependent rows. Additional constraints prevent the insertion of additional rows. But we need a subsidiary table with foreign keys to prevent the accidental deletion of the rows of interest.
In this example, n = 3.
SQL> create table parent
2 ( pk_col number not null
3 , col1 varchar2(20)
4 , constraint par_pk primary key (pk_col)
5 )
6 /
Table created.
SQL>
SQL> create table child
2 ( pk_col number not null
3 , seqno number(1,0) not null
4 , col2 date
5 , constraint ch_pk primary key
6 (pk_col, seqno)
7 , constraint ch_par_fk foreign key
8 (pk_col) references parent (pk_col)
9 , constraint ch_ck check (seqno between 1 and 3)
10 )
11 /
Table created.
SQL>
SQL> create table child_lock
2 ( pk_col_1 number not null
3 , seqno_1 number(1,0) not null
4 , pk_col_2 number not null
5 , seqno_2 number(1,0) not null
6 , pk_col_3 number not null
7 , seqno_3 number(1,0) not null
8 , constraint chlk_pk primary key
9 (pk_col_1, seqno_1, pk_col_2, seqno_2, pk_col_3, seqno_3)
10 , constraint chlk_par_1_fk foreign key
11 (pk_col_1, seqno_1) references child (pk_col, seqno)
12 , constraint chlk_par_2_fk foreign key
13 (pk_col_2, seqno_2) references child (pk_col, seqno)
14 , constraint chlk_par_3_fk foreign key
15 (pk_col_3, seqno_3) references child (pk_col, seqno)
16 )
17 /
Table created.
SQL>
SQL> insert all
2 into parent values (pk_val, val_1)
3 into child values (pk_val, 1, sysdate)
4 into child values (pk_val, 2, sysdate+1)
5 into child values (pk_val, 3, sysdate+2)
6 into child_lock values (pk_val, 1, pk_val, 2, pk_val, 3)
7 select 999 as pk_val
8 , 'APPLE PIE' as val_1
9 from dual
10 /
5 rows created.
SQL>
SQL> insert into child values (999, 4, sysdate+4)
2 /
insert into child values (999, 4, sysdate+4)
*
ERROR at line 1:
ORA-02290: check constraint (APC.CH_CK) violated
SQL> insert into child values (999, 3, sysdate+4)
2 /
insert into child values (999, 3, sysdate+4)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.CH_PK) violated
SQL> insert into child values (999, 2.5, sysdate+4)
2 /
insert into child values (999, 2.5, sysdate+4)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.CH_PK) violated
SQL> delete from child
2 /
delete from child
*
ERROR at line 1:
ORA-02292: integrity constraint (APC.CHLK_PAR_1_FK) violated - child record found
SQL>
I accept the solution is a trifle contrived and also inflexible, but then so is the original requirement. It is also far from bulletproof - delete the row from CHILD_LOCK and you can delete one or more CHILD records.
You can create your tables as normal with a 1:M relationship, then on the child table have a count column with a check constraint that determines how many children can exist for a parent, as well as a unique constraint over the Parent ID + count column. e.g.:
CREATE TABLE Parent (PID NUMBER PRIMARY KEY);
CREATE TABLE Child (
PID NUMBER NOT NULL,
Count NUMBER(1) NOT NULL,
CONSTRAINT count_check CHECK (Count BETWEEN 1 AND 5),
CONSTRAINT parent_child_fk FOREIGN KEY (PID) REFERENCES Parent (PID),
CONSTRAINT count_unique UNIQUE (PID, Count));
The only thing this doesn't guarantee is that for each parent there are AT LEAST five children; to get around this you might create a materialized view with a constraint as WW suggests, or build something extra in the application (e.g. an "error" report).

Resources