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.
Related
I trying to save some sql insert statements to files to use them for testing.
I would like to use flyway placeholders for that but I'm not able to find any.
Some example in Java:
var sqlTXT = sql.insertInto(table("TBLNAME"))
.set(field("strCol"), field("strVal").toString())
.set(field("placeHolderCol"), field(inline("${flyway:user}")))
.getSQL(ParamType.INLINED);
This will produce SQL string like this one:
insert into TBLNAME (strCol, placeHolderCol) values ('strVal', '${flyway:user}')
and I'm looking for something like this
insert into TBLNAME (strCol, placeHolderCol) values ('strVal', ${flyway:user})
So flyway can substitute ${flyway:user} and insert user name.
Is there any way to render sql like this or will I have to do it "manually"?
Flyway's placeholders are no different from any other "vendor specific" SQL syntax, which isn't supported out of the box by jOOQ, so plain SQL templating has the answer.
Just use
field("${flyway:user}")
Don't use DSL.inline(), which is used for creating "inline values" (e.g. a string literal).
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 need to update the sql database daily manner for changes in clicks impressions and conversions column. i had array for each column. In this statement there is a error . i cant find the error too.help please
for(int j=1;j<row;j++){
pst= conn.prepareStatement("INSERT INTO babum_test.l2ttracker SET Clientid='"+Customer_ID[j]+"',Accountname='"+Account[j]"',Dates='"Day[j]"',Clicks='"Clicks[j]"',Impressions='"Impressions[j]"',CTR='"CTR[j]"',Avg_CPC='"Avg_CPC[j]"',Cost='"Costs[j]"',Conversions='"Conversions[j]"',Converted_clicks='"Converted_clicks[j]"',Avg_position='"Avg_position[j]"',Revenue='"Total_Conv_value[j]+"' ON DUPLICATE KEY UPDATE'"+ "'Clicks='"+Clicks[j]"',Impressions='"Impressions[j]"',CTR='"CTR[j]"',Avg_CPC='"Avg_CPC[j]"',Cost='"Costs[j]"',Conversions='"Conversions[j]"',Avg_position='"Avg_position[j]"',Converted_clicks='"Converted_clicks[j]"',Revenue='"Total_Conv_value[j]);
}
Three main problems there:
You're not using quotes where you should be using quotes.
I'm not aware of any database that uses an INSERT ... SET statement.
You're leaving yourself wide open to SQL Injection attacks.
Obligatory comic re #3:
Instead:
pst = conn.prepareStatement(
"INSERT INTO sampletable " +
"(FirstColumn SecondColumn, Etc) " +
"VALUES " +
"(?, ?, ?)"
);
pst.setString(1, "value for first column");
pst.setInt(2, 42);
pst.setDate(3, /*...some date...*/);
pst.execute();
The question marks are placeholders where the prepared statement will put the values. Even when the parameter is a string, you don't put quotes around the question mark; that's handled behind the scenes and is part of the reason for using prepared statements. Note that the parameter numbers start at 1, not 0.
Read up on SQL syntax and how to correctly use prepared statements; this site may be useful.
I ran the query in both sql Workbench and in the executeUpdate() method in java:
in Workbench:
INSERT INTO recentsearches (name) VALUES ("blah");
in java:
statement.executeUpdate("INSERT INTO recentsearches (name) VALUES (\""+name+"\""));
Assuming name = "blah". But I get a syntax error from running the query in java, I've already checked the string value for name. It definitely comes up as "blah", and I didn't forget the speech marks around string values, yet I still get a syntax error.
The error I get in my console is:
check the manual that corresponds to your MySQL server version for the
right syntax to use near '' at line 1
Try to use:
"INSERT INTO recentsearches (name) VALUES("+name+")";
My advice, use PreparedStatement because it has:
-Precompilation and DB-side caching of the SQL statement leads to overall faster execution and the ability to reuse the same SQL statement in batches.
-Automatic prevention of SQL injection attacks by builtin escaping of quotes and other special characters. Note that this requires that you use any of the PreparedStatement setXxx() methods to set the values
I've tried to call a stored procedure with parameter names specified, but the JDBC failed to accept the parameters. It says:
Method org.postgresql.jdbc4.Jdbc4CallableStatement.setObject(String,Object) is not yet implemented.
I use postgresql-9.2-1003.jdbc4
I there any other way to do this?
I know that I can just specify the sequence number. But I want to specify the parameter names as it is more convenient for me to do so.
My code:
String call_statement = "{ ? = call procedure_name(?, ?, ?) }";
CallableStatement proc = connection.prepareCall(call_statement);
proc.registerOutParameter(1, Types.OTHER);
proc.setObject("param1", 1);
proc.setObject("param2", "hello");
proc.setObject("param3", true);
proc.execute();
ResultSet result = (ResultSet)proc.getObject(1);
Unfortunately, using the parameter names is not a feature supported by the implementation of the JDBC 4 driver for the PostgreSQL database. See the code of this JDBC 4 implementation in GrepCode.
However, you can still continue to use an integer (variable or literal) to indicate the position of the parameter.
It's 2020 here and the standard open source JDBC driver for Postgres still doesn't support named parameter notation for CallableStatement.
Interestingly, EnterpriseDB driver does support it (with that said - I tried to use EDB JDBC driver - it indeed supports named parameters but it does so many things differently, if at all, that we ruled out this option entirly, for those other reasons)
A solution which worked for us - is to use this "hack" (pseudo-code, YMMV):
String sql = "SELECT * FROM PROC(IN_PARAM1 => ?, IN_PARAM2 => ?, IN_PARAM => ?)";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setObject("IN_PARAM1", 1);
ps.setObject("IN_PARAM2", "hello");
ps.setObject("IN_PARAM3", true);
ps.execute();
ResultSet result = (ResultSet)ps.getObject(1);
The killer feature of this notation - is the ability to call SPs with optional params (it could be achieved by having optional ordinal params, but if you have more than a few of them - it becomes a nightmare, as one needs to pass so many nulls, that it's too easy to miscount, and those are very hard to spot)
There are also additional benefits, like ability to return multiple ResultSets (refcursors), ability to use maps as params, etc.
P.S.: we also use the same trick for Node.js with node-postgres - works well for years.