Fetch primary key generated by sequence using jdbctemplate / jdbc - java

The situation is as follows:
I have one table(GUI_FILTER) and one sequence(GUI_FILTER_SEQ)
I am inserting data using springjdbctemplate, also can use core jdbc as well.
In the insert query I am retrieving sequence.nextval and also using a keyholder to fetch that back to java as below.
I have tried several ways like using
returnRowCount = namedJdbctmplt.update(sql.toString(), namedParameters, keyHolder, keyColumnNames);
returnRowCount = jdbctemplate.update(psc, keyHolder);
both are failing,
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: ORA-00936: missing expression
StringBuilder sql = new StringBuilder();
sql.append(" Insert Into MAPS_AMPS.AMPS_GUI_FILTER ");
sql.append(" (FILTER_PRESET_ID,USER_ID,FILTER_PAGE_TXT,FILTER_DISPLY_NM,FILTER_DATA_TXT,ROW_CREATE_TMS,LAST_UPDT_TMS ) ");
sql.append(" values(Select MAPS_AMPS.AMPS_GUI_FILTER_SEQ.NEXTVAL,?,?,?,?,CURRENT_TIMESTAMP ,null from dual) ");
final String sqlQuery = sql.toString();
If I remove the sequence generation part Select MAPS_AMPS.AMPS_GUI_FILTER_SEQ.NEXTVAL to something constant like 30 or so it is working fine.
Is there any way to get sequence value as insert command and also at the same time fetch the primary key using jdbc ?
Complete code is as below.
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = DataAccessException.class)
public Long addFilterPreset(final String userId, final String filterPageCd,
final String filterPresetDisplayName,
final String serializedTaskBrowserSearchObj) {
logger.debug("Start of FilterPresetDaoImpl - addFilterPreset() method");
if(!countSavedFilterPresets(userId,filterPageCd))
return null;
//StringBuilder sql = new StringBuilder();
Map<String, Object> parameterMap = new HashMap<String, Object>();
//sql.append(" Insert Into MAPS_AMPS.AMPS_GUI_FILTER ");
//sql.append(" (FILTER_PRESET_ID,USER_ID,FILTER_PAGE_TXT,FILTER_DISPLY_NM,FILTER_DATA_TXT,ROW_CREATE_TMS,LAST_UPDT_TMS ) ");
//sql.append( "Values( Select MAPS_AMPS.AMPS_GUI_FILTER_SEQ.NEXTVAL,:userId,:filterPageCd,:filterPresetDisplayName,:filterPresetData,CURRENT_TIMESTAMP ,null)" );
/*parameterMap.put("userId", userId);
parameterMap.put("filterPageCd", filterPageCd);
parameterMap.put("filterPresetDisplayName", filterPresetDisplayName);
parameterMap.put("filterPresetData", serializedTaskBrowserSearchObj);
*/
StringBuilder sql = new StringBuilder();
sql.append(" Insert Into MAPS_AMPS.AMPS_GUI_FILTER ");
sql.append(" (FILTER_PRESET_ID,USER_ID,FILTER_PAGE_TXT,FILTER_DISPLY_NM,FILTER_DATA_TXT,ROW_CREATE_TMS,LAST_UPDT_TMS ) ");
sql.append(" values(Select MAPS_AMPS.AMPS_GUI_FILTER_SEQ.NEXTVAL,?,?,?,?,CURRENT_TIMESTAMP ,null from dual) ");
final String sqlQuery = sql.toString();
logger.info("SQL Add Filter Preset Query : {}", sqlQuery);
int returnRowCount = 0;
KeyHolder keyHolder = new GeneratedKeyHolder();
final String[] keyColumnNames = {"FILTER_PRESET_ID"};
SqlParameterSource namedParameters = new MapSqlParameterSource(parameterMap);
PreparedStatementCreator psc = new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
PreparedStatement ps = connection.prepareStatement(sqlQuery, keyColumnNames);
ps.setString(1, userId);
ps.setString(2, filterPageCd);
ps.setString(3, filterPresetDisplayName);
ps.setString(4, serializedTaskBrowserSearchObj);
return ps;
}
};
try{
long startValue = System.currentTimeMillis();
//returnRowCount = namedJdbctmplt.update(sql.toString(), namedParameters, keyHolder, keyColumnNames);
returnRowCount = jdbctemplate.update(psc, keyHolder);
long endValue = System.currentTimeMillis();
logger.info("Filter Preset added in {} seconds", (endValue - startValue)/1000);
}catch(Exception e){
logger.info("Exception while adding filter preset - " + "userId : "+userId+" filterPageCd : "+filterPageCd +" filterPresetData : "+serializedTaskBrowserSearchObj);
}finally{
logger.debug("End of FilterPresetDaoImpl - addFilterPreset() method");
}
if (returnRowCount > 0)
return keyHolder.getKey().longValue();
return null;
}
Error

