Performing a sub insert statement within an insert statement using mybatis - java

So I have an object that I'm trying to insert into a database that has the following structure
int id
String name
Array tags
and I'd want to insert the first two columns into the following table
CREATE TABLE foo (
id number(20) PRIMARY KEY,
name varchar2(50) NOT NULL
);
and the array into this table
CREATE TABLE fooTags (
id number(20) PRIMARY KEY,
fooId number(20), //foreign key to foo. I don't know what the sql is for that.
tagName varchar2(50)
);
How would performing a sub insert that would take the id created by the initial insert work? I'd assume a SELECT is needed, but I'm unsure as to how it would be ordered to get the information needed inserted into the proper areas for each object.

I wrote 2 procedure;
If you can learn your seq names for ids ;
create or replace procedure FOO_INSERT(foo_name in varchar2, FooTags_tagName in varchar2 )
is
foo_seq_val number;
footag_seq_val number;
begin
select foo_seq.nextval into foo_seq_val from dual;
insert into foo(id,name) values (foo_seq_val, foo_name);
select footag_seq.nextval into footag_seq_val from dual;
insert into footags (id,fooid,tagName) values(footag_seq_val,foo_seq_val,FooTags_tagName);
commit;
end;
If you can not learn your seq names for ids ;
create or replace procedure FOO_INSERT_T(foo_name in varchar2, FooTags_tagName in varchar2 )
is
foo_seq_val number;
begin
insert into foo_T(name) values (foo_name);
select id into foo_seq_val from FOO_T where name =foo_name;
insert into footags_T (fooid,tagName) values(foo_seq_val,FooTags_tagName);
commit;
end;
If you pass ids ;
insert into foo (id, name) values (123,'foo_name');
insert into footags (id,fooid,tagname) select 444,id, 'tag_name' from foo ;
commit;
For second procedure I assume your foo_T.name values are unique or your other values for each row make your id unique. You can put in order end of your select with and ... and ..
You can see one COMMIT each method. Because if there is an error, transaction rollback all your inserts for foo_table and fooTags_table. This is accuracy.

