I'm trying to write an H2 stored procedure to insert some values into a table. I'm doing this because in my unit testing environment, I'd rather connect to an in-memory than a real database. Here's what I have (note that I'm doing this in groovy, not java, but I don't think that matters).
Sql sql = new Sql(JdbcConnectionPool.create("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "user", "password"))
sql.executeUpdate("CREATE TABLE data (key VARCHAR(255) PRIMARY KEY,value VARCHAR(1023) )")
sql.execute("CREATE ALIAS insertInto FOR \"com.vanguard.fig.batch.core.StoredProcs.insertInto\"")
sql.execute("CALL insertInto(?,?)", val1, val2)
sql.eachRow("Select * From data") { row ->
println row
}
And my procedure is as follows
public class StoredProcs {
void insertInto(java.sql.Connection con, String val1, String val2) throws Exception {
String resultValue=null;
Sql sql = new Sql(con)
sql.executeInsert("Insert Into data VALUES(?, ?)", val1, val2)
}
}
The examples were all fairly confusing and I'm not sure I'm doing this right. Here's the error
WARNING: Failed to execute: CALL insertInto(?,?) because: Parameter "#1" is not set; SQL statement:
Insert Into data VALUES(?, ?) [90012-193]
Which version of h2 are you using.?
Looks like h2 has a bug filed for this issue. GitHub Bug
Please upgrade it to the latest version to fix this issue.
PS : I was not able to add this as comment since i need to have 50 reputation to add a comment. Hope this helps
Related
I'm trying to run an insert or update on a table - the string generated from below works fine when copy pasted into HeidiSQL but throws SQLSyntaxErrorExceptions when run from Java:
Statement statement = con.createStatement();
String escapedXML = EscapeString(billboard.getXml());
String sql = String.format(
"DELIMITER $ \r\nBEGIN NOT ATOMIC\r\n" +
"IF EXISTS(SELECT * FROM billboards where Name='%s') THEN UPDATE billboards SET XML='%s' where Name='%s';\r\n" +
"ELSE insert into billboards(Name, XML, CreatorName) values('%s', '%s', '%s');\r\n" +
"END IF;\r\n" +
"END $\r\n" +
"DELIMITER ;", billboard.getName(), escapedXML, billboard.getName(), billboard.getName(), escapedXML, billboard.getCreatorName());
// Insert or update billboard
statement.execute(sql);
I can't figure out why.
I would recommend using the insert ... ok duplicate key syntax here rather than a code block. This is more efficient, and implements the lockout a single statement, which should avoid the problem you meet when running the query from your php code.
insert into billboards(Name, XML, CreatorName)
values(?, ?, ?)
on duplicate key update set XML = values(XML)
For this to work, you need a unique (or primary key) constraint on column Name.
Also, consider using a parameterized query rather than concatenating variables in your query stringW Escaping is inefficient and does not really make your code safer.
You should have tried NamedParameterStatement with your query to facilitate setting of string parameters and avoid their duplication (using refactored query suggested in GMB's earlier answer):
String sql = "INSERT INTO billboards (Name, XML, CreatorName) VALUES (:name, :xml, :creator) "
+ "ON DUPLICATE KEY UPDATE SET XML = :xml";
NamedParameterStatement statement = new NamedParameterStatement(con, sql);
statement.setString("name", billboard.getName());
statement.setString("xml", EscapeString(billboard.getXml()));
statement.setString("creator", billboard.getCreatorName());
// Insert or update billboard
statement.execute(sql);
The reason that you are getting a syntax error is that DELIMITER is a MySQL client command and not an SQL statement. MySQL commands may not be used in with JDBC.
For more information:
Delimiters in MySQL
I have the following stored procedure in mysql
DELIMITER $$
CREATE DEFINER=`user`#`%` PROCEDURE `sp_getJobs`()
BEGIN
SELECT * FROM jobs_table;
END$$
DELIMITER ;
and the following defined in hsqldb (for unit tests)
CREATE PROCEDURE sp_getJobs() READS SQL DATA
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE result CURSOR WITH RETURN FOR SELECT * FROM jobs_table FOR READ ONLY;
OPEN result;
END
Spring framework code that executes stored procedure
query = "Call sp_getJobs"; //This is used when MySql is database
query = "Call sp_getJobs()"; //This is used when HsqlDb is used in testing
getJdbcTemplate().query(query, rs -> {
process(rs);
return null;
});
private void process(ResultSet rs) {
while (rs.next()) {
//do something
}
}
The spring code that executes mysql works fine, however, when I execute the same code against hsqldb I get an empty result set (rs.next() returns false).
Note - The createdata.sql file that I use for testing, creates the jobs_table and populates it with 6 rows of data too. So the table should have data.
The Spring code doesn't look right for HSQLDB procedures. It will probably work for a function that returns a table.
Change the procedure to FUNCTION and return a table containing the data.
Alternatively, use the procedure but after calling it, execute getMoreResults() prior to process(ResultSet rs) call.
While the code below didnt work for only hsqldb stored procedure listed in question
getJdbcTemplate().query(query, rs -> {
process(rs);
return null;
});
private void process(ResultSet rs) {
while (rs.next()) {
//do something
}
}
code below worked for all of them - mysql, sqlserver, informix & also hsqldb
SimpleJdbcCall simpleJdbcCall =
new SimpleJdbcCall(getJdbcTemplate().getDataSource())
.withCatalogName(catalog)
.withProcedureName(query);
where catalog is the package name. So, if your stored procedure is such as accounts.get_all_expired_accounts then catalog="accounts" & query="get_all_expired_accounts".
I'm using PostgreSQL with Spring JDBC. Everythig building with Gradle:
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework:spring-jdbc")
compile("postgresql:postgresql:9.0-801.jdbc4")
compile("com.h2database:h2")
compile("com.googlecode.json-simple:json-simple:1.1")
testCompile("junit:junit")
}
Here is code fragment where I got exception:
String sql = "INSERT INTO " + dbname + " (:fields) VALUES (:values)";
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue( "fields", fieldsObj.keySet().toArray() );
parameters.addValue( "values", fieldsObj.values().toArray() );
count = jdbcTemplate.update( sql, parameters );
I get this error:
PreparedStatementCallback; bad SQL grammar [INSERT INTO test (?, ?, ?) VALUES (?, ?, ?)]; nested exception is org.postgresql.util.PSQLException
I've tried to convert array into string with values, divided by commas, but this gave me same error. What should I do? Or, if you have better ideas what to use, to build custom queries, please, post this idea in comments.
You can't use value placeholders ("?") for things that aren't values (like your field-names).
If you really want to do this, just build up the query, mapping the field-names in. Then apply the values with placeholders.
BUT do make sure you check the field-names are as you would expect and escape/quote them properly.
Oh - Not a java programmer, but do check that keySet() and values() return their results in the same order too.
Edit:
The "?" marks are value placeholders. They are used in database connection libraries of all sorts. They will be mapped to whatever parameterised query syntax the database can use. However, they only work for values - so the database can parse the query with the placeholders.
I am trying to do an insert for return generated id INSERT RETURNING id. In postgres editor it work without problems, but in code execution - java 1.6 with iBatis 3 (8.4 postgres driver version 8.4-702) gives the error - Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near " RETURNING. "
It this means Postgres does not support Returning to insert?
I found this online - Concurrency issues when retriveing Ids of newly inserted rows with ibatis
but not how to do
Code iBatis xml
<insert id="insertNewItem" parameterType="itemAlias" useGeneratedKeys="true" keyProperty="item_id">
INSERT INTO items (
category_id,
description,
...)
VALUES(
#{category_id},
#{description},
...)
RETURNING item_id
</insert>
La respuesta es:
Public void insert(Item itemAlias) {
SqlSession session = sqlSessionFactory.openSession();
try {
Session.getMapper(ItemMapper.class).insert(itemAlias);
Logger.debug("itemAlias id:" + itemAlias.getItem_id()); // Here this you give you the generated key.
}
Finally {
Session.close();
}}
MyBatis xml
<insert id="insertNewItem" parameterType="itemAlias" useGeneratedKeys="true" keyProperty="item_id">
INSERT INTO items (
category_id,
description,
...)
VALUES(
#{category_id},
#{description},
...)
</insert>
MyBatis guys helped me. Thanks a lot
i htink u miss some concept here. RETURNING is neither RETURN nor SELECT. it just throw some variable out. To make it work, the statement gonna look like this
declare tmp integer;
INSERT INTO items (
category_id,
description,
...)
VALUES(
#{category_id},
#{description},
...)
RETURNING item_id INTO tmp
return tmp;
u can change integer to your primary key data type
Is there a cross database platform way to get the primary key of the record you have just inserted?
I noted that this answer says that you can get it by Calling SELECT LAST_INSERT_ID() and I think that you can call SELECT ##IDENTITY AS 'Identity'; is there a common way to do this accross databases in jdbc?
If not how would you suggest I implement this for a piece of code that could access any of SQL Server, MySQL and Oracle?
Copied from my code:
pInsertOid = connection.prepareStatement(INSERT_OID_SQL, Statement.RETURN_GENERATED_KEYS);
where pInsertOid is a prepared statement.
you can then obtain the key:
// fill in the prepared statement and
pInsertOid.executeUpdate();
ResultSet rs = pInsertOid.getGeneratedKeys();
if (rs.next()) {
int newId = rs.getInt(1);
oid.setId(newId);
}
Hope this gives you a good starting point.
extraneon's answer, although correct, doesn't work for Oracle.
The way you do this for Oracle is:
String key[] = {"ID"}; //put the name of the primary key column
ps = con.prepareStatement(insertQuery, key);
ps.executeUpdate();
rs = ps.getGeneratedKeys();
if (rs.next()) {
generatedKey = rs.getLong(1);
}
Have you tried the Statement.executeUpdate() and Statement.getGeneratedKeys() methods? There is a developerWorks article that mentions the approach.
Also, in JDBC 4.0 Sun added the row_id feature that allows you to get a unique handle on a row. The feature is supported by Oracle and DB2. For sql server you will probably need a third party driver such as this one.
Good luck!
for oracle, Hibernate uses NEXT_VALUE from a sequence if you have mapped a sequence for PKEY value generation.
Not sure what it does for MySQL or MS SQL server
Spring provides some useful support for this operation and the reference guide seems to answer your question:
There is not a standard single way to
create an appropriate
PreparedStatement (which explains why
the method signature is the way it
is). An example that works on Oracle
and may not work on other platforms
is...
I've tested this example on MySQL and it works there too, but I can't speak for other platforms.
For databases that conform to SQL-99, you can use identity columns:
CREATE TABLE sometable (id INTEGER GENERATED ALWAYS AS IDENTITY(START WITH 101) PRIMARY KEY, ...
Use getGeneratedKeys() to retrieve the key that was just inserted with executeUpdate(String sql, int autoGeneratedKeys). Use Statement.RETURN_GENERATED_KEYS for 2nd parameter to executeUpdate()
Just declare id column as id integer not NULL primary key auto_increment
after this execute this code
ResultSet ds=st.executeQuery("select * from user");
while(ds.next())
{
ds.last();
System.out.println("please note down your registration id which is "+ds.getInt("id"));
}
ds.close();
the above code will show you the current row's id
if you remove ds.last() than it will show all values of id column