Many delete queries in single JDBC query - java

I'm having a problem with in my class with generating delete command:
private String generateDeleteCommand() {
StringBuilder deleteCommand = new StringBuilder();
for (ForeignKey fk : exportedForeignKeys) {
deleteCommand.append("DELETE FROM ").append(fk.foreignTableName)
.append(" WHERE ").append(fk.foreignColumnName)
.append("=:").append(fk.primaryColumnName).append(";\n");
}
deleteCommand.append("DELETE FROM ").append(tableName)
.append(" WHERE ");
for (String key : primaryKeys.keySet()) {
deleteCommand.append(key).append("=:").append(key).append(" AND ");
}
deleteCommand
.delete(deleteCommand.length() - 5, deleteCommand.length());
deleteCommand.append(";");
System.out.println(deleteCommand);
return deleteCommand.toString();
}
The query I get is valid when using in phpmyadmin - but when I'll try to use it with jdbc executeUpdate() i get MySQLSyntaxError, i.e. for table "trasy" with two exported foreign keys query looks like:
DELETE FROM kursy WHERE ID_TRASY=19;
DELETE FROM przystanki WHERE ID_TRASY=19;
DELETE FROM trasy WHERE ID_TRASY=19;
Exception:
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 'DELETE FROM przystanki WHERE ID_TRASY=19;
DELETE FROM trasy WHERE ID_TRASY=19' at line 2
It doesn't matter whether there is \n between the queries or no.

Use the .addBatch() method on Statement and add each DELETE query separately and .executeBatch() them as a batch.
If using transactions, you can test the counts returned by each statement, and if anything is wrong, you can .rollback() the entire batch.
I have an open source project that shows exactly how to do this.
SQL Construction Kit on GitHub, there is an AbstractDatabase.java class that has an .executeBatch() method that you can copy and use yourself with very little modification. It even has the code for testing each command and doing the commit/rollback.

jdbc executes one statement at a time, so your statements, even if are multiple seperated by \n, are in fact executed as one instruction hence the error from mysql.

Related

Spring Boot schema.sql, initializing database using PL/SQL

