Related
Lets say I have one column with datatype of VARCHAR(500) in db2 zOS table and I wanted to insert data into that column using prepared statement. But that data/string has null character in between. e.g: "hello\0test\0example!".
My question is does setString(index, str) will change those null characters with space or any other character ? or will get it inserted as it is ?
I tried inserting data but not sure. I found change in hex values of original string and inserted string.
'\0' in Java is considered null. Answering your question, your string will be inserted as-is. It will have null inside.
It will then be dependent on your DBMS in your case 'db2' that how it will handle null inside string.
I have tested it using MySQL . I am getting the same data which is inserted. Additionally I have verified it using char instead of String and by printing their ascii values which is just an additional check.
I have used this code for printing every character value:
private static void printStringAndAscii(String str)
{
System.out.println("Incoming String:" + str);
System.out.println("Char with ASCII Values:");
for (char c : str.toCharArray())
{
System.out.println(((int) c) + "-" + c);
}
}
To test it with database I have first printed your given string and save in database, then extracted it and got it printed (deletion is for my own testing). I have tested following code with MySQL utf8 default colation :
Class.forName("com.mysql.jdbc.Driver").newInstance();
String originalString = "hello\0test\0example!";
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/corporate", "user", "pwd"))
{
printStringAndAscii(originalString);
//Removing previously inserted entry
PreparedStatement prepareStatement = connection.prepareStatement("DELETE FROM tip WHERE tip_id=?");
prepareStatement.setInt(1, 1);
int result = prepareStatement.executeUpdate();
prepareStatement.close();
System.out.println("Delete Result:" + result);
//Inserting new entry
prepareStatement = connection.prepareStatement("INSERT INTO tip(tip_id,tip_text) VALUES(?,?)");
prepareStatement.setString(1, "1");
prepareStatement.setString(2, originalString);
result = prepareStatement.executeUpdate();
System.out.println("Insert Result:" + result);
prepareStatement.close();
Statement statement = connection.createStatement();
//Loading inserted entry from database
ResultSet resultSet = statement.executeQuery("SELECT * FROM tip WHERE tip_id=1");
if (resultSet.next())
{
//printing the fetched string from database
String string = resultSet.getString(2);
printStringAndAscii(string);
}
resultSet.close();
statement.close();
connection.close();
}
catch (Throwable e)
{
e.printStackTrace();
}
Interestingly, I have found out that System.out.println() prints until it finds '\0' (like c), however prepared statement inserts complete string with length.
I am using java to execute postgresql statements. In one step, I need to create a table in which one column will store blocks of text (that may contain punctuation marks, such as comma, semi-colon, etc).
What data type do I use to populate this column?
For example, in the given example, I am creating a table called "MYTHOUGHTS", and that has a column called "THOUGHTS". I am trying the following code:
try {
con = DriverManager.getConnection(url, user, password);
System.out.println("Opened Database Successfully");
st = con.createStatement();
String sql = "CREATE TABLE MYTHOUGHTS " + "(ID INT PRIMARY KEY NOT NULL," + " THOUGHTS TEXT NOT NULL," + " Number INT NOT NULL," + " ADDRESS CHAR(50), " + " SALARY REAL)";
st.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,THOUGHTS,AGE,ADDRESS,SALARY) " + "VALUES (1," + "This is life, as I see it. Do you think otherwise?" + ", 32, 'California', 20000.00 );";
st.executeUpdate(sql);
st.close();
con.close();
}
catch (Exception e) {
e.printStackTrace();
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
I get the following error:
org.postgresql.util.PSQLException: ERROR: syntax error at or near "life"
Position: 68
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2161)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1890)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:560)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:403)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:331)
at com.vivek.PostgreSQLExample.main(PostgreSQLExample.java:39)
org.postgresql.util.PSQLException: ERROR: syntax error at or near "life"
Position: 68
It is possible that TEXT data type is not appropriate. Please, let me know how I may add multiple lines of text that may have commas and periods into a column.
Thank you for your time and assistance. Highly appreciate it.
Use the text data type. The contents do not matter, the only thing you can't put in text is the null byte \0.
If you're having problems with the contents then you're running dangerous code that's doing direct string interpolation, instead of using parameterized statements. See: http://bobby-tables.com/ , http://en.wikipedia.org/wiki/SQL_injection, http://docs.oracle.com/javase/tutorial/jdbc/basics/
I'm trying to insert CLOBs into a database (see related question). I can't quite figure out what's wrong. I have a list of about 85 clobs I want to insert into a table. Even when inserting only the first clob I get ORA-00911: invalid character. I can't figure out how to get the statement out of the PreparedStatement before it executes, so I can't be 100% certain that it's right, but if I got it right, then it should look exactly like this:
insert all
into domo_queries values ('select
substr(to_char(max_data),1,4) as year,
substr(to_char(max_data),5,6) as month,
max_data
from dss_fin_user.acq_dashboard_src_load_success
where source = ''CHQ PeopleSoft FS''')
select * from dual;
Ultimately, this insert all statement would have a lot of into's, which is why I just don't do a regular insert statement. I don't see an invalid character in there, do you? (Oh, and that code above runs fine when I run it in my sql developer tool.) And I if I remove the semi-colon in the PreparedStatement, it throws an ORA-00933: SQL command not properly ended error.
In any case, here's my code for executing the query (and the values of the variables for the example above).
public ResultSet executeQuery(String connection, String query, QueryParameter... params) throws DataException, SQLException {
// query at this point = "insert all
//into domo_queries values (?)
//select * from dual;"
Connection conn = ConnectionPool.getInstance().get(connection);
PreparedStatement pstmt = conn.prepareStatement(query);
for (int i = 1; i <= params.length; i++) {
QueryParameter param = params[i - 1];
switch (param.getType()) { //The type in the example is QueryParameter.CLOB
case QueryParameter.CLOB:
Clob clob = CLOB.createTemporary(conn, false, oracle.sql.CLOB.DURATION_SESSION);
clob.setString(i, "'" + param.getValue() + "'");
//the value of param.getValue() at this point is:
/*
* select
* substr(to_char(max_data),1,4) as year,
* substr(to_char(max_data),5,6) as month,
* max_data
* from dss_fin_user.acq_dashboard_src_load_success
* where source = ''CHQ PeopleSoft FS''
*/
pstmt.setClob(i, clob);
break;
case QueryParameter.STRING:
pstmt.setString(i, "'" + param.getValue() + "'");
break;
}
}
ResultSet rs = pstmt.executeQuery(); //Obviously, this is where the error is thrown
conn.commit();
ConnectionPool.getInstance().release(conn);
return rs;
}
Is there anything I'm just missing big time?
If you use the string literal exactly as you have shown us, the problem is the ; character at the end. You may not include that in the query string in the JDBC calls.
As you are inserting only a single row, a regular INSERT should be just fine even when inserting multiple rows. Using a batched statement is probable more efficient anywy. No need for INSERT ALL. Additionally you don't need the temporary clob and all that. You can simplify your method to something like this (assuming I got the parameters right):
String query1 = "select substr(to_char(max_data),1,4) as year, " +
"substr(to_char(max_data),5,6) as month, max_data " +
"from dss_fin_user.acq_dashboard_src_load_success " +
"where source = 'CHQ PeopleSoft FS'";
String query2 = ".....";
String sql = "insert into domo_queries (clob_column) values (?)";
PreparedStatement pstmt = con.prepareStatement(sql);
StringReader reader = new StringReader(query1);
pstmt.setCharacterStream(1, reader, query1.length());
pstmt.addBatch();
reader = new StringReader(query2);
pstmt.setCharacterStream(1, reader, query2.length());
pstmt.addBatch();
pstmt.executeBatch();
con.commit();
Of the top of my head, can you try to use the 'q' operator for the string literal
something like
insert all
into domo_queries values (q'[select
substr(to_char(max_data),1,4) as year,
substr(to_char(max_data),5,6) as month,
max_data
from dss_fin_user.acq_dashboard_src_load_success
where source = 'CHQ PeopleSoft FS']')
select * from dual;
Note that the single quotes of your predicate are not escaped, and the string sits between q'[...]'.
One of the reason may be if any one of table column have an underscore(_) in its name . That is considered as invalid characters by the JDBC . Rename the column by a ALTER Command and change in your code SQL , that will fix .
Oracle provide some explanation for ORA-00911. You can got this explanation after executing SQL request in Oracle SQL Developer.
ORA-00911. 00000 - "invalid character"
*Cause: identifiers may not start with any ASCII character other than
letters and numbers. $#_ are also allowed after the first
character. Identifiers enclosed by doublequotes may contain
any character other than a doublequote. Alternative quotes
(q'#...#') cannot use spaces, tabs, or carriage returns as
delimiters. For all other contexts, consult the SQL Language
Reference Manual
But in your case it seems to be double ' character
This question already has answers here:
Is there a way to retrieve the autoincrement ID from a prepared statement
(5 answers)
Closed 7 years ago.
I have this code trying to insert a record in the database:
try {
Connection conn = getConnection();
String sql =
"INSERT INTO myTable(userId,content,timestamp) VALUES(?,?,NOW())";
PreparedStatement st =
conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
st.setLong(1, userId);
st.setString(2, content);
id = st.executeUpdate(); //this is the problem line
} catch(Exception e) {}
The problem is, though the record is inserted correctly, I want id to contain the primary key + auto_increment id of the record that was just inserted. However, for some reason, it always returns '1' as the id, possibly because the value of userId is 1 during the inserts.
My table is InnoDB. At first userId was a foreign key to another table, owever I've since deleted the foreign key and even the index on the userId column, but I'm still getting 1 as the return value.
Any ideas what I'm doing wrong?
PreparedStatment.executeUpdate()
Returns:
either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements that return nothing
You need to use execute() instead and get the ResultSet with getGeneratedKeys(); it's going to contain the data you want.
Edit to add: I read your question as there is an auto-increment field in the table that is not userId
The accepted Answer by Brian Roach is correct. I'm adding some thoughts and an example with full code.
RETURN_GENERATED_KEYS Does Not Mean “return generated keys”
The original poster seems to be confused, understandably, by the phrasing of the flag Statement.RETURN_GENERATED_KEYS. Contrary to intuition, passing this flag does not change the behavior of the PreparedStatement::executeUpdate method. That method always returns an int, the number of rows affected by the SQL executed. The "executeUpdate" method never returns the generated keys.
int countRowsAffected = pstmt.executeUpdate(); // Always return number of rows affected, *not* the generated keys.
Ask, and Ye Shall Receive
If you want the generated keys, you must do two steps:
Pass the flag, and
Ask for a ResultSet made up of rows containing only the generated key values.
This arrangement allows you to add the behavior of getting back generated keys while keeping the other desirable behavior, getting a count of the number of rows affected.
Example Code
Here is a nearly real-world example taken from a Java 8 app that scrapes data from a data feed. I think in this context a full-blown example may be more useful than a minimal one.
Minor details… This code may not be perfect, syntactically or otherwise, as I copy-pasted-modified real source code. I am using the UUID data type rather than integers as the surrogate primary key of my table. The classes CharHelper and DBHelper are my own, the details of which are not important here. The x and y variables are replacements of my own app's meaningful data. My logging calls are made to the SLF4J framework. The UUID hex strings are a convenient way to link reports in the logs back to the original source code. The database is Postgres, but this kind of code should work on any database supporting the reporting of generating keys.
public UUID dbWrite ( String x , String y , DateTime whenRetrievedArg ) {
if ( whenRetrievedArg == null ) {
logger.error( "Passed null for whenRetrievedArg. Message # 2112ed1a-4612-4d5d-8cc5-bf27087a350d." );
return null;
}
Boolean rowInsertComplete = Boolean.FALSE; // Might be used for debugging or logging or some logic in other copy-pasted methods.
String method = "Method 'dbWrite'";
String message = "Insert row for some_table_ in " + method + ". Message # edbea872-d3ed-489c-94e8-106a8e3b58f7.";
this.logger.trace( message );
String tableName = "some_table_";
java.sql.Timestamp tsWhenRetrieved = new java.sql.Timestamp( whenRetrievedArg.getMillis() ); // Convert Joda-Time DatTime object to a java.sql.Timestamp object.
UUID uuidNew = null;
StringBuilder sql = new StringBuilder( AbstractPersister.INITIAL_CAPACITY_OF_SQL_STRING ); // private final static Integer INITIAL_CAPACITY_OF_SQL_STRING = 1024;
sql.append( "INSERT INTO " ).append( tableName ).append( CharHelper.CHAR.PAREN_OPEN_SPACED ).append( " x_ , y_ " ).append( CharHelper.CHAR.PAREN_CLOSED ).append( DBHelper.SQL_NEWLINE );
sql.append( "VALUES ( ? , ? , ? ) " ).append( DBHelper.SQL_NEWLINE );
sql.append( ";" );
try ( Connection conn = DBHelper.instance().dataSource().getConnection() ;
Here we do Step # 1, pass the RETURN_GENERATED_KEYS flag.
PreparedStatement pstmt = conn.prepareStatement( sql.toString() , Statement.RETURN_GENERATED_KEYS ); ) {
We continue to prepare and execute the statement. Note that int countRows = pstmt.executeUpdate(); returns the count of affected rows, not the generated keys.
pstmt.setString( 1 , x );
pstmt.setString( 2 , y );
pstmt.setTimestamp( 3 , tsWhenRetrieved );
// Execute
int countRows = pstmt.executeUpdate(); // Always returns an int, a count of affected rows. Does *not* return the generated keys.
if ( countRows == 0 ) { // Bad.
this.logger.error( "Insert into database for new " + tableName + " failed to affect any rows. Message # 67e8de7e-67a5-42a6-a4fc-06929211e6e3." );
} else if ( countRows == 1 ) { // Good.
rowInsertComplete = Boolean.TRUE;
} else if ( countRows > 1 ) { // Bad.
rowInsertComplete = Boolean.TRUE;
this.logger.error( "Insert into database for new " + tableName + " failed, affecting more than one row. Should not be possible. Message # a366e215-6cf2-4e5c-8443-0b5d537cbd68." );
} else { // Impossible.
this.logger.error( "Should never reach this Case-Else with countRows value " + countRows + " Message # 48af80d4-6f50-4c52-8ea8-98856873f3bb." );
}
Here we do Step # 2, ask for a ResultSet of the generated keys. In the case of this example, we inserted a single row and expect back a single generated key.
if ( rowInsertComplete ) {
// Return new row’s primary key value.
ResultSet genKeys = pstmt.getGeneratedKeys();
if ( genKeys.next() ) {
uuidNew = ( UUID ) genKeys.getObject( 1 ); // ResultSet should have exactly one column, the primary key of INSERT table.
} else {
logger.error( "Failed to get a generated key returned from database INSERT. Message # 6426843e-30b6-4237-b110-ec93faf7537d." );
}
}
The rest is error-handling and clean-up. Do note that we return the UUID, the generated primary key of the inserted record, at the bottom of this code.
} catch ( SQLException ex ) {
// We expect to have occasional violations of unique constraint on this table in this data-scraping app.
String sqlState = ex.getSQLState();
if ( sqlState.equals( DBHelper.SQL_STATE.POSTGRES.UNIQUE_CONSTRAINT_VIOLATION ) ) { // SqlState code '23505' = 'unique_violation'.
this.logger.trace( "Found existing row when inserting a '" + tableName + "' row for y: " + y + ". Expected to happen on most attempts. Message # 0131e8aa-0bf6-4d19-b1b3-2ed9d333df27." );
return null; // Bail out.
} else { // Else any other exception, throw it.
this.logger.error( "SQLException during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # 67908d00-2a5f-4e4e-815c-5e5a480d614b.\n" + ex );
return null; // Bail out.
}
} catch ( Exception ex ) {
this.logger.error( "Exception during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # eecc25d8-de38-458a-bb46-bd6f33117969.\n" + ex );
return null; // Bail out.
}
if ( uuidNew == null ) {
logger.error( "Returning a null uuidNew var. SQL: {} \nMessage # 92e2374b-8095-4557-a4ed-291652c210ae." , sql );
}
return uuidNew;
}
String SQLQuery=" ";
String generatedKeys[]= {"column_name"};//'column_name' auto-increment column
prepSt = Connection.prepareStatement(SQLQuery,generatedKeys);
prepSt.setInt(1, 1234);
.....
.....
....
prepSt.executeUpdate();
ResultSet rs = prepSt.getGeneratedKeys; // used same PreparedStatement object as used for Insert .
if(rs.next()) {
int id=rs.getLong("column_name");
System.out.println(id);
}
} catch (SQLException e) {
}
If you have set userId has auto-increment in your database, you shouldn't try and add it yourself. You should insert NULL, and it will auto-increment for you! (The clue is in the name!)
Also, you are not updating your table, you are inserting into it. So you don't executeUpdate(). Try...
PreparedStatement pst = conn.prepareStatement("INSERT INTO myTable(userId,content,timestamp) VALUES(NULL,?,NOW())");
pst.setString(1, content);
pst.executeQuery();
What you get is the notification of 'Rows insetrted' (for INSERT statement). We use this method to know whether our DML query is succesful or not. The following is the way to get the Auto generated ID using [prepareStatement(yourSQL, Statement.RETURN_GENERATED_KEYS)]. Pls note that this method only return you a RowID ref. To get the actual val, pls refer to Method 2.
(Method 1)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
myPrepStatement = <Connection>.prepareStatement(yourSQL, Statement.RETURN_GENERATED_KEYS);
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
java.sql.RowId rid=rs.getRowId(1);
//what you get is only a RowId ref, try make use of it anyway U could think of
System.out.println(rid);
}
} catch (SQLException e) {
}
(Method 2)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
//IMPORTANT: here's where other threads don tell U, you need to list ALL cols
//mentioned in your query in the array
myPrepStatement = <Connection>.prepareStatement(yourSQL, new String[]{"Id","Col2","Col3"});
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
//In this exp, the autoKey val is in 1st col
int id=rs.getLong(1);
//now this's a real value of col Id
System.out.println(id);
}
} catch (SQLException e) {
}
Basically, try not used Method1 if you just want the value of SEQ.Nextval, b'cse it just return the RowID ref that you may cracked your head finding way to make use of it, which also don fit all data type you tried casting it to! This may works fine (return actual val) in MySQL, DB2 but not in Oracle.
IMPORTANT:
Turn off your SQL Developer, Toad or any client which use the same login session to do INSERT when you're debugging. It MAY not affect you every time (debugging call) ... until you find your apps freeze without exception for some time. Yes ... halt without exception!
I am using a MYSQL database to store persisted data for a java application.
CREATE TABLE testtable1( key1a CHAR, key1b CHAR, encoded1a CHAR, encoded1b CHAR);
As you can see i create a table that stores for each row a 4 different CHAR's.
the sql i have written for inputing the chars into the table is
char k1[] = new char[2]
char p[] = new char[2];
k1[0]=0x00aa;
k1[1]=(char)0xaaaa;
p[0]=0x0001;
p[1]=0x0002;
sql = "INSERT INTO testtable1 "
+"(key1a, key1b , encoded1a,encoded1b) "
+ "VALUES "
+ "('"+ k1[0] + "',"
+ "'"+ k1[1] + "',"
+ "'"+ p[0] + "',"
+ "'"+ p[1] + "')";
The above statement i dont think work. it inpts the data as such
And then into the database like this
So as you can see, it is been inputed with a ?(whatis this?) and then the final two columns are not being populated.
So could any of you guys point me in the right direction to where my thinking is wrong when it comes to storing char variable.
And also , how would i retrieve the chars from the database table? as i cannot find any Doc's on this???
thank you very much
I would suggest looking into PreparedStatement.
An example would be as follows:
PreparedStatement ps = nulll;
try {
ps = connection.prepareStatement("INSERT INTO testtable1 (key1a, key1b , encoded1a,encoded1b) VALUES (? ,? ,?, ?)");
for (int i = 1; i <= k.length; i++) {
ps.setString(i, "" + k[i]);
}
ps.executeUpdate();
} catch (SQLException) {
//Log exception
} finally {
if (ps != null) {
try {
ps.close();
ps = null;
} catch (SQLException e) {}
}
}
The problem here is that if char[]k's length is greater than 4, an exception will be thrown.
In addition to the PreparedStatement answer:
Java's char is a 16-bit UTF-16 value.
MySQL's CHAR is a synonym for CHAR(1)... which is just a single character wide, (number of bytes depends on the table's character set).
Now, PreparedStatement's setString should take care of the encoding conversion for you, but that still leaves one problem: MySQL on some systems defaults to the latin1 character set. Which means CHAR is too small.
There is a fix for this. During table creation, you can force a table to be a specific character set:
CREATE TABLE testtable1( key1a CHAR, key1b CHAR, encoded1a CHAR, encoded1b CHAR) CHARACTER SET utf8;
Note: You can also use utf16 instead of utf8; I just used utf8 for storage size reasons.