I have following question.
I'm using JDBC in my project and I made simple method to insert data into my database.
My problem is: What to do when I want to insert something like sysdate or just NULL to auto increment? To my method I send only strings and writing NULL to string doesn't work.
Can you give me any advice how to improve it?
This is the code with constant null in query, but it isn't what I want to.
public static void insertInto(String Table, ArrayList<String> values) throws SQLException
{
Connection conn = JavaConnectDB.ConnectDb();
OraclePreparedStatement pst = null;
StringBuilder Query = new StringBuilder("INSERT INTO " + Table + " VALUES (NULL, ");
for (int i = 0; i < values.size(); i++)
{
Query.append("? ");
if (i + 1 != values.size())
Query.append(", ");
}
Query.append(")");
pst = (OraclePreparedStatement) conn.prepareStatement(Query.toString());
for (int i = 0; i < values.size(); i++)
{
pst.setString(i + 1, values.get(i));
}
pst.executeUpdate();
}
This method creates query like "INSERT INTO TABLE VALUES (NULL, ?, ? ,?)" and then fills gaps with values from array.
There is java.sql.PreparedStatement.setNull(int, int)
Try, e.g.
pst.setNull(1, Types.BIGINT);
for (int i = 1; i < values.size(); i++)
{
pst.setString(i + 1, values.get(i));
}
Change Types.BIGINT for apporpriate type for your column.
Note, that values.get(0) is just ignored, but should present in the array.
Problem is the setString function. If you have a string "NULL" or "SYSDATE", it will result in the query being quoted ('NULL', 'SYSDATE'), so this will be inserted as string.
According to this answer, pst.setString(n, null) should do the trick already, so inserting SQL NULL values is yet relatively easy, just insert a Java null value into the array where you want the database value to be SQL NULL.
SYSDATE gets more delicate. But I think, here comes something more fundamental into play: how are you going to handle data types other than VARCHAR (see setString documentation). If you really wanted such a generic method, I would rather to pass ArrayList<Object> as parameter (or with ellipsis ...) and call the appropriate setXXX method for the specific Object type - or setObject with appropriate Type parameter set. You could then create your own class SysDate which could easily be detected.
Is the database layout known to your application? Then I'd rather recommend to have a separate insert method for each table accepting exactly the number of required parameters of correct type, such as
bool insertIntoTest(int someValue, Integer anotherValue, String andAnotherOne)
{
Connection conn = JavaConnectDB.ConnectDb();
OraclePreparedStatement pst
= (OraclePreparedStatement) conn.prepareStatement(
"INSERT INTO TEST (someValue, anotherValue) VALUES(?, ?)"
);
pst.setInt(1, someValue);
if(anotherValue == 0)
pst.setNull(2, Types.INTEGER);
else
pst.setInt(2, anotherValue);
// can handle null already...
pst.setString(3, andAnotherOne);
/* ... (execute, try/catch, return) */
}
Well you should define exactly what do ou want to do.
It's not possible to put null in an autoinkrement field on database by definition
Auto-increment allows a unique number to be generated when a new record is inserted into a table.
So if you want just to insert some filed to your table and delegate the genration of autoincrement to your database, you should create your query like that :
INSERT INTO TABLE VALUES (?, ? ,?).
Example :
Table employee(id,time,name)
Query :
INSERT INTO employee (time, name) VALUES (?, ?)
Related
Am I missing something obvious here?
I have the following method to execute queries against an SQLite local database using org.xerial:sqlite-jdbc:3.21.0.
public List<HashMap<String, Object>> executeQuery(String sql, List<Object> vals) throws Exception {
List<HashMap<String, Object>> rows = new ArrayList<>();
Connection conn = getConnection();
try (PreparedStatement stmnt = conn.prepareStatement(sql)) {
if (!vals.isEmpty()) {
for (int i = 0; i < vals.size(); i++) {
stmnt.setObject(i + 1, vals.get(i));
}
}
ResultSet rs = stmnt.executeQuery();
ResultSetMetaData meta = rs.getMetaData();
HashMap<String, Object> row;
while (rs.next()) {
row = new HashMap<>();
for (int i = 0; i < meta.getColumnCount(); i++) {
row.put(meta.getColumnName(i + 1), rs.getObject(i + 1));
}
rows.add(row);
}
} finally {
putConnection(conn);
}
return rows;
}
However, when I pass the following SQL into the method along with the following values, they don't get set (but it also doesn't throw an exception). It's like it internally assigns it but forgets to tell the database.
SELECT * FROM 'airlines' WHERE 'name' LIKE ? LIMIT 1
vals: size = 1 {"MyAirline"}
I can see from debugging that it gets inside the loop to setObject.
In ANSI standard SQL, single quotes (') are used to delimit literal strings and double quotes (") are used to delimit table/column names. So
SELECT * FROM 'airlines' WHERE 'name' LIKE ? LIMIT 1
really means "select all columns from the literal string 'airlines' where the literal string 'name' matches the pattern supplied by the parameter".
Interestingly, SQLite seems to be clever enough to interpret the literal string 'airlines' as the table name "airlines" but it is still interpreting 'name' as a literal string. Therefore, for every row in "airlines" it is comparing the literal string 'name' to the string value 'MyAirline' and it never matches, so the ResultSet contains no rows.
Your SQL command text should be
SELECT * FROM "airlines" WHERE "name" LIKE ? LIMIT 1
so SQLite will compare the contents of the "name" column with the value 'MyAirline'.
I have a query in which I am using Cursors in Select clause along with some other columns Values.I wanted to iterate through its result via ResultSet in Java.But couldn't find a way to get the Cusror from the result set.Is it possible to do so?Can anyone help me?
For eg
select name, roll_no,
cursor (select subj1
from Subject
where id = 'abc'
) as cusr1
from student
Here would be a generic way to read columns from a ResultSet.
while (resultSet.next()) { //Read every row
int columnCount = resultSet.getMetaData().getColumnCount();
for (int column = 1; column <= columnCount; column++) { //Read every column
String columnName = resultSet.getMetaData().getColumnName(column);
Object value = resultSet.getObject(columnName);
if (value != null) {
doSomething(columnName, value);
}
}
}
I assume you are using an Oracle DB.
I also assume Java 7 or higher (try with resources).
You are using a Cursor Expression in your SQL.
The Oracle JDBC Driver returns a java.sql.Resultset object when you call Resultset.getObject(column number|column label) for a Cursor Expression column.
So it is safe to cast:
try (Resultset innerResultset = (Resultset) outerResultset.getObject(column number/label)) {
while(innerResultset.next()) {
...
}
One comment: In many cases, for example when creating "master-detail" queries, you use a correlated Cursor Expression, meaning there is a join on a table that appears in the outer query.
Please take a look at my code snippet below. What I am trying to achieve is querying the database for any results equal to the user input. It is querying SDS_NUMBER column which is an integer column.
When I execute the query it returns the following exception:
java.sql.SQLSyntaxErrorException: Comparisons between 'INTEGER' and
'CHAR (UCS_BASIC)' are not supported. Types must be comparable.
String types must also have matching collation....etc.
I understand that it is saying I am trying to compare an integer to char but I have tried to cast from examples I found on the net but no luck. Also I tried parseInt and using that value in the search but I still can't get it to work. Please advise what I am doing wrong and excuse my newbies to all of this.
} else if (tmp == "sdsNumber") {
try {
Integer sdsNum = Integer.parseInt(val);
String sql = "SELECT SDS_NUMBER, PRODUCT_NAME, PROPER_SHIPPING_NAME FROM APP.MASTER WHERE SDS_NUMBER = '"+sdsNum+"'";
pst = conn.prepareStatement(sql);
pst.executeQuery();
jTable1.setModel(DbUtils.resultSetToTableModel(rs));
} catch (Exception e) {
e.printStackTrace(System.out);
JOptionPane.showMessageDialog(null, e);
}
Master table creation:
CREATE TABLE MASTER
(
id integer NOT NULL GENERATED ALWAYS AS (START WITH 1, INCREMENT BY 1)
, SDS_NUMBER integer UNIQUE
, PRODUCT_NAME varchar(100)
, PRODUCT_DESCRIPTION varchar(500)
, SDS_FILE_PATH varchar(50)
, USE_STATUS BOOLEAN
, DATA_UPDATED date
, PROPER_SHIPPING_NAME varchar(100)
, SIGNAL_WORD varchar(20)
, CONSTRAINT MASTER_PRIMARY_KEY PRIMARY KEY (id)
);
it seems you're wrapping an integer in quotes
SDS_NUMBER = '"+sdsNum+"'"
probably what you want is
SDS_NUMBER = ?
and then set the number with your PreparedStatement instead of concatenating strings
see http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html
and
http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html#setInt%28int,%20int%29
Remove the single quote "'"+sdsNum+"'" to be a number
Her ara Example by using preparedStatement
String sql = "SELECT SDS_NUMBER, PRODUCT_NAME, PROPER_SHIPPING_NAME FROM APP.MASTER WHERE SDS_NUMBER = ?";
pst = conn.prepareStatement(sql);
pst.setInt(1, sdsNum);
ResultSet rs = pst.executeQuery(sql);
while (rs.next()) {
int SDS_NUMBER = rs.getInt("SDS_NUMBER");
String PRODUCT_NAME= rs.getString("PRODUCT_NAME");
....
}
} else if ("sdsNumber".equals(tmp)) {
after I changed the first line to this everything works. I dont fully understand what difference that made and if someone could shine some light on it, I would appreciate it as I am slowly learning this.
How can I use a prepared statement to delete entries from a database? I have found that I must write the following code
String deleteSQL = "DELETE DBUSER WHERE USER_ID = ?
but I want to specify a clause with more than one variable. I have used the AND operator but it doesn't seem to work.
Here is an example if your syntax is not correct..
DELETE DBUSER WHERE USER_ID = ? and USER_NAME = ?;
you can append more conditions in where clause by using more AND ... operators.
OR if you have more than one USER_IDs to delete in a single query..
DELETE DBUSER WHERE USER_ID in (?, ?, ?, ?);
It's must work/ for example
Select from Employee e where e.ID < ? and e.ID >= ? order by e.ID
to set values use this:
int id1 = 1;
int id2 = 10;
preparedStatement.setInt(2, id1);
preparedStatement.setInt(1, id2);
for delete I use this code:
public synchronized boolean deleteNewsById(Integer[] idList)
throws NewsManagerException {
DatabaseConnection connection = pool.getConnection();
StringBuffer buffer = new StringBuffer();
buffer.append("(");
buffer.append(idList[0]);
for (int i = 1; i < idList.length; i++) {
buffer.append(",");
buffer.append(idList[i]);
}
buffer.append(")");
PreparedStatement statement = connection
.getPreparedStatement(DELETE_NEWS_BY_ID + buffer);
}
and sql query looks like this
private static final String DELETE_NEWS_BY_ID = "delete from NEWS where ID in ";
or simple write delete from NEWS where ID in (?,?,?) and set values like in first example
I think the response from Aleksei Bulgak is correct, but to perhaps more straightforwardly word it...you can set your parameters like this:
String stmt = "DELETE DBUSER WHERE USER_ID = ? and (USER_NAME = ? or USER_NAME = ?)";
preparedStatement.setInt(1, firstParam);
preparedStatement.setString(2, secondParam);
preparedStatement.setString(3, thirdParam);
...and for however many parameters(question marks) in your SQL (no matter if you're using IN or whatever you want), you should set that many parameters here(using setInt for ints, setString for Strings, etc). This goes for select and delete queries.
Are you looking for the IN operator which allows you to specify multiple values in the WHERE clause such as in my example.
String deleteSQL = "DELETE DBUSER WHERE USER_ID IN (?)"
Though in PreparedStatement IN clause alternatives there are some useful answers and links that you may want to take a look at such as Batch Statements in JDBC which discuss the pros and cons of different batching approaches. The IN approach I'm suggesting is part of that discussion. The end result is that you make just one trip to the database, rather than one per delete and that's better performing because of the reduced network activity required.
This question already has answers here:
How to get a value from the last inserted row? [duplicate]
(14 answers)
Closed 9 years ago.
Is there some way to get a value from the last inserted row?
I am inserting a row where the PK will automatically increase due to sequence created, and I would like to get this sequence number. Only the PK is guaranteed to be unique in the table.
I am using Java with a JDBC and Oracle.
I forgot to add that I would like to retrieve this value using the resultset below. (I have tried this with mysql and it worked successfully, but I had to switch over to Oracle and now I get a string representation of the ID and not the actually sequence number)
Statement stmt = conn.createStatement();
stmt.executeUpdate(insertCmd, Statement.RETURN_GENERATED_KEYS);
stmt.RETURN_GENERATED_KEYS;
ResultSet rs = stmt.getGeneratedKeys();
if(rs.next()){
log.info("Successful insert");
id = rs.getString(1);
}
The above snippet would return the column int value stored in a mysql table. But since I have switched over to Oracle, the value returned is now a strange string value.
What you're trying to do is take advantage of the RETURNING clause. Let's setup an example table and sequence:
CREATE TABLE "TEST"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(100 CHAR) NOT NULL ENABLE,
CONSTRAINT "PK_TEST" PRIMARY KEY ("ID")
);
CREATE SEQUENCE SEQ_TEST;
Now, your Java code should look like this:
String insertSql = "BEGIN INSERT INTO TEST (ID, NAME) VALUES (SEQ_TEST.NEXTVAL(), ?) RETURNING ID INTO ?; END;";
java.sql.CallableStatement stmt = conn.prepareCall(insertSql);
stmt.setString(1, "John Smith");
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
stmt.execute();
int id = stmt.getInt(2);
This is not consistent with other databases but, when using Oracle, getGeneratedKeys() returns the ROWID for the inserted row when using Statement.RETURN_GENERATEDKEYS. So you need to use the oracle.sql.ROWID proprietary type to "read" it:
Statement stmt = connection.createStatement();
stmt.executeUpdate(insertCmd, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
oracle.sql.ROWID rid = (oracle.sql.ROWID) rs.getObject(1);
But this won't give you the generated ID of the PK. When working with Oracle, you should either use the method executeUpdate(String sql, int[] columnIndexes) or executeUpdate(String sql, String[] columnNames) instead of executeUpdate(String sql, int autoGeneratedKeys) to get the generated sequence value. Something like this (adapt the value to match the index or the name of your primary key column):
stmt.executeUpdate(INSERT_SQL, new int[] {1});
ResultSet rs = stmt.getGeneratedKeys();
Or
stmt.executeUpdate(INSERT_SQL, new String[] {"ID"});
ResultSet rs = stmt.getGeneratedKeys();
While digging a bit more on this, it appears that this approach is shown in the Spring documentation (as mentioned here) so, well, I guess it can't be totally wrong. But, unfortunately, it is not really portable and it may not work on other platforms.
You should use ResultSet#getLong() instead. If in vain, try ResultSet#getRowId() and eventually cast it to oracle.sql.ROWID. If the returned hex string is actually the ID in hexadecimal flavor, then you can try converting it to decimal by Long#valueOf() or Integer#valueOf().
Long id = Long.valueOf(hexId, 16);
That said, Oracle's JDBC driver didn't support ResultSet#getGeneratedKeys() for a long time and is still somewhat troublesome with it. If you can't get that right, then you need to execute a SELECT CURRVAL(sequencename) on the same statement as you did the insert, or a new statement inside the same transaction, if it was a PreparedStatement. Basic example:
public void create(User user) throws SQLException {
Connection connection = null;
PreparedStatement preparedStatement = null;
Statement statement = null;
ResultSet generatedKeys = null;
try {
connection = daoFactory.getConnection();
preparedStatement = connection.prepareStatement(SQL_INSERT);
preparedStatement.setValue(1, user.getName());
// Set more values here.
int affectedRows = preparedStatement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
statement = connection.createStatement();
generatedKeys = statement.executeQuery(SQL_CURRVAL);
if (generatedKeys.next()) {
user.setId(generatedKeys.getLong(1));
} else {
throw new SQLException("Creating user failed, no generated key obtained.");
}
} finally {
close(generatedKeys);
close(statement);
close(preparedStatement);
close(connection);
}
}
Oh, from your code example, the following line
stmt.RETURN_GENERATED_KEYS;
is entirely superfluous. Remove it.
You can find here another example which I posted before about getting the generated keys, it uses the normal getGeneratedKeys() approach.