My solution: two insert query, the first for parent object (foo table), and the second for his tags (fooTags table):
<insert id="fooInsert" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO foo (name) VALUES (#{name})
</insert>
<insert id="fooTagsInsert">
INSERT INTO fooTags ("fooId", "tagName") VALUES
<foreach item="tag" collection="tags" separator=",">
(#{id}, #{tag})
</foreach>
</insert>
The attributes "useGeneratedKeys", "keyProperty" and "keyColumn" are used to reload newly generated keys from the database, if JDBC driver supports the getGeneratedKeys function. Alternatively we must reload the id using a select query. More info: http://www.mybatis.org/mybatis-3/sqlmap-xml.html#insert_update_and_delete
The tags insert use "foreach" to iterate through tag names (in this case I used a String array, but they could be objects). The "inner" insert refers to the "id" from the "foo" object and "tag", that is the iterating String. In case of an object, we can access inner fields with "tag.", i.e. "tag.name".
Usage in Java code:
Foo foo = new Foo();
foo.setName("James");
foo.setTags(new String[] {"one", "two", "three"});
fooMapper.fooInsert(foo);
fooMapper.fooTagsInsert(foo);
Tables definitions (tested with PostgreSQL):
CREATE TABLE public.foo (
id numeric NOT NULL DEFAULT nextval('seq_foo_id'::regclass),
"name" varchar NULL,
CONSTRAINT foo_pk PRIMARY KEY (id)
)
CREATE TABLE public.footags (
id varchar NOT NULL DEFAULT nextval('seq_foo_id'::regclass),
"fooId" numeric NULL,
"tagName" varchar NULL,
CONSTRAINT footags_pk PRIMARY KEY (id),
CONSTRAINT footags_foo_fk FOREIGN KEY ("fooId") REFERENCES public.foo(id)
)

Related

Oracle - Hyphenated column in where clause

Consider a table :
ID COUNTRY_CODE
1 ab-cd-ef
2 gh-ef
3 cd-ab-pq-xy
And I need an sql query that selects the records which contain a specific country code.
The traditional LIKE approach works, of course:
select ID from TableName where COUNTRY_CODE like '%cd%';
The concern here is, this query would run over millions of records, thus increasing the cost of total operation. Due to the cost issues, nested tables is not an option here.
Note : The query can be parameterized with Java, if needed.
Is there any cost-effective way to handle such searchable columns?
Do not store lists as delimited strings. Either create a separate table:
CREATE TABLE TableName ( id NUMBER PRIMARY KEY );
CREATE TABLE TableName__Country_Codes(
id NUMBER REFERENCES table_name ( id ),
country_code CHAR(2),
PRIMARY KEY ( id, country_code )
);
Then you could use
SELECT ID
FROM TableName__Country_codes
WHERE 'cd' = COUNTRY_CODE;
Or use a nested table:
CREATE TYPE Char2List IS TABLE OF CHAR(2)
/
CREATE TABLE TableName(
ID NUMBER PRIMARY KEY,
Country_Code Char2List
) NESTED TABLE Country_code STORE AS Table_Name__Country_Codes;
Then you could use
SELECT ID
FROM TableName
WHERE 'cd' MEMBER OF COUNTRY_CODE;
If you have to use a delimited string then your query should include the delimiters in the search expression:
SELECT ID
FROM TableName
WHERE '-' || COUNTRY_CODE || '-' like '%-cd-%';
Otherwise, if you have longer list elements then %cd% could match cd or cda or bbcdbb.

How to populate object property with value generated by Mysql Trigger in MyBatis Insert

I can't get the value back from function. It does an insert on a table and must return a number. The data is insert correctly, but the number returned is always null.
Mysql
create table driver_order (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
area_start varchar(200),
area_end varchar(200),
order_number varchar(200),
create_user varchar(200),
primary key (id)
);
DELIMITER $$
CREATE TRIGGER seq_driver_order_number BEFORE INSERT ON driver_order
FOR each ROW
BEGIN
DECLARE seq_type INT(10);
SET seq_type = getUserNo(NEW.create_user);
SET NEW.order_number = getNextCommSequence("motor", seq_type);
END$$
DELIMITER ;
Mybatis
<insert id="insertOrder" useGeneratedKeys="true" keyProperty="id" parameterType="DriverOrder">
INSERT INTO
DRIVER_ORDER(ID,ORDER_NUMBER,AREA_START,AREA_END,CREATE_USER,CREATE_TIME)
VALUES
(#{id},
#{orderNumber,jdbcType=VARCHAR},
#{areaStart,jdbcType=VARCHAR},
#{areaEnd,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR},
now())
</insert>
The return Object all attributes have correctly value include id, except order_number which TRIGGER set value is return null.
Is there something wrong?
The problem is not with a trigger but how to make mybatis get value generated on mysql side during record insertion. Mybatis is rather simple tool in sense that you can't specify properties to columns mapping and everything else happens automagically.
Mybatis core is sql queries not properties-to-columns mappings like in hibernate for example. So mybatis only executes queries and simplifies setting parameters and construction objects from query result.
Nevertheless starting from version 3.2.6 you can use selectKey to get several values and set properties in the object being inserted. If you combine this with last_insert_id() you can get what you need. For your case it is done like this:
<insert id="insertOrder" parameterType="DriverOrder">
<selectKey keyProperty="id,orderNumber" keyColumn="ID,ORDER_NUMBER" order="AFTER" resultType="java.util.Map">
SELECT ID,ORDER_NUMBER FROM DRIVER_ORDER where ID = last_insert_id()
</selectKey>
INSERT INTO
DRIVER_ORDER(ID,ORDER_NUMBER,AREA_START,AREA_END,CREATE_USER,CREATE_TIME)
VALUES
(#{id},
#{orderNumber,jdbcType=VARCHAR},
#{areaStart,jdbcType=VARCHAR},
#{areaEnd,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR},
now())
</insert>

Referencing Surrogate keys

I have 2 tables Customers & Accounts in my Oracle Database. I have sequence numbers for generating surrogate key values for both tables.
CREATE SEQUENCE customers_seq NOCACHE;
CREATE SEQUENCE accounts_seq NOCACHE;
CREATE TABLE customers
(
customer_surrogate_id NUMBER(10),
customer_id VARCHAR2(8) UNIQUE NOT NULL,
customer_password VARCHAR2(20),
customer_name VARCHAR2(20),
customer_pan VARCHAR2(10) UNIQUE,
customer_email_id VARCHAR2(20) UNIQUE,
CONSTRAINT customer_pk
PRIMARY KEY (customer_surrogate_id)
);
CREATE TABLE accounts
(
accounts_surrogate_id NUMBER(10),
account_id VARCHAR2(10) UNIQUE NOT NULL,
customer_surrogate_id NUMBER(10),
account_type VARCHAR2(10),
account_currency VARCHAR2(20),
account_balance NUMBER(20, 2),
CONSTRAINT accounts_pk
PRIMARY KEY (accounts_surrogate_id),
CONSTRAINT accounts_fk
FOREIGN KEY (customer_surrogate_id)
REFERENCES customers(customer_surrogate_id)
);
I know how to use sequence_name.NEXTVAL & sequence_name.CURRVAL in insert statements to perform the reference
The problem is with using NEXTVAL & CURRVAL is that it assumes that inserts to both tables occur sequentially like
insert into Customers(// use NEXTVAL here)
insert into Accounts(// use CURRVAL here to reference the above row in Customers)
But in my java application, the multiple inserts for the Customers table can occur before even one insert occurs in Accounts table. CURRVAL will return the value of the last inserted row of the Customers table.
When inserting a row into Accounts table, I can get customers_id values in my application. Should the customer_id be used to query the Customers table to get the customer_surrogate_id as shown below?
insert into Customers(// use NEXTVAL here)
...
insert into Accounts(// use the customer_id to query and find customer_surrogate_id)
Is there better way to reference the Customers table in this situation?
Edit: I am using JDBC to access the database.
Simply select the value, and store it in a variable:
long customer1Id = selectNextValueFromSequence("customers_seq");
long customer2Id = selectNextValueFromSequence("customers_seq");
insertCustomerWithId(customer1Id);
insertCustomerWithId(customer2Id);
insertAccountWithCustomerId(customer1Id);
insertAccountWithCustomerId(customer2Id);

Assign ID number automatically

strSQL = "INSERT INTO emp(NO, EMP_NAME, EMP_TEL)VALUES(088000, 'JIMMY', *****)";
stmt.executeUpdate(strSQL);
I have this statement to insert a new employee into the database.
What if I want the employee NO to be automatically generated by adding 1 to the previous employee NO? How can this be done in JSP?
While not JSP, a possible solution would be to create an auto generated incrementing column (known as an identity column) in the database. Importantly, this avoids the race condition that exists with a solution that retrieves the current maximum and increments it.
MySQL example:
create table emp (
emp_id integer not null auto_increment,
...
);
Apache Derby example:
create table emp (
emp_id integer not null generated always as identity,
...
);
MS SQL Server 2008 R2 example:
create table emp (
emp_id integer not null identity,
...
);
The INSERT statements do not include the emp_id column. See Statement.getGeneratedKeys() for obtaining generated id if required.
Depending of your DB... I give you a mysql example.
create table emp{
NO int unsigned auto_increment,
EMP_NAME varchar(30) not null,
...
}
insert into emp(EMP_NAME,...) values ("Jimmy", ...);
Now you can ask mysql the last inserted id with
LAST_INSERT_ID()
Yes of course, you can do this by setting "employee no" to be unique and A_I (auto_increament) in this column properties
Check database Schema where you are creating table emp with ID int NOT NULL AUTO_INCREMENT
Then update the schema strSQL = "INSERT INTO emp(EMP_NAME, EMP_TEL) VALUES('ABC_NAME', '321321')";
Though it is possible BUT we should not do any logical operation into JSP. Forward all input in Servlet and do there.
There are several way to do.
Some of databases like Oracle has features like sequence, which allows you to increment numbers sequently and operates as atomic.
Set the column (possibly primary key) to auto increment ( database option ), and do not specify that "NO" in your query. That way, the NO column you didn't add will be added by database automatically.
You can get max values from database table and add 1 for new NO, or you can save those latest value even in file, memcached, whatever you want. The problem of this #3 is, if you don't make program to be atomic between GET LATEST VALUE, ADD 1, CALL DATABASE INSERT QUERY, multiple query can have same NO to use. It's OK, however, if NO is primary key since only very first update/insert query will executed and others query will be failed due to primary key unique violation... but problematic in some cases.
You can use the AUTOINCREMENT option on the field NO on the database, or execute a query like SELECT MAX(NO) FROM emp
and get the max value
I think this will be going to solve your doubt in database and use this following query as:
CREATE TABLE:
CREATE TABLE `test` (
`id` INT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`emp_name` VARCHAR(50) NOT NULL,
`emp_tel` INT(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
INSERT TABLE METHOD:1
INSERT INTO test
VALUES (0,jmail,1234567)OR(?,?,?);
INSERT TABLE METHOD:2
INSERT INTO test (id,emp_name,emp_tel)
VALUES (0,jmail,1234567);
If you had any doubt give me comment.
And if your using the sqlyog to use the shortcut.
if your wants this method like following as:
PreparedStatement ps = con.prepareStatement("INSERT INTO test(id,emp_name,emp_tel)
VALUES (0,jmail,1234567)");
ps.executeUpdate();
PreparedStatement ps = con.prepareStatement("INSERT INTO test(id,emp_name,emp_tel)
VALUES (?,?,?)");
ps.setString(1, id );
ps.setString(2, name);
ps.setString(3, tel);
ps.executeUpdate();

Howto return ids on Inserts with Ibatis ( with RETURNING keyword )

I'm using iBatis/Java and Postgres 8.3.
When I do an insert in ibatis i need the id returned.
I use the following table for describing my question:
CREATE TABLE sometable ( id serial NOT NULL, somefield VARCHAR(10) );
The Sequence sometable_id_seq gets autogenerated by running the create statement.
At the moment i use the following sql map:
<insert id="insertValue" parameterClass="string" >
INSERT INTO sometable ( somefield ) VALUES ( #value# );
<selectKey keyProperty="id" resultClass="int">
SELECT last_value AS id FROM sometable_id_seq
</selectKey>
</insert>
It seems this is the ibatis way of retrieving the newly inserted id. Ibatis first runs a INSERT statement and afterwards it asks the sequence for the last id.
I have doubts that this will work with many concurrent inserts. ( discussed in this question )
I'd like to use the following statement with ibatis:
INSERT INTO sometable ( somefield ) VALUES ( #value# ) RETURNING id;
But when i try to use it within a <insert> sqlMap ibatis does not return the id. It seems to need the <selectKey> tag.
So here comes the question:
How can i use the above statement with ibatis?
The <selectKey> element is a child of the <insert> element and its content is executed before the main INSERT statement. You can use two approaches.
Fetch the key after you have inserted the record
This approach works depending on your driver. Threading can be a problem with this.
Fetching the key before inserting the record
This approach avoids threading problems but is more work. Example:
<insert id="insert">
<selectKey keyProperty="myId"
resultClass="int">
SELECT nextVal('my_id_seq')
</selectKey>
INSERT INTO my
(myId, foo, bar)
VALUES
(#myId#, #foo#, #bar#)
</insert>
On the Java side you can then do
Integer insertedId = (Integer) sqlMap.insert("insert", params)
This should give you the key selected from the my_id_seq sequence.
Here is simple example:
<statement id="addObject"
parameterClass="test.Object"
resultClass="int">
INSERT INTO objects(expression, meta, title,
usersid)
VALUES (#expression#, #meta#, #title#, #usersId#)
RETURNING id
</statement>
And in Java code:
Integer id = (Integer) executor.queryForObject("addObject", object);
object.setId(id);
This way more better than use :
It's simpler;
It have not requested to know sequence name (what usually hidden from postgresql developers).

Categories

Resources