How to write "INSERT IF EXISTS UPDATE" in Oracle using JAVA - java

I have a ERROR_MSG table which stores error messages with some ids. I want to insert error message if id is not present in table and if its present update error message. Inserting using below java JDBC code.
ID ERROR_MSG
1 ERR1
2 ERR2
3 ERR3
This is my code:
insertQry = "SQL";
Connection con = null;
PreparedStatement stmt = null;
try {
con = getDataSource().getConnection();
stmt = con.prepareStatement(insertQry);
for(ListingAckNackData errorList: listOfListingERROR) {
stmt.setLong(1, eqGlobalData.getSrcMsgId());
stmt.setString(2, errorList.getGliId());
if (null != errorList.getListingRevisionNo()) {
stmt.setInt(3, errorList.getListingRevisionNo());
} else {
stmt.setNull(3, Types.NULL);
}
if (null != errorList.getErrorMessage()) {
stmt.setString(4, errorList.getErrorMessage());
} else {
stmt.setNull(4, Types.NULL);
}
stmt.addBatch();
}
stmt.executeBatch();
}

The simplest solution in JAVA is to check if the row exist.
You start by getting a row count for the specific id you want to insert/update
select count('a') as rowExist from table where id = ?
Then, based on the result, you can easily create your query
if(rowExist > 0){
query = "update ..";
else
query = "insert ...";
Note that the parameters are probably not in the same order as you expect, you need to create the insert in the correct order to have the id at the end (since update need a where clause)
insert into Table (name, birthday, id) values (?, ?, ?)
update Table set name = ?, birthday = ? where id = ?

It is possible to run a database statement as questioned. Simply use SQL command MERGE INTO... IF NOT MATCHED INSERT... IF MATCHED UPDATE ...
You will find an full example and documentation here.

Related

Get the inserted id of the record before or after execute the insert statement in sql

I have this method in my DAO class to insert record to a table called idea this is my method:
public long addIdea(AddIdeaDto addIdeaDto, int userId) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement(
"INSERT INTO IDEA ( IDEA.I_ID,IDEA.I_NO,IDEA.I_APPROVER_NAME_CODE, IDEA.I_TITLE,IDEA.I_DESCRIPITION, IDEA.I_CREATED_DATE,IDEA.I_STATUS_CODE, "
+ "IDEA.I_IS_CODE, IDEA.I_CONTRIBUTION_CODE, IDEA.I_POSITIVE_IMPACT, IDEA.I_SECOND_MEMBER_ID,IDEA.I_THIRD_MEMBER_ID,IDEA.I_FOURTH_MEMBER_ID,"
+ "IDEA.I_FIFTH_MEMBER_ID, IDEA.I_POINTS,IDEA.I_CREATED_USER_ID)"
+ " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
preparedStatement.executeQuery("SELECT IDEA_SEQ.nextval FROM DUAL");
// Set parameters
preparedStatement.setObject(1, Types.NUMERIC);
preparedStatement.setObject(2, Types.NUMERIC);
preparedStatement.setObject(3, addIdeaDto.getApproverNameCode());
preparedStatement.setString(4, addIdeaDto.getTitle());
preparedStatement.setString(5, addIdeaDto.getDescription());
preparedStatement.setDate(6, addIdeaDto.getCreatedDate() == null ? null
: new java.sql.Date(addIdeaDto.getCreatedDate().getTime()));
preparedStatement.setObject(7, addIdeaDto.getStatusCode());
preparedStatement.setObject(8, addIdeaDto.getIsNewCode());
preparedStatement.setObject(9, addIdeaDto.getContributionCode());
preparedStatement.setString(10, addIdeaDto.getPositiveImpact());
preparedStatement.setObject(11, addIdeaDto.getSecondMemberName());
preparedStatement.setObject(12, addIdeaDto.getThirdMemberName());
preparedStatement.setObject(13, addIdeaDto.getFourthMemberName());
preparedStatement.setObject(14, addIdeaDto.getFifthMemberName());
preparedStatement.setObject(15, addIdeaDto.getPoints());
preparedStatement.setInt(16, userId);
preparedStatement.executeQuery();
return addIdeaDto.getIdeaId();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
actually what I want is after or before the insert statement I want to get the id (IDEA_SEQ.nextval) and save it in a value in order to use it as an input to insert in anther table.
For example, I insert this record : id = 1 , no = 1, approver code = 2, title = 'test'.............
I want this value id = 1 to use it in order to insert in table A, A_id = 33, IDEA.I_ID = 1, A_name ='testing'
how i can achieve it in properer way?
I update the code based on the comments that i receive but I did not achieve it
Usually ID that need to be reuse can be handle using a previous and separate SQL query
previousPreparedStatement = connection.prepareStatement(
"select IDEA_SEQ.nextval as nextval from dual");
Result saved as a int or String parameter according to column (number or varchar) which is passed to the existing insert statement:
(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
Notice also an answer from DBA forum
you won't be able to use plain SQL to overcome this limitation: you will need some PL/SQL
A better way to handle this is the RETURNING INTO clause, which uses a single, atomic statement:
INSERT INTO mytable (id, col1, col2)
VALUES ( seq_id.nextval, c1, c2 )
RETURNING id INTO myval;
You can use PreparedStatement.getGeneratedKeys() to obtain the generated value. There is no need to use a separate statement:
You also can't prefix column names with the table name in list of columns of an INSERT statement.
String insert =
"INSERT INTO IDEA ( I_ID,I_NO,I_APPROVER_NAME_CODE, I_TITLE,I_DESCRIPITION, I_CREATED_DATE,I_STATUS_CODE, "
+ "I_IS_CODE, I_CONTRIBUTION_CODE, I_POSITIVE_IMPACT, I_SECOND_MEMBER_ID,I_THIRD_MEMBER_ID,I_FOURTH_MEMBER_ID,"
+ "I_FIFTH_MEMBER_ID, I_POINTS,I_CREATED_USER_ID)"
+ " VALUES (idea_seq.nextval,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(insertSql, new String[] {"I_ID"});
preparedStatement.setInt(1, ???); // don't know where the value for I_NO comes from
preparedStatement.setString(2, addIdeaDto.getApproverNameCode());
preparedStatement.setString(3, addIdeaDto.getTitle());
... other parameters
preparedStatement.executeUpdate();
ResultSet rs = preparedStatement.getGeneratedKeys();
long newId = -1;
if (rs.next()) {
newId = rs.getLong("I_ID");
}
... use the NewId ...
The parameter new String[] {"I_ID"} for the prepareStatement() call tells the JDBC driver to return the generated value for that column. That value can be retrieved through getGeneratedKeys() which returns a ResultSet that contains one row for each inserted row (so exactly one in this case). The ID value can then be extracted from the ResultSet using the the usual getLong() (or getInt()) methods.

GetgeneratedKeys not working although row inserted

I use HSQLDB - Tried version 2.3.4 and 2.4.
I have this Java code:
String sqlquery = "MERGE INTO bewertung pu USING "
.concat("(VALUES ?,?,?) ")
.concat("temp (tid,jurorid,runde) ")
.concat("ON temp.tid = pu.tid and temp.jurorid=pu.jurorid and temp.runde=pu.runde ")
.concat("WHEN NOT MATCHED THEN ")
.concat("INSERT (tid,jurorid,runde) ")
.concat("VALUES (temp.tid,temp.jurorid,temp.runde)");
PreparedStatement preparedStatement = sql.getConnection().prepareStatement(sqlquery,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setInt(1, eineBewertung.getTanzID()); //TID
preparedStatement.setInt(2, eineBewertung.getJurorID()); //JURORID
preparedStatement.setInt(3, eineBewertung.getRunde()); //RUNDE
int rowsAffected = preparedStatement.executeUpdate();
if (rowsAffected == 0) {
//UPDATE
//DO SOMETHING
}else{
//INSERT
try (ResultSet rs = preparedStatement.getGeneratedKeys()) {
if (rs.next()) {
eineBewertung.setBewertungsid(rs.getInt(1));
}
}catch (SQLException ex) {
this.controller.error_ausgeben(ex);
}
}
It works. If I insert a new row I get rowsAffected = 1. I check the database and the insert worked.
But, I do not get anything back in the resultset getGeneratedKeys()
It is every time empty.
I have found some tips to replace Statement.RETURN_GENERATED_KEYS with the primary key. But this didn`t work for me.
PreparedStatement preparedStatement = sql.getConnection().prepareStatement(sqlquery,
new String[]{"BEWERTUNGSID"});
This is how I create the table:
statement.execute("create table PUBLIC.BEWERTUNG"
.concat("(BEWERTUNGSID INTEGER IDENTITY,")
.concat("TID INTEGER FOREIGN KEY REFERENCES Tanz(Tanzid),")
.concat("JURORID INTEGER not null FOREIGN KEY References JUROR(JURORID),")
.concat("RUNDE INTEGER not null,")
.concat("primary key(BEWERTUNGSID)")
.concat(")"));
Why do I not get any generated keys back? Thank you
//EDIT
If I replace my sqlquery with an insert statement it is working.
sqlquery = "INSERT INTO BEWERTUNG(TID, JURORID, RUNDE) VALUES(22, 2, 2)";
Why is merge not working in the sqlquery?
With versions of HSQLDB up to 2.4.0, generated keys are not available when inserting data using a MERGE statement. Code has been committed to allow this in the next version.

Getting inserted (or existing) ids with executeBatch()

I am trying to insert some words to database and return newly inserted id or existing id if the word is already in the database.
I found that I can do this using PreparedStatement and including Statement.RETURN_GENERATED_KEYS. But PreparedStatement is terribly slow. I need to insert like 5000 words at once. Another way I could achieve it by running individual query in for loop:
public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag) {
ArrayList ids = new ArrayList<Integer>();
ResultSet rs = null;
try{
Statement st = connection.createStatement();
for (String[] articleTerms: allTermsForTag) {
for(String term: articleTerms) {
String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
"RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
rs = st.executeQuery(query);
while (rs.next())
{
int id = rs.getInt(1);
ids.add(id);
System.out.printf("id: "+id);
}
}
}
rs.close();
st.close();
}catch(SQLException e){
System.out.println("SQL exception was raised while performing SELECT: "+e);
}
return ids;
}
This does what I need nicely, but this is too slow as well.
Another method that I wrote uses executeBatch(), however, it does not return ids:
public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag){
ResultSet rs = null;
ArrayList ids = new ArrayList<Integer>();
try{
Statement st = connection.createStatement();
for (String[] articleTerms: allTermsForTag) {
for(String term: articleTerms) {
String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
"RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
st.addBatch(query);
}
st.executeBatch();
rs = st.getGeneratedKeys();
while (rs.next()) {
int id = rs.getInt(1);
ids.add(id);
}
}
st.close();
return ids;
}catch (SQLException e){
System.out.println("SQL exception was raised while performing batch INSERT: "+e.getNextException());
System.out.println("dub");
}
return null;
}
So the question is - how to get ids when using executeBatch() or if this is not possible, how to approach this problem? I need it to work as fast as possible, because there will be a lot of INSERT operations with large amount of data.
Thank you!
Set set = new HashSet();
try {
PreparedStatement ps = cn.prepareStatement("delete from myTable where... ",
Statement.RETURN_GENERATED_KEYS);
ps.setInt(1,200);
ps.setInt(2,262);
ps.setString(3, "108gf99");
ps.addBatch();
ps.setInt(1,200);
ps.setInt(2,250);
ps.setString(3, "hgfha");
ps.addBatch();
ps.executeBatch();
ResultSet rs = ps.getGeneratedKeys();
while (rs.next()){
set.addAll(Collections.singleton(rs.getLong(1)));
}
System.out.println(set);
} catch (SQLException e) {
e.printStackTrace();
}
executeBatch can return generated keys in the latest PgJDBC versions. See issue 195 and pull 204. You must use the prepareStatement variant that takes a String[] of returned column names.
However... take a step back here. The solution isn't loops. The solution is almost never loops.
In this case, you should almost certainly use COPY via the PgJDBC CopyManager API to COPY data into a TEMPORARY table. Then do an INSERT INTO ... SELECT ... RETURNING ... to insert the temp table's contents into the final table and return any generated fields. You can also do a SELECT to join on the temp table to return any that already exist. This is basically a bulk upsert or closely related bulk insert-if-not-exists.
If for some reason you can't do that, the next-best option is probably multi-valued INSERTs with large VALUES lists, but this requires some ugly dynamic SQL. Since you need existing values if the row already exists you'll probably need a writeable CTE too. So really, just use COPY and a query to do the table merge.

Retrieving serial id from batch inserted rows in postgresql

Here is the code that works:
Connection c = ds.getConnection();
c.setAutoCommit(false);
PreparedStatement stmt = c.prepareStatement("INSERT INTO items (name, description) VALUES(?, ?)");
while (!(items = bus.take()).isEmpty()) {
for (Item item : items) {
stmt.setString(1, item.name);
stmt.setString(2, item.description);
stmt.addBatch();
}
stmt.executeBatch();
c.commit();
}
But now I need to populate another table where id is a foreign key.
If I use INSERT with RETURNING id then executeBatch fails with "A result was returned when none was expected" error.
I see several ways to solve this
Do individual insert rather than the batch insert.
Replace serial id with client generated guid.
Use some kind of a stored procedure to perform the batch insert and return a list of ids.
Of the three methods that I see the last one seems to preserve both the efficiency of batch insert and return the ids, but it is also the most complex for me as I have never written stored procedures.
Is there a better way to batch insert and get the IDs? I have no problem using postgresql specific API rather than jdbc.
If not, could any one sketch such a stored procedure?
Here is the table schema:
CREATE UNLOGGED TABLE items
(
id serial,
name character varying(1000),
description character varying(10000)
)
WITH (
OIDS=FALSE
);
Something like this should work:
// tell the driver you want the generated keys
stmt = c.prepareStatement("INSERT ... ", Statement.RETURN_GENERATED_KEYS);
stmt.executeBatch();
// now retrieve the generated keys
ResultSet rs = stmt.getGeneratedKeys();
while (rs.next()) {
int id = rs.getInt(1);
.. save the id somewhere or update the items list
}
I think (I am not sure!) that the keys are returned in the order they were generated. So the first row from the ResultSet should map to the first "item" from the list you are processing. But do verify that!
Edit
If that doesn't work, try specifying the actual columns for which the values are generated:
stmt = c.prepareStatement("INSERT ... ", new String[] {"id"});

Using Oracle sequence to insert log id into 2 tables from jdbc?

I am using oracle sequence for inserting log id into tableA as follows,
String SQL_PREP_INSERT = "INSERT INTO tableA (LOG_ID,USER_ID,EXEC_TIME) VALUES"
+ " (logid_seq.nextval, ?, ?)";
Then getting the recently inserted value,
String SQL_PREP_SEL = "SELECT max(LOG_ID) FROM tableA ";
stmt = con.prepareStatement(SQL_PREP_SEL);
stmt.execute();
ResultSet rs = stmt.getResultSet();
if (rs.next()) {
logid = rs.getInt(1);
}
And inserting it into tableB,
String SQL_PREP_INSERT_DETAIL = "INSERT INTO tableB (LOG_ID, RESPONSE_CODE, RESPONSE_MSG) VALUES"
+ " (?, ?)";
stmt = con.prepareStatement(SQL_PREP_INSERT_DETAIL);
stmt.setInt(1, logid);
stmt.setString(2, respCode);
stmt.setString(3, respMsg);
stmt.execute();
Is there a way to generate sequence in Java instead of Oracle and insert into both tables at once, instead of selecting from tableA and inserting into tableB?
In general, selecting the MAX(log_id) is not going to give you the same value that logid_seq.nextval provided. Assuming that this is a multi-user system, some other user could have inserted another row with a larger log_id value than the row you just inserted before your query is executed.
Assuming that both INSERT statements are run in the same session, the simplest option is probably to use the logid_seq.currval in the second INSERT statement. currval will return the last value of the sequence that was returned to the current session so it will always return the same value that was generated by the nextval call in the first statement.
INSERT INTO tableB (LOG_ID, RESPONSE_CODE, RESPONSE_MSG)
VALUES( logid_seq.currval, ?, ? )
Alternatively, you could use the RETURNING clause in your first statement to fetch the sequence value into a local variable and use that in the second INSERT statement. But that is probably more work than simply using the currval.
String QUERY = "INSERT INTO students "+
" VALUES (student_seq.NEXTVAL,"+
" 'Harry', 'harry#hogwarts.edu', '31-July-1980')";
// load oracle driver
Class.forName("oracle.jdbc.driver.OracleDriver");
// get database connection from connection string
Connection connection = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:sample", "scott", "tiger");
// prepare statement to execute insert query
// note the 2nd argument passed to prepareStatement() method
// pass name of primary key column, in this case student_id is
// generated from sequence
PreparedStatement ps = connection.prepareStatement(QUERY,
new String[] { "student_id" });
// local variable to hold auto generated student id
Long studentId = null;
// execute the insert statement, if success get the primary key value
if (ps.executeUpdate() > 0) {
// getGeneratedKeys() returns result set of keys that were auto
// generated
// in our case student_id column
ResultSet generatedKeys = ps.getGeneratedKeys();
// if resultset has data, get the primary key value
// of last inserted record
if (null != generatedKeys && generatedKeys.next()) {
// voila! we got student id which was generated from sequence
studentId = generatedKeys.getLong(1);
}
}

Categories