I am working on a project where I have to use Oracle Database 12c and I have to write all queries manually (so I can't use Spring Data).
For creating all tables and relationships, I use schema.sql and for template data I use data.sql.
And I have a problem with checking if table or data already exists.
In MySQL creating table would be like "create table if not exists".
In PL/SQL unfortunately, there is no equivalent for "if not exists". I replaced this functionality by:
begin
execute immediate
'CREATE TABLE user_data (
some data
)';
exception when others then if SQLCODE = -955 then null; else raise; end if;
end;
And it works when I run this script in SQL Developer or in Intellij's SQL console but the problem occurs when I want to run an application and Spring Boot tries to execute a script from schema.sql.
Output in terminal tells that:
nested exception is java.sql.SQLException: ORA-06550: line 8, column 4:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
* & = - + ; < / > at in is mod remainder not rem return
returning <an exponent (**)> <> or != or ~= >= <= <> and or
like like2 like4 likec between into using || multiset bulk
member submultiset
So it looks like Spring Boot doesn't know that it should run statement between "begin" and "end".
Any idea how can I manage the problem with database initialization ?
As a workaround, I could drop tables with every application run but it is not an optimal solution (and it wouldn't work when someone run the application for the first time).
Firstly, I would like to share two topics that seem to be relevant to this problem:
Unable to use "DROP TABLE IF EXISTS" in schema.sql for a Spring Boot application
executeSqlScript fails with Spring for PL/SQL block
There you will find a solution that should work: create a stored procedure and use in your schema.sql statement like
call recreate_table('USER_DATA','CREATE TABLE USER_DATA (SOME DATA)');
CALL statement is widely used across different databases, shortened to statement with only one semicolon and thus works well.
Secondly, I may only suppose, that the main problem is that anonymous blocks in PL/SQL (as well as other complex enough statements that may contain more than one semicolon) should be finished by a / character. I would recommend you to try to append this character to the end of your script, take a look at this and this answers, and if it does not work, create a stored procedure.
Also note that there is another way to check existence of the table (that comes over this wait for an exception pattern):
select count(*)
from user_tables t
where t.table_name = 'USER_DATA'
and rownum < 2

JDBC throws MySQL syntax Exception, despite valid query

Hello helpful folks,
I've a small part in an application which creates a new db schema based on a mysqldump.
The relevant part looks like this:
log.info("= setup basic database");
// Execute each command in the dump
for (String query : dump.split(";")) {
statement.execute(query);
}
This works fine but some statements just break with something like
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 ''ot_subtotal.php' at line 1
The query in this case is
INSERT INTO `configuration` VALUES (194,'MODULE_ORDER_TOTAL_INSTALLED','ot_subtotal.php;ot_discount.php;ot_coupon.php;ot_shipping.php;ot_cod_fee.php;ot_gv.php;ot_subtotal_no_tax.php;ot_tax.php;ot_total_netto.php;ot_total.php',6,0,NULL,'2016-06-17 15:27:24',NULL,NULL);
If I replace the string "'ot_subtotal.php;ot_discount.php;ot_coupon.php;ot_shipping.php;ot_cod_fee.php;ot_gv.php;ot_subtotal_no_tax.php;ot_tax.php;ot_total_netto.php;ot_total.php'" with something like "hello world" it works and breaks at a another query later on.
The thing is, I'm able to import the dump via the terminal command "mysql ... < dump.sql" without a problem.
Any ideas?
This is happening because of
for (String query : dump.split(";")) {
statement.execute(query);
}
You are splitting at ; which result in incomplete queries.
For example:
INSERT INTO `configuration` VALUES (194,'MODULE_ORDER_TOTAL_INSTALLED','ot_subtotal.php;ot_discount.php;ot_coupon.php;ot_shipping.php;ot_cod_fee.php;ot_gv.php;ot_subtotal_no_tax.php;ot_tax.php;ot_total_netto.php;ot_total.php',6,0,NULL,'2016-06-17 15:27:24',NULL,NULL);
Above query after split at ; will be
INSERT INTO `configuration` VALUES (194,'MODULE_ORDER_TOTAL_INSTALLED','ot_subtotal.php
Which is incorrect.
Instead of
dump.split(";")
Use
dump.split(";\n")
If you created the dump file in normal way.

newline charter \n gives "java.sql.SQLException: ORA-00911: invalid character\n" Oracle 11g

I have Oracle DB 11g Enterprise Edition and I want to create a table by reading the sql script from a file.Through java code I am reading following sql script from a file and storing it in a String sqlBlock:
CREATE SEQUENCE VerHist_SeqNum
START WITH 1
INCREMENT BY 1;
CREATE TABLE VerHist
(
SequenceNumber NUMBER(10,0) NOT NULL,
SQLFileName VARCHAR2(100) NOT NULL,
STATUS VARCHAR2(10) NOT NULL,
AppliedDate DATE NOT NULL,
DateCreated DATE
DEFAULT (SYSDATE),
DateUpdated DATE
DEFAULT (SYSDATE),
CONSTRAINT PK_VerHist PRIMARY KEY( SequenceNumber ),
CONSTRAINT UC_VerHist_SQLFileNa UNIQUE( SQLFileName )
);
CREATE OR REPLACE TRIGGER VerHist_SeqNum_TRG
BEFORE INSERT
ON VerHist
FOR EACH ROW
BEGIN
SELECT VerHist_SeqNum.NEXTVAL INTO :NEW.SequenceNumber
FROM DUAL;
END;
When I execute this query it gives
java.sql.SQLException: ORA-00911: invalid character\n
at
oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331) at
oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288) at
oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:743) at
oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:207) at
oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:946)
at
oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1168)
at
oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:1687)
at
oracle.jdbc.driver.OracleStatement.execute(OracleStatement.java:1653)
Following is my code to execute the sql block:
Statement stmt = conn.createStatement();
String sqlBlock = //"this contains the content of the file (it contains \n charters)";
stmt.execute(sqlBlock);
Is the newline charter invalid here, if yes, how to get this working otherwise?
Please note that when I copy paste the contents of this file and run the script through Oracle SQL Developer it runs fine.
I think the \n reference is a red-herring, and an artefact of how the error is being logged. You're trying to run two SQL statements, separated by a semi-colon, in one execute. That is not allowed. The semi-colon is a statement separator in SQL*Plus, not in SQL, and will generate an ORA-00911 even with a single statement. And execute has to be a single statement.
If you were doing DML you could wrap the statements in a PL/SQL block and execute that, but since this is DDL you can't do that unless you resort to dynamic SQL, which is going to be overly complicated and messy for what you're trying to do.
You need to put each statement in a separate file (without the trailing semi-colon on the create sequence; you still need it on the create trigger because there it is ending the trigger's PL/SQL block, not acting as a statement separator - confusing, I know), and read and execute them separately. Then each execute has a single statement, and will be much happier.
As an aside, you don't need to select your sequence value into your variable in 11g; you can now do it like this:
CREATE OR REPLACE TRIGGER VerHist_SeqNum_TRG
BEFORE INSERT
ON VerHist
FOR EACH ROW
BEGIN
:NEW.SequenceNumber := VerHist_SeqNum.NEXTVAL;
END;
When you copy paste the contains of the file to browser. Browser will treat \n as new line.
Whereas for the code /n is character only.
Try to replace \n with single space and then run it will work
sqlBlock = sqlBlock.replaceAll("\n"," ");
Remove the \n from the query. If you want the query to be formatted in sql developer, you can select the query and press Ctrl+F7

A query works in SQL*Plus but fails in JDBC with an ORA-00911 exception

I'm trying to track the amount of redo being generated during a database session with the following query:
SELECT a.name, b.VALUE
FROM v$statname a, v$mystat b
WHERE a.statistic# = b.statistic# AND a.name = 'redo size';
This query works directly in SQL*Plus and Toad, but I get an ORA-00911 exception using JDBC, and I've narrowed it down to the "statistic#" column name.
How do I get around this?
The column name statistic# is not the problem.
My bet is that you also send the terminating ; from inside your Java program.
But you may not include the the ; when executing a SQL statement through JDBC (at least not in Oracle and some other DBMS).
Remove the ; from your SQL String and it should be fine.
put it in double quotes - that should let you call a field anything in Oracle
Switch on JDBC logging, check your driver documentation for how to do this. In the JDBC log you see the actual statement prepared and executed in the DB. This eliminates one possible cause for the error.

SELECT * FROM table throws error in JDBC

I am trying to make a select from a table, which works fine with every other table in my database, but when I try the following I recieve an error:
db.makeQuery("SELECT * FROM References");
Which calls:
public ResultSet makeQuery(String query) throws Exception
{
preparedStatement = connect.prepareStatement(query);
resultSet = preparedStatement.executeQuery(query);
return resultSet;
}
It then throws the following error:
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 'References' at line 1
I seems very strange to me, since this statement works:
db.makeQuery("select * from Products");
References is a keyword in SQL, so you better avoid it for table names. (See for instance this documentation.)
As suggested by Nishant, you can use reserved words in queries if you escape them with backticks.
Related question:
Using MySQL keywords in a query?
use
db.makeQuery("SELECT * FROM `References`");
if you can, better, avoid having names that are MySQL keywords. As suggested by aioobe
You might be misspelling the name of your table. MySQL gives this error when it can't find that table you're referring to.
Use SHOW TABLES; to see the names of the tables in your database, and double-check the name.

Categories

Resources