I have two tables in a single database and I want to persist data in tables one after another.
public Mono<Void> createProduct(final Product product){
AccountProcessingDetails apd = AccountProcessingDetails
.builder()
.accountNumber("121212")
.status("INPROGRESS")
.build();
this.productRepository.save(product).map(result-> accountRepository.save(apd));
return Mono.empty();
}
The following code doesn't save anything in tables and produces no error.
The product object has only been set the following values
{
"description": "iPad",
"price": 30000.96,
"prodid": 116
}
Script for two tables are as below:
CREATE TABLE IF NOT EXISTS product(
id SERIAL PRIMARY KEY,
prodid numeric NOT NULL UNIQUE,
description VARCHAR (500),
price numeric (10, 2) NOT NULL
);
CREATE TABLE IF NOT EXISTS ACCOUNT_PROCESSING_DETAILS
(
ID SERIAL PRIMARY KEY,
ACCOUNT_NUMBER NUMERIC NOT NULL ,
STATUS VARCHAR(10) NOT NULL
);
Can anyone help me here how to persist data in transactional manner in these two tables?
Related
I have a question.
My customer has a database with some tables like this:
Table A
a_id (type uuid, is primary key)
[...]
Table B
b_id (type uuid, is primary key)
[...]
Table AB
a_id (type uuid, is primary key with b_id)
b_id (type uuid, is primary key with a_id)
level (type int) [...]
The relations between A table and B table is many to many.
This is no problem:
// On model A
return $this->belongsToMany('App\Models\B', 'App\Models\AB', 'a_id', 'b_id');
// On model B
return $this->belongsToMany('App\Models\A', 'App\Models\AB', 'b_id', 'a_id');
Now I need get level from table AB at the same time.
I've tried to add
->withPivot('level');
at the end, Something like this:
return $this->belongsToMany('App\Models\A', 'App\Models\AB', 'b_id', 'a_id')->withPivot('level');
If I do this, I get not errors, but level not appears on pivot.
Only appears a_id and b_id.
Someone helps me to obtein level??
In code, I tried with #Interleaved in 1-many relationship at non-owning side to get child list. Could anyone help with below questions:
How to implement bidirectional relationship e.g. get parent from child for 1-1, 1-many relationship
Regarding many-many relationship, what are best practices to implement it and how to implement bidirectional relationship for it.
Thank you very much.
Cloud Spanner currently doesn't offer a way to enforce foreign-key constraints between non-interleaved tables. You will have to enforce such constraints in your application logic. You could use DML statements in Cloud Spanner(that come with the ability to read-your-writes in a Cloud Spanner transaction) to enforce these constraints at insert time by inserting into your tables as follows:
INSERT INTO Referenced(key1,value1) VALUES ('Referenced','Value1');
INSERT INTO Referencing(key2, value2, key1)
SELECT 'Referencing', 'Value2', key1 FROM Referenced WHERE
key1 = 'Referenced';
Running the two statements in a read-write transaction will ensure that the PK-FK relationship between the Referenced and Referencing table is always maintained at insert time. You may have to similarly modify update requests/SQL update statements in your application logic to enforce the PK-FK constraint for updates.
For a 1-many relationship, when using interleaved tables, then the child row's primary key already contains the primary key of its parent, so it is trivial to get the parent row.
CREATE TABLE parent (
parent_key INT64 NOT NULL,
...
) PRIMARY KEY (parent_key);
CREATE TABLE child (
parent_key INT64 NOT NULL,
child_key INT64 NOT NULL,
...
) PRIMARY KEY (parent_key, child_key),
INTERLEAVE IN PARENT parent ON DELETE CASCADE;
If for some reason you do not have the key of the parent, and only the key of the child, then for efficiency you would need to create an index for the reverse lookup:
CREATE INDEX child_to_parent_index
ON child (
child_key
);
and force use of that index when performing the query for the parent:
SELECT
p.*
FROM
parent as p
JOIN
child#{FORCE_INDEX=child_by_id_index} AS c ON p.parent_key = c.parent_key
WHERE
c.child_key = #CHILD_KEY_VALUE;
Many-many relationships would have to be implemented using a 'mapping' table linking table1-key to table2-key.
You will also need a top-level index to get efficient reverse-lookups, and use the FORCE_INDEX directive as above in your queries.
And as #adi mentioned, foreign key constraints would have to be enforced by the application.
CREATE TABLE table1 (
table1_key INT64 NOT NULL,
...
) PRIMARY KEY (table1_key);
CREATE TABLE table2 (
table2_key INT64 NOT NULL,
...
) PRIMARY KEY (table2_key);
CREATE TABLE table1_table2_map (
table1_key INT64 NOT NULL,
table2_key INT64 NOT NULL,
) PRIMARY KEY (table1_key, table2_key);
CREATE INDEX table2_table1_map_index
ON table1_table2_map (
table2_key
) STORING (
table1_key
);
Your application would be responsible for keeping the referential integrity of the mapping table - deleting the mapping rows when rows in table1 or table2 are deleted
If you want to use interleaved tables, then if your application needs to perform bi-directional lookups, you may have to create 2 mapping tables - as a child of each parent, so that finding the mappings from both directions are equally efficient.
CREATE TABLE table1 (
table1_key INT64 NOT NULL,
...
) PRIMARY KEY (table1_key);
CREATE TABLE table2 (
table2_key INT64 NOT NULL,
...
) PRIMARY KEY (table2_key);
CREATE TABLE table1_table2_map (
table1_key INT64 NOT NULL,
table2_key INT64 NOT NULL,
) PRIMARY KEY (table1_key, table2_key),
INTERLEAVE IN PARENT table1 ON DELETE CASCADE;
CREATE TABLE table2_table1_map (
table2_key INT64 NOT NULL,
table1_key INT64 NOT NULL,
) PRIMARY KEY (table2_key, table1_key),
INTERLEAVE IN PARENT table2 ON DELETE CASCADE;
Note that the application needs to keep both of these mapping tables up to date -- ie when deleting a row from table1, the application has to get the referenced table2_key values and delete the mappings from the table2_table1_map (and vice versa).
I have an integration test running against either a MySQL instance or an Oracle instance.
The test passes fine when building the Maven project against the MySQL instance.
Here is the table structure:
drop table if exists operator;
create table operator (
id bigint(20) unsigned not null auto_increment,
version int(10) unsigned not null,
name varchar(50),
unique key name (name),
description varchar(255),
operator_id varchar(50),
unique key operator_id (operator_id),
image varchar(255),
url varchar(255),
country_id bigint(20) unsigned not null,
primary key (id),
unique key id (id),
key country_id (country_id),
constraint operator_fk1 foreign key (country_id) references country (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
But when the same test runs against the Oracle instance then it gives me the exception:
1. insert into operator (version, country_id, description, image, name, operator_id, url, id) values (0, 19, 'The SFR operator', 'sfr.jpg', 'SFR', NULL, 'sfr.fr', 10)
java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (NITROPROJECT.OPERATOR_FK1) violated - parent key not found
Here is the table structure:
create table operator (
id number(10) not null,
version number(10) not null,
name varchar2(50),
constraint operator_u1 unique (name),
description varchar2(255),
operator_id varchar2(50),
constraint operator_u2 unique (operator_id),
image varchar2(255),
url varchar2(255),
country_id number(10) not null,
constraint operator_pk primary key (id),
constraint operator_fk1 foreign key (country_id) references country (id)
);
create sequence sq_id_operator increment by 1 start with 1 nomaxvalue nocycle cache 10;
create or replace trigger tr_id_inc_operator
before insert
on operator
for each row
declare
begin
if (:new.id is null)
then
select sq_id_operator.nextval into :new.id from dual;
end if;
end;
/
create table country (
id number(10) not null,
version number(10) not null,
code varchar2(4) not null,
constraint country_u1 unique (code),
name varchar2(50) not null,
list_order number(10),
constraint country_pk primary key (id)
);
create sequence sq_id_country increment by 1 start with 1 nomaxvalue nocycle cache 10;
create index country_i1 on country (list_order, name);
create or replace trigger tr_id_inc_country
before insert
on country
for each row
declare
begin
select sq_id_country.nextval into :new.id from dual;
end;
/
The country is created with the following service:
#Modifying
#Transactional(rollbackFor = EntityAlreadyExistsException.class)
#Override
public Country add(Country country) {
if (findByCode(country.getCode()) == null) {
// Save the returned id into the entity
country = countryRepository.saveAndFlush(country);
return country;
} else {
throw new EntityAlreadyExistsException();
}
}
I can see in the console log that the country is actually created and that its primary key id is returned:
2014-09-19 13:00:05,839 DEBUG [sqlonly] com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:147)
1. insert into country (version, code, list_order, name, id) values (0, 'fr', 1, 'France', 19)
In fact, I also added a finder call to make sure the country could be retrieved after it being created:
countryFR = new Country();
countryFR.setCode("fr");
countryFR.setName("France");
countryFR.setListOrder(1);
Country country = countryService.findByCode(countryFR.getCode());
if (country == null) {
countryFR = countryService.add(countryFR);
} else {
countryFR = country;
}
Country myc = countryService.findById(countryFR.getId());
if (myc != null) {
logger.debug("==============>> Found the country id: " + myc.getId());
}
And the console log does show the logger output:
2014-09-19 13:00:05,854 DEBUG [BTSControllerTest] ==============>> Found the country id: 19
NOTE: The console log does NOT show any select statement corresponding to that findById call.
And then comes the attempt to insert an operator:
1. insert into operator (version, country_id, description, image, name, operator_id, url, id) values (0, 19, 'The SFR operator', 'sfr.jpg', 'SFR', NULL, 'sfr.fr', 10)
You can see that the country id is the same as the one for the inserted country.
To sum things up:
The above note about the absence of select statement makes me wonder if the country was really inserted or not.
The primary key id of 19 retrieved after inserting the country leads me to think the country was actually inserted.
So how come Oracle complains it cannot find it for the operator foreign key ?
I'm using JPA2 hibernate-jpa-2.1-api 1.0.0.Final and spring-data-jpa 1.6.2.RELEASE and hibernate 4.3.6.Final
Here are the connection properties:
jpaPropertiesMap.put("hibernate.dialect", databaseProperties.getHibernateDialect());
jpaPropertiesMap.put("hibernate.show_sql", "true");
jpaPropertiesMap.put("hibernate.format_sql", "true");
jpaPropertiesMap.put("hibernate.hbm2ddl.auto", databaseProperties.getHibernateHbm2ddlAuto());
jpaPropertiesMap.put("hibernate.transaction.factory_class", "org.hibernate.transaction.JDBCTransactionFactory");
jpaPropertiesMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
jpaPropertiesMap.put("hibernate.c3p0.min_size", "5");
jpaPropertiesMap.put("hibernate.c3p0.max_size", "20");
jpaPropertiesMap.put("hibernate.c3p0.timeout", "1000");
jpaPropertiesMap.put("hibernate.c3p0.max_statements", "50");
EDIT:
I added the following properties to the JPA setup:
jpaPropertiesMap.put("hibernate.connection.autocommit", "true");
jpaPropertiesMap.put("hibernate.cache.use_query_cache", "false");
jpaPropertiesMap.put("hibernate.cache.use_second_level_cache", "false");
But it didn't change anything in the issue.
I found the solution. My sequence trigger was missing an if statement. Now it has one as in: if (:new.id is null)
create or replace trigger tr_id_inc_country
before insert
on country
for each row
declare
begin
if (:new.id is null)
then
select sq_id_country.nextval into :new.id from dual;
end if;
end;
/
I suppose Hibernate was getting a sequence number for the insert, and then Oracle was getting another one at commit time. I'm just guessing here.
I have a situation where I need to enforce a unique constraint on a column[attribute] depending on another column value.
So for example, I have a table like Table(ID, EID, Name, ISDeleted)
ISDeleted can only have a value null or 'y' (active or deleted), and i want to create a unique constraint on EID, ISDeleted only when ISDeleted = null, since I dont care if there are multiple deleted records with the same id. Please note that, EID can have null value.
I am using Oracle DB for this.
You can't create a constraint. But you can create a unique function-based index. This takes advantage of the fact that Oracle does not index NULL values-- any rows where isDeleted is NOT NULL will not be included in the index so the unique constraint won't apply to them.
CREATE UNIQUE INDEX one_not_deleted
ON table_name( (CASE WHEN isDeleted IS NULL
THEN eid
ELSE null
END) );
I want to write a query that presents for every event (tbl_events) all objects (tbl_objects) related to it (relation type — M:N).
I have a problem with tables that are connection tables (association class) that holds only foreign keys of the 2 tables that connects.
For instance, tbl_events is connected with a connection table named tbl_object_has_tbl_events to tbl_objects.
Here is a structure of connected tables:
tbl events has: eventID, eventName
tbl_object has: objectID, objectName
tbl_object_has_tbl_events: eventID, objectID
Here is what I tried to write:
IList dataList = (from dEvent in App.glidusContext.tbl_events.
join dObject in App.glidusContext.tbl_objects
on dEvent.tbl_objects equals dObject.objectID
select new { dEvent.eventName, dObject.objectName}).ToList();
I can't reach the connection table tbl_object_has_tbl_events
How I can implement such query, when I have an M:N relationship?
UPDATE Generation of Many-to-many relationship:
-- -----------------------------------------------------
-- Table tbl_events
-- -----------------------------------------------------
CREATE TABLE tbl_events (
eventID INT NOT NULL IDENTITY,
eventName NVARCHAR(100) NOT NULL,
PRIMARY KEY (eventID));
-- -----------------------------------------------------
-- Table tbl_objects
-- -----------------------------------------------------
CREATE TABLE tbl_objects (
objectID INT NOT NULL IDENTITY,
objectName NVARCHAR(100) NOT NULL,
PRIMARY KEY (objectID));
-- -----------------------------------------------------
-- Table tbl_objects_has_tbl_events
-- -----------------------------------------------------
CREATE TABLE tbl_objects_has_tbl_events (
objectID INT NOT NULL,
eventID INT NOT NULL,
PRIMARY KEY (objectID, eventID),
CONSTRAINT fk_tbl_objects_has_tbl_events_tbl_objects
FOREIGN KEY (objectID)
REFERENCES tbl_objects (objectID)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_tbl_objects_has_tbl_events_tbl_events
FOREIGN KEY (eventID)
REFERENCES tbl_events (eventID)
ON DELETE CASCADE ON UPDATE CASCADE);
The entity data model doesn't show tables that only contain FK's. So in your case the Events entity will have a navigation property Objects and your Object entity will have a navigation property Events.
So to get your information you could write a query like this:
IList dataList = (from dEvent in App.glidusContext.tbl_events
from dObject in dEvent.Objects
select new { dEvent.eventName, dObject.objectName}).ToList();