How to execute a set of related SQL instructions with JDBC? - java

I'm trying to execute this script through JDBC (it's SQL Server):
DECLARE #var VARCHAR(10)
SET #var = "test"
INSERT INTO foo (name) VALUES (#var)
It's just an example, in my business case I have a big collection of SQL statements in one script.
I'm trying this:
final Statement stmt = connection.createStatement();
for (String sql : lines) {
stmt.executeUpdate(sql);
}
stmt.close();
SQL Server is saying on the second line:
java.sql.SQLException: Must declare the scalar variable "#var".
Looks like I'm doing something wrong...

You're executing it one line at a time, so of course the second line fails as it depends on the first line. Why not execute it all at once?...

Related

If table exists drop table then create it - syntax in line Create

I just want to create a table, but if it exists it needs to be dropped and re-created.
I use MySQL 8.0.29
Error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'CREATE TABLE users (Id long, name varchar(100), lastName
varchar(100), age tin' at line 1
Code:
public static void main(String[] args) throws SQLException {
try (Connection connection = Util.getConnection()) {
String sqlCommand = "DROP TABLE IF EXISTS `users`;" +
"CREATE TABLE `users` (Id long, name varchar(100), lastName varchar(100), age tinyint)";
Statement statement = connection.createStatement();
statement.executeUpdate(sqlCommand);
System.out.println("table created");
int rows = statement.executeUpdate("INSERT users(Id,name,lastName,age) VALUES (101,'Mike','Manson',31)");
}
}
The following may be the reason:
long is not a valid MySQL data type.
variable sqlCommand contains multiple statements. Each SQL statement should be terminated with a semicolon symbol. Adding a semicolon at the end of CREATE statement can solve your issue.
By default the JDBC driver does not support multiple SQL statements in one execute call. There's a connect string option to enable it:
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-security.html#cj-conn-prop_allowMultiQueries
allowMultiQueries
Allow the use of ';' to delimit multiple queries during one statement.
But there's no good reason to use multi-queries. They make your code more complex, not simpler. Allowing multi-queries creates the opportunity for a type of SQL injection vulnerabilities that are not possible otherwise. See https://xkcd.com/327/
Just run one statement per call to executeUpdate().

Raise Java SQLException from SQL Server stored procedure?

I have an SQL Server stored procedure similar to this:
CREATE PROCEDURE proc1
(
#param DECIMAL
)
AS
BEGIN
INSERT INTO table1 (#param);
-- further commands
END;
GO
This procedure is called from Java. I introduced a unique constraint on table1, on the same column which is inserted above. Is expected to get an SQLException in Java in case of a constraint violation, but it is not happening. When the procedure is executed manually from SSMS, I can see that it prints the constraint violation error, and then continues along the rest of the process, which I think is weird, I expected it to fail. So I changed it like this:
CREATE PROCEDURE proc1
(
#param DECIMAL
)
AS
BEGIN
BEGIN TRY
INSERT INTO table1 (#param);
END TRY
BEGIN CATCH
THROW 51000, 'Unable to insert', 1;
END CATCH
-- further commands
END;
GO
Now when I execute it manually in SSMS, the procedure stops in case of a failure, and prints my error message. However the Java calling process is not receiving any indication of the error. How can I propagate this error to the Java calling layer?
UPDATE: Java calling layer:
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String connectionUrl = "jdbc:sqlserver://x.x.x.x;database=x";
try (Connection conn = DriverManager.getConnection(connectionUrl, "x", "x")) {
try (CallableStatement stmt = conn.prepareCall("{call proc1(?)}")) {
stmt.setInt(1, 1);
stmt.execute();
System.out.println("Done");
}
}
At the end, I can see the "Done" message printed to the console.
Add SET NOCOUNT ON as the first statement in the proc to suppress the DONE_IN_PROC (rowcount) TDS messages. Otherwise, the code will need to consume all results returned using a ResultSet and getMoreResults before the error is raised on the client.

Update command with special SQL keywords in JDBC

I am trying a update an entry in my SQL table which has a column name "from" in JDBC.
Following is the SQL command that I am trying to execute:
sql = "Update email_template set [from]="+"'"+3+"'"+" WHERE id="+idno;
stmt.executeUpdate(sql);
However it shows the following error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '[from]='Akshit' WHERE id=1' at line
MySQL's way of escaping column names is by using backticks:
sql = "Update email_template set `from`="+"'"+3+"'"+" WHERE id="+idno;
I recommend using java.sql.PreparedStatement when handling SQL in Java. It can be used for batches and ensures malicious SQL is not injected as part of the SQL code.
This is how your code looks with a PreparedStatement:
PreparedStatement stmt = connection.prepareStatement("UPDATE `email_template` SET `from` = ? WHERE id = ?");
stmt.setInt(1, 3);
stmt.setInt(2, idno);
stmt.executeUpdate();
If this is an operation you execute for many rows in one go, replace stmt.executeUpdate() with stmt.addBatch() (likely in some loop) and when you're ready to execute the batched updates you call stmt.executeBatch().
Note that both executeUpdate() and executeBatch() return how many rows were affected; which is something you may want to validate after a commit.

Can I execute multiple queries separated by semicolon with MySQL Connector/J? [duplicate]

This question already has answers here:
Multiple queries executed in java in single statement
(6 answers)
Closed 4 years ago.
My jdbc driver for mysql db is of version 5.1.25.
I want to execute sql query like so:
statement.execute("select fullName from user where user_id=1; select fullName from user where user_id=2");
And I always receive exception:
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select fullName from user where user_id=2' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4187)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4119)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2809)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2758)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:894)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:732)
at dbViewer.model.UserConnectionManager.retrieveRoutinesNames1(UserConnectionManager.java:622)
at dbViewer.model.UserConnectionManager.main(UserConnectionManager.java:637)
BUT when I run this same query(separated by semicolon) from command line it works perfectly and outputs two tables as expected.
Using ; in a query for most databases doesn't work as it is usually not part of the statement syntax itself, but a terminator for command line or script input to separate statements. The command line or script processor sees a semi-colon as the signal that the statement is complete and can be sent to the server.
Also in JDBC a single statement prepare (or execute) should only be one actual statement so multiple statements are not allowed and so there is also no need to have a semi-colon, and as for some (most?) databases the semi-colon isn't part of the statement syntax, it is simply a syntax error to have one included.
If you want to execute multiple statements, you need to use separate executes. Technically, MySQL does have an option to support multiple executions which can be enabled by a connection property. This behavior is not compliant with the JDBC specification/API and makes your code less portable. See allowMultiQueries on Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J
I want to execute sql query like so:
statement.execute("select fullName from user where user_id=1; select fullName from user where user_id=2");
This is possible only when you have set one database connection property to allow multiple queries to execute all at once. And the property name is allowMultiQueries=true. This property has to be set and send along with a database connection request to the server. General syntax is like this:
String dbUrl = "jdbc:mysql:///test?allowMultiQueries=true";
This is additional connection property to those if already exists some, like autoReConnect=true, etc.
Acceptable values for allowMultiQueries property are true, false, yes, and no. Any other value is rejected at runtime with an SQLException.
You have to use execute( String sql ) or its other variants to fetch results of the query execution.
multiQuerySqlString = "select fullName from user where user_id=1; ";
multiQuerySqlString += "select fullName from user where user_id=2; ";
// you can multiple types of result sets
multiQuerySqlString += "select last_login from user_logs where user_id=1; ";
boolean hasMoreResultSets = stmt.execute( multiQuerySqlString );
To iterate through and process results you require following steps:
int rsNumber = 0;
while ( hasMoreResultSets ) {
rsNumber += 1;
Resultset rs = stmt.getResultSet();
// based on the structure of the result set,
// you can handle column values.
if ( rsNumber == 1 ) {
while( rs.next() ) {
// handle your rs here
} // while rs
} // if rs is 1
else if ( rsNumber == 2 ) {
// call a method using this rs.
processMyResultSet( rs ); // example
} // if rs is 2
// ... etc
// check whether there exist more result sets
hasMoreResultSets = stmt.getMoreResults();
} // while results
Refer to:
Multiple queries executed in java in single statement
One of the similar postings on SO, for which I gave an answer.
No you can't. What are you expecting to get by calling statement.execute(...)?
It returns one ResultSet (... which means one table).
You can just call "select fullName from user where user_id in (1, 2)" to geht back both results.
Having semicolons in JDBC statements is very error prone in general. Some JDBC drivers do not support this (e.g. IBM's JDBC driver for DB2 10.x throws an exception if you close your SQL statement with ";").

Java PreparedStatement complaining about SQL syntax on execute()

This is driving me nuts... What am I doing wrong here?
ArrayList<String> toAdd = new ArrayList<String>();
toAdd.add("password");
try{
PreparedStatement pStmt = conn.prepareStatement("ALTER TABLE testTable ADD ? varchar(100)");
for (String s : toAdd) {
pStmt.setString(1, s);
pStmt.execute();
}
} catch (SQLException e) {
e.printStackTrace();
}
Results in...
02:59:12,885 ERROR [STDERR] com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''password' varchar(100)' at line 1
but...
ArrayList<String> toAdd = new ArrayList<String>();
toAdd.add("password");
try{
Statement stmt = conn.prepareStatement();
for (String s : toAdd) {
stmt.execute("ALTER TABLE testTable ADD "+s+" varchar(100)");
}
} catch (SQLException e) {
e.printStackTrace();
}
works perfectly... So does directly entering the text directly into the MySQL command line client.
mysql> alter table testTable add stringGoesHere varchar(100);
Query OK, 1 row affected (0.23 sec)
Records: 1 Duplicates: 0 Warnings: 0
What am I doing wrong?
The MySQL manual clearly says that ? (parameter markers) are for binding data values only, not for column names.
Parameter markers can be used only
where data values should appear, not
for SQL keywords, identifiers, and so
forth.
So you will have to use your second approach.
Placeholders in JDBC are for data, not for table, column, view or function names. And for good reason. The DB schema of an application is static most of the time and only changes rarely. There are no benefits making them dynamic.
Prepared statements need to define a fixed structure so they can be precompiled. That means you can have variable values, but never variable table names, column names, function names etc.
When using a prepared statement, your parameter is treated similarily to a string literal. As a result, your statement is equivalent to "ALTER TABLE testTable ADD \'"+s+"\' varchar(100)". Notice the single quotations around the field name in the error message.
I would suggest building a literal statement in this case, and not attempting to used a prepared statement.
You cannot submit an ALTER TABLE statement using parameters like this.
I guess it is not permissible to execute DDL statements in Java PreparedStatement.
Try using the following
pStmt.executeUpdate();
pStmt.close();

Categories

Resources