How to create a mySQL stored procedure in a JdbcTemplate - java

Background
To work around the issue in MySql that certain statements are only permitted within a stored procedure I'm trying to create, run, then drop a stored procedure within sql submitted by a JdbcTemplate. A simplied example would be (this happens to be within spring boot):
#Service
public class StartupDatabaseCheck {
private JdbcTemplate template;
#Autowired
public StartupDatabaseCheck(JdbcTemplate template){
this.template = template;
}
#PostConstruct
public void init() {
log.info("Running custom fields table creation (if required)");
try {
String migrateSql = Resources.toString(Resources.getResource("migrateScript.sql"), Charsets.UTF_8);
template.execute(migrateSql);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Where migrateScript.sql is
DELIMITER //
CREATE PROCEDURE migrate()
BEGIN
IF ((SELECT count(1)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'custom_field_instance_data'
and column_name='entity_id' and is_nullable = false) > 0)
THEN
alter table custom_field_instance_data MODIFY COLUMN entity_id char(32) null;
END IF;
END //
DELIMITER ;
call migrate;
drop procedure migrate;
Running this within mySql workbench works fine, but submitted by the JdbcTemplate I get the 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 'CREATE PROCEDURE migrate_custom_fields()
As I understand it thats because those DELIMITER statements are not permitted by JdbcTemplate but just removing them as suggested in that link leads to other syntax errors
Question
How can a mySQL stored procedure be created (or statements usually only allowed with a stored procedure be executed) by a JdbcTemplate
Notes
The error without the deliminator statements is
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 'CREATE PROCEDURE migrate_custom_fields()

It seems that the driver is not taking the delimited queries into the account.If you want to create an stored procedure on fly using the jdbc.
Using the following property and pass it as the connection parameter in the URL.
jdbc:mysql://localhost:3306/test?allowMultiQueries=true
The above property will allow ';' delimited queries.
You can find more on this at here
Create MySQL stored procedure using JPA Hibernate
The updated migrateScript.sql in this case would be
drop procedure IF EXISTS migrate_custom_fields;
CREATE PROCEDURE migrate_custom_fields()
BEGIN
IF ((SELECT count(1)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'custom_field_instance_data'
and column_name='entity_id' and is_nullable = false) > 0)
THEN
alter table custom_field_instance_data MODIFY COLUMN entity_id char(32) null;
END IF;
END ;
call migrate_custom_fields;
drop procedure migrate_custom_fields;

Related

Unable execute this oracle procedure with springframework JdbcTemplate in Java? Error: Cannot perform fetch on a PLSQL statement :next;

I'm trying to execute a Oracle procedure using spring JdbcTemplate but getting following error :
Cannot perform fetch on PLSQL statement: next;
This is the oracle procedure
create or replace PROCEDURE PROCEDURE_NAME
AS
cursorname SYS_REFCURSOR;
variablename VARCHAR2(250);
BEGIN
INSERT INTO tablename
SELECT RTRIM(FIELDNAME)
RTRIM(FIELDNAME2)
FROM tablename2;
WHERE OTHERFIELDNAME='CONDITIONSTRING'
OPEN CURSORNAME FOR
SELECT *
FROM tablename;
DBMS.SQL.RETURN_RESULT(cursorname);
EXCEPTION WHEN OTHERS THEN
variablename := SQLERRM;
INSERT INTO debuggingtablename
VALUES ('labelname', systimestamp, 'labelname2failname' || substr(variablename,1,120));
RETURN;
END;
And I'm calling it as
import org.springframework.jdbc.core.JdbcTemplate;
public Classname {
private JdbcTemplate jdbcTemplate;
public List<Map<String,Object>> getdata(){
return jdbcTemplate.queryForList("CALL PROCEDURE_NAME()",new Object[]{});
}
I'm not allowed to edit/modify the procedure code.
If there is no solution without modifying the procedure is there anyway I can get the output in List<Map<String,Object>> format where column name and values are mapped to each other.
This procedure does not take any input but some other procedure might do in future.
PS: I have simplified the program so ignore any grammar mistakes.

Create MySQL stored procedure using JPA Hibernate

I'm trying to create a stored procedure in a MySQL database using the contents of a text file:
USE myDatabase;
DROP PROCEDURE IF EXISTS myStoredProcedure;
DELIMITER $$
CREATE PROCEDURE myStoredProcedure
(
_description VARCHAR(50),
_value INT
)
BEGIN
INSERT INTO myTable
(
description,
value
) VALUES (
_description,
_value
);
SELECT
id,
description,
value
FROM myTable
WHERE id = LAST_INSERT_ID();
END;
$$
DELIMITER ;
I execute the SQL using a native query:
Query query = entityManager.createNativeQuery(queryText);
...
query.executeUpdate();
But it gets an error on the DROP PROCEDURE
I commented out the DROP PROCEDURE and then it gets an error on the DELIMITER
Basically, it gets an error on any line after the first semicolon.
It seems as if JPA hibernate is parsing my query and telling me there's a problem with it rather than passing the unadulterated text onto MySQL.
The sql runs in MySQL without error.
I can't find anything in Google about creating a stored procedure with JPA, only calling one.
Does anyone have any insight on what I might be doing wrong? Or if this is even possible.
This can be possible if you mention the following property in the url
spring.datasource.url=jdbc:mysql://localhost:3306/test?allowMultiQueries=true
The allowMultiQueries will instruct the driver to sent delimited queries to the database.
Please note that if you are using native queries be-aware of sql injection attack.
You dont need to put the delimiter(DELIMITER) explicitly.The sql statement
The following query works
SET myDatabase;
DROP PROCEDURE IF EXISTS myStoredProcedure;
CREATE PROCEDURE myStoredProcedure ( _description VARCHAR(50), _value INT )
BEGIN
INSERT INTO
myTable ( description, value )
VALUES ( _description, _value );
SELECT id, description, value
FROM myTable
WHERE id = LAST_INSERT_ID();
END;

How to create mysql stored procedure using jdbc

How can I create a stored procedure using JDBC? I've tried the following code, but it fails with errors.
Contents of the 'sql' variable which is used in jdbc code
CREATE PROCEDURE 'init'()
BEGIN
DECLARE '_rollback' BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET '_rollback' = 1;
START TRANSACTION;
DROP TABLE IF EXISTS tst;
CREATE TABLE IF NOT EXISTS tst
(
did INT AUTO_INCREMENT PRIMARY KEY,
stage INT
);
INSERT INTO tst (stage) VALUES (11);
INSERT INTO tst (stage) VALUES (23);
IF '_rollback'
THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;
JDBC Code:
Statement statement = connection.createStatement();
statement.executeLargeUpdate(sql);
JDBC Error:
java.sql.SQLSyntaxErrorException: 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_and_populate_schema'()
BEGIN
DECLARE '_rollback' BOOL DEFAULT 0;
' at line 1
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:536)
I hope DELIMITER should not be set because it only works inside 'mysql' client and it seems that there is no any alternative with sql-String that is supposed to be used with JDBC

Teradata JDBC executeBatch errorhandling

I am inserting data into a teradata table using executeBatch method. Currently if one insert in the batch fails all the other inserts in the batch also fails and no records end up being inserted. How can I change this behaviour to let the other inserts in the batch succeed if any inserts fails and the some ability to track the rejected records.
PS: I have ensured that TMODE is set to TERA and autocommit enabled.
UPDATE:
target table definition.
CREATE SET TABLE mydb.mytable ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
col1 INTEGER,
col2 VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL)
PRIMARY INDEX ( col1 );
Below is the sample scala code. As you can see, this batch contains 5 insert statements. The First insert is set to fail because it is trying to insert null into an not null field (col2). The other 4 inserts dont have any issues and should succeed. But as you can see from below all 5 inserts in the batch failed. Is there any way we can make other inserts succeed?. As stated above tmode is tera and autocommit is enabled. if there is no way other than re-submitting all failed queries individually then we would have to reduce the batch size and settle for lower throughput.
Class.forName("com.teradata.jdbc.TeraDriver");
val conn = DriverManager.getConnection("jdbc:teradata://teradata-server/mydb,tmode=TERA","username","password")
val insertSQL = "INSERT INTO mydb.mytable VALUES (?,?)"
val stmt = conn.prepareStatement(insertSQL)
stmt.setInt(1,1)
stmt.setNull(2,Types.VARCHAR) // Inserting Null here. This insert will fail
stmt.addBatch()
stmt.setInt(1,2)
stmt.setString(2,"XXX")
stmt.addBatch()
stmt.setInt(1,3)
stmt.setString(2,"YYY")
stmt.addBatch()
stmt.setInt(1,4)
stmt.setString(2,"ZZZ")
stmt.addBatch()
stmt.setInt(1,5)
stmt.setString(2,"ABC")
stmt.addBatch()
try {
val res = stmt.executeBatch()
println(res.mkString(","))
}
catch {
case th: BatchUpdateException => {
println(th.getUpdateCounts().mkString(","))
}
}
Result
-3,-3,-3,-3,-3
This is from Teradata's JDBC manual:
Beginning with Teradata Database 13.10 and Teradata JDBC Driver
13.00.00.16, PreparedStatement batch execution can return individual success and error conditions for each parameter set.
An application using the PreparedStatement executeBatch method must
have a catch-block for BatchUpdateException and the application must
examine the error code returned by the BatchUpdateException
getErrorCode method.
PreparedStatement BatchUpdateException Handling
Execute a multi-statement request using a PreparedStatement batch request and demonstrates the handling of the PreparedStatement BatchUpdateException

com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near '|'

There are two database connection in project.
1. oracle database
2. mssql database
Database connection is OK. Issue is when data transferred/inserted in one database[oracle] then it is display error and same time data inserted in another database[mssql] successfully.Insert query is fine but there is another query which is generate sequesnce number. There have problem.
This is the query which belongs to oracle database
return jdbcTemplate.queryForObject("SELECT 'AK'||LPAD(adds_seq.NEXTVAL,13, '0') adds_seq_no FROM sys.dual ",String.class);
Error is :
SELECT 'AK'||LPAD(adds_seq.NEXTVAL,13, '0') adds_seq_no FROM sys.dual ",String.class ]; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near '|'.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:98)
It is working well before when there is no mssql connection.
Below query is for oracle database. When I tried to change code like this:
String sql = " SELECT 'AK'||LPAD(adds_seq.NEXTVAL,13, '0') adds_seq_no FROM sys.dual";
String adSeqNum = null;
try {
adSeqNum = jdbcTemplate.queryForObject(sql, String.class);
} catch (Exception e) {
e.printStackTrace();
}
return addSeqNum;
then the error is
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL []; ORA-01400: cannot insert NULL into ("ADDSS_HST"."ADDS_SEQ_NO")
1.Can you guide me how to solve this issue?
2.Can you please give me example for how to do separate database connection on one java file?
The "Incorrect syntax near '|'" happens because you are sending the Oracle statement "SELECT 'AK'||LPAD(adds_seq.NEXTVAL,13, '0') adds_seq_no FROM sys.dual " to the MSSQL server connection.
The second error, cannot insert null into "ADDSS_HST"."ADDS_SEQ_NO" I suspect happens because before the posted sql that selects from the sequence, you are inserting records into ADDSS_HST table. Is this correct? If so, I recommend that you put the code that generates adds_seq_no into a trigger like the one below (adjust names for your needs):
CREATE OR REPLACE TRIGGER "APPLICATION_BI_TRG"
BEFORE INSERT ON APPLICATION
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
BEGIN
if :new.application_id is null then
// if no value was given in insert statement for column application_id
SELECT APP_WEB_ID_SEQ.NEXTVAL INTO :NEW.application_id FROM dual;
// select a value from sequence into :NEW.application_id
end if;
END;
/

Categories

Resources