You don't need the VALUES keyword when inserting a result from query.
StringBuilder sql = new StringBuilder();
sql.append(" Insert Into MAPS_AMPS.AMPS_GUI_FILTER ");
sql.append(" (FILTER_PRESET_ID,USER_ID,FILTER_PAGE_TXT,FILTER_DISPLY_NM,FILTER_DATA_TXT,ROW_CREATE_TMS,LAST_UPDT_TMS ) ");
sql.append(" (Select MAPS_AMPS.AMPS_GUI_FILTER_SEQ.NEXTVAL,?,?,?,?,CURRENT_TIMESTAMP ,null from dual) ");
final String sqlQuery = sql.toString();
To get an inserted value try using RETURNING clause: some details are discussed in this post Oracle's RETURNING INTO usage in Java (JDBC, Prepared Statement)
However, you can use RETURNING clause for INSERT only if you use the VALUES keyword, so you can't have both. As it's discussed here: PLSQL Insert into with subquery and returning clause (Oracle)

Related

Varying content of an SQL update statement in java

I need to write an update function where its content is different based on what parameters are passed, e.g. if I have updateBook(int id, String title, String author, int pages), I have to do something like:
String sql;
if((!title.equals("null"))&&(!author.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', author='"+author+"', pages="+pages;
else if(((!title.equals("null"))&&(!author.equals("null")))
sql = "UPDATE book SET title='"+title+"', author='"+author+"'";
else if(((!title.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', pages="+pages;
... //and so on
sql = sql + " WHERE bookid="+id+";";
The more fields I have in my table, the more checks I have to do, which is uncomfortable, and requires me to write a lot of code.
Also, doing something like:
sql = "UPDATE book SET ";
if(!title.equals("null"))
sql = sql +"title='"+title+"',";
if(!author.equals("null"))
sql = sql+"author='"+author+"',";
if(pages>0)
sql = sql+"pages="+pages";
sql = sql + ";";
can't work since the unwanted commas cause statement errors.
You can see as well that if I have something like 6, 7, 8 etc field the checks start to get too many, and I can't also do more separated update statements as if something goes wrong I would need to rollback any query that has been done in that function.
Is there any way round to get a custom update statement having to write few code?
Firstly, use a PreparedStatement.
I would do it something like the following.
List<Object> params = new ArrayList<>();
StringBuilder sql = new StringBuilder();
if(!title.equals("null")) {
sql.append("title = ?");
params.add(title);
}
if(!author.equals("null")) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("author = ?");
params.add(author);
}
if(pages>0) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("pages = ?");
params.add(pages);
}
if (sql.length() > 0) {
sql.insert(0, "UPDATE book SET ");
sql.append(" WHERE bookid=?");
java.sql.Connection conn = // however you obtain it
java.sql.PreparedStatement ps = conn.prepareStatement(sql.toString());
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
ps.executeUpdate();
}

Batch Execute as Aysnc Does not update any row

I am trying to use aync update of dsc cassandra 3,
Integer count = 0;
String query = "select status, guid from catalog_new where affiliate_id = ? AND store_id =?";
String approveStoreQuery = "UPDATE catalog_new SET status = ? WHERE affiliate_id = ? AND store_id = ? AND guid = ?";
PreparedStatement selectStmt = session.prepare(query);
BoundStatement selectBoundStatement = new BoundStatement(selectStmt);
ResultSet selectSet = session.execute(selectBoundStatement.bind(new Object[]{affiliateId, storeId}));
BatchStatement batchStatement = new BatchStatement(BatchStatement.Type.UNLOGGED);
Iterator<Row> rowItr = selectSet.iterator();
while (!selectSet.isFullyFetched()) {
selectSet.fetchMoreResults();
Row row = rowItr.next();
if(row.getInt("status") == statusFrom){
String guid = row.getString("guid");
PreparedStatement preparedStatement = session.prepare(approveStoreQuery);
BoundStatement boundStatement = new BoundStatement(preparedStatement);
batchStatement.add(boundStatement.bind(new Object[]{statusTo, affiliateId, storeId, guid}));
count++;
}
}
session.executeAsync(batchStatement);
return count;
{Here statusFrom is -2 and statusto is -2 , ids are 3 and 9}
This does not update any row, what I am doing wrong here?
I found it:
Just replace the while construct with a do while and you will be fine!
I also tested this code against select fetch statement that has multiple pages, so it should work as expected for you:
import com.datastax.driver.core.*;
import java.util.Iterator;
public class Hello {
public static void main(String[] args) {
Cluster cluster = Cluster.builder()
.addContactPoint("127.0.0.2")
.build();
//I tried to reverse engineer this from your code:
//I think it's relatively close to what you got
/*
CREATE TABLE catalog_new (
affiliate_id text,
store_id text,
guid text,
status int,
PRIMARY KEY(affiliate_id, store_id, guid)
);
-- Just some test data
INSERT INTO catalog_new(affiliate_id, store_id, guid, status) VALUES ('af1', 'st1', 'guid1', 0);
INSERT INTO catalog_new(affiliate_id, store_id, guid, status) VALUES ('af1', 'st1', 'guid2', 0);
INSERT INTO catalog_new(affiliate_id, store_id, guid, status) VALUES ('af1', 'st1', 'guid3', 0);
*/
Session session = cluster.connect();
String affiliateId = "af1";
String storeId = "st1";
Integer statusFrom = 0;
Integer statusTo = 1;
Integer count = 0;
String query = "select status, guid from test.catalog_new where affiliate_id = ? AND store_id =?";
String approveStoreQuery = "UPDATE test.catalog_new SET status = ? WHERE affiliate_id = ? AND store_id = ? AND guid = ?";
PreparedStatement selectStmt = session.prepare(query);
BoundStatement selectBoundStatement = new BoundStatement(selectStmt);
ResultSet selectSet = session.execute(selectBoundStatement.bind(new Object[]{affiliateId, storeId}));
Iterator<Row> rowItr = selectSet.iterator();
BatchStatement batchStatement = new BatchStatement(BatchStatement.Type.UNLOGGED);
// the way you wrote it is
// while (!selectSet.isFullyFetched()) {
// basically you never even go into a loop
// you might try a do while! - that's all there is to it
do {
selectSet.fetchMoreResults();
Row row = rowItr.next();
if (row.getInt("status") == statusFrom) {
String guid = row.getString("guid");
PreparedStatement preparedStatement = session.prepare(approveStoreQuery);
BoundStatement boundStatement = new BoundStatement(preparedStatement);
batchStatement.add(boundStatement.bind(statusTo, affiliateId, storeId, guid));
count++;
}
} while (!selectSet.isFullyFetched());
session.executeAsync(batchStatement);
// I just made simple print without returning anything just to make sure this works, I tried your example locally and everything runs fine
System.out.println(count);
}
}

Java - Sql query with Alias

I want to retrieve a particular column from the database. For a simple Select statement, I can able to able to retrieve a column like below
public String getDbColumnValue(String tableName, String columnName, String applicationNumber) {
String columnValue = null;
try {
PreparedStatement ps = null;
String query = "SELECT " + columnName + " FROM " + tableName +
" WHERE ApplicationNumber = ?;";
ps = conn.prepareStatement(query);
ps.setString(1, applicationNumber);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
columnValue = rs.getString(columnName);
return columnValue;
}
}
catch (Exception ex) {
}
return columnValue;
}
But, I'm using alias in my query like below. And this query works fine. How to use this in Java to retrieve a particular column
select S.StatusDesc from application A, StatusMaster S
where A.StatusMasterId = S.StatusMasterId and A.ApplicationNumber = '100041702404'
Any help would be greatly appreciated!
I think you are confusing simple aliases, which are used for table names, with the aliases used for column names. To solve your problem, you can just alias each column you want to select with a unique name, i.e. use this query:
select S.StatusDesc as sc
from application A
inner join StatusMaster S
on A.StatusMasterId = S.StatusMasterId and
A.ApplicationNumber = '100041702404'
Then use the following code and look for your aliased column sc in the result set.
PreparedStatement ps = null;
String query = "select S.StatusDesc as sc ";
query += "from application A ";
query += "inner join StatusMaster S ";
query += "on A.StatusMasterId = S.StatusMasterId ";
query += "and A.ApplicationNumber = ?";
ps = conn.prepareStatement(query);
ps.setString(1, applicationNumber);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
columnValue = rs.getString("sc");
return columnValue;
}
Note: I refactored your query to use an explicit inner join instead of joining using the where clause. This is usually considered the better way to write a query.

How do you use a WHERE clause to filter out duplicate data, Without hard coding?

I have been sitting with this for a good few days, searching online high and low, but have not found a proper answer
My problem description, I need to filter duplicate data from an incoming .csv file, do a check to see if that data is already in the table if not then persist data, else do not insert data. My method, I read in a unique .csv file line by line, I use a preparedStatement that is suppose to use the WHERE clause to do the check for the duplicates.
The part I am having a problem with is, how to use the WHERE clause in my preparedStatement.
Below is some o my code to clear up any confusion.
PS: this is what the ,message_id looks like <329577443.1427052727657.JavaMail.javamailuser#localhost> there are thousands of them.
try {
dataRow = reader.readLine();
while ((dataRow = reader.readLine()) != null) {
totalRecordsProcessed++;
dataRow = dataRow.replace("\"", "");
dataRow = dataRow.replace("<", "");
dataRow = dataRow.replace(">", "");
dataItems = dataRow.split(",", -1);
ExchangeReport exchangeReport = new ExchangeReport(dataItems);
//System.out.println(exchangeReport.toString());
persistData(exchangeReport);
}
}
//Do a check in the insert method to see if we are persisting duplicate data
public void persistData(ExchangeReport exchangeReport) throws SQLException{
Connection connection = super.getEnvironment().getConnection();
PreparedStatement preparedStatement = null;
String insertScript = null;
String skipScript = null;
//ResultSet incomeSet;
ArrayList<String> rowVals = new ArrayList();
ArrayList<String> dbList = new ArrayList<String>();
ArrayList<String> incomeList = new ArrayList<String>();
String tempVal = null;
skipScript = "select unique_ref, message_id, event_id from InboundCorres " +
"where message_id = " +
"";
preparedStatement = connection.prepareStatement(skipScript);
ResultSet dbSet = preparedStatement.executeQuery();
while (dbSet.next()) {
rowVals.add(dbSet.getString(1).trim());
rowVals.add(dbSet.getString(2).trim());
rowVals.add(dbSet.getString(3).trim());
System.out.println(rowVals.toString());
}
//List<String> incomeList = Arrays.asList(dataItems);
insertScript = "INSERT INTO INBOUNDCORRES(EVENT_ID, SOURCE_INBOUND, TIME_STAMP," +
"RECIPIENTS, MESSAGE_SUBJECT, SENDER, MESSAGE_ID," +
"CONNECTOR_ID, UNIQUE_REF)" +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)";
preparedStatement = connection.prepareStatement(insertScript);
preparedStatement.setString(1, exchangeReport.getEventId() );
preparedStatement.setString(2, exchangeReport.getSourceInbound() );
preparedStatement.setString(3, exchangeReport.getTimeStamp() );
preparedStatement.setString(4, exchangeReport.getRecipient());
preparedStatement.setString(5, exchangeReport.getMessageSubject());
preparedStatement.setString(6, exchangeReport.getSender());
preparedStatement.setString(7, exchangeReport.getMessageId());
preparedStatement.setString(8, exchangeReport.getConnectorId());
preparedStatement.setString(9, exchangeReport.getUniqueRef());
preparedStatement.executeUpdate();
}
The SQL statement in skipScript is incomplete.
I would expect to see something like:
skipScript = "select unique_ref, message_id, event_id from InboundCorres " +
"where message_id = :msg_id";
So message_id is the column name, and msg_id is a local String variable to hold the value we are looking for.
To get the value in msg_id, we could call a method called saveMessage() which would look something like:
public void saveMessage (final String messageId) {
Map<String, Object> values = new HashMap<String, Object>();
values.put("msg_id", messageId);
jdbcTemplate.update(skipScript , values);
}
Note that I have deliberately used three different names for message_id to make it clear what is going on.
Hope this helps. I realise my example used jdbcTemplate.update() which isn't like the code example, but the principle is similar.
Thank you user4810988, you helped me towards a solution.
skipScript = "select TIME_STAMP " +
" from InboundCorres where unique_ref = ? " +
"and message_id = ? " +
"and event_id = ?";
preparedStatement = connection.prepareStatement(skipScript);
preparedStatement.setString(1, exchangeReport.getUniqueRef());
preparedStatement.setString(2, exchangeReport.getMessageId());
preparedStatement.setString(3, exchangeReport.getEventId());
ResultSet dbSet = preparedStatement.executeQuery();
while (dbSet.next()) {
rowVals.add(dbSet.getString(1).trim());
rowVals.add(dbSet.getString(2).trim());
rowVals.add(dbSet.getString(3).trim());
System.out.println("Duplicates found ==>" + rowVals.toString());
}
That is the solution that I was looking towards.

Mysql select and update column in one query

Is possible to merge my following queries into one? I'm using mysql as my database.
String qry = "SELECT id FROM customers WHERE completed=false AND server=?";
ps = connection.prepareStatement(qry);
ps.setString(1,getServerId());
rs = ps.executeQuery();
final Set<Long> ids = new HashSet<>();
while (rs.next()) {
ids.add(rs.getLong(1));
}
qry = "";
for (long l : ids) {
qry += "UPDATE customers SET completed=true WHERE id = "+l+"; ";
}
... execute query, close streams, and do some application logic with ids from database...
You can just
UPDATE customers SET completed=true WHERE completed=false AND server=?
Edit: You provided the information saying you need to actually use the Ids.
Select normally then build a string in the format MySql understands as a "list", it will certainly be faster than multiple update queries and will cost less bytes to send over the network. Here is the snippet:
String qry = "SELECT id FROM customers WHERE completed=false AND server=?";
ps = connection.prepareStatement(qry);
ps.setString(1,getServerId());
rs = ps.executeQuery();
final Set<Long> ids = new HashSet<>();
while (rs.next()) {
ids.add(rs.getLong(1));
}
qry = "";
if(ids.size()>0) {
StringBuilder idsAsString = new StringBuilder();
for(int i = 0; i < ids.size(); i++) {
idsAsString.append(ids.get(i));
if(i < ids.size()-1)
idsAsString.append(",");
}
qry = "UPDATE customers SET completed=true WHERE id IN ("+idsAsString+"); ";
}
instead of building multiple UPDATE statements in a for loop.

Categories