MyBatis executing multiple sql statements in one go, is that possible? - java

i was wondering if it is possible to execute multiple sql statements in 1 go.
For example the scenario that i want to delete rows from multiple tables, is there a way i can do things like..
<delete id="delete" parameterType="String">
DELETE FROM DUMMYTABLE_A where X=${value}
DELETE FROM DUMMYTABLE_B where X=${value}
</delete>

I'm using myBatis with Oracle. I guess there is something similar in other DB. Actually you always can create procedures in DB, which usually is better for the future, when you have to support the project.
<delete id="deleteUnfinishedData" parameterType="map">
{call
declare
begin
delete from TABLE1 where id = #{valueFromMap1};
delete from TABLE2 where id = #{valueFromMap2};
end
}
</delete>

Yes, most databases allow this. Usually you have to delimit your SQL statements with something. In PostGRES and MySQL it's a semicolon (;). In Microsoft SQL server you should use the keyword GO. [ May 2013 Update: As of SQL Server 2012, you can and should use semicolons to delimit your statements. After SQL Server 2012 (ie. the next version and beyond) these will be mandatory. Using GO is now the deprecated way to do things in SQL2012 and beyond). ]
MySQL / PostGRES example:
DELETE FROM DUMMYTABLE_A where X=${value};
DELETE FROM DUMMYTABLE_B where X=${value};
DELETE FROM DUMMYTABLE_C where X=${value};
MS-SQL example:
DELETE FROM DUMMYTABLE_A where X=${value}
GO
DELETE FROM DUMMYTABLE_B where X=${value}
GO
DELETE FROM DUMMYTABLE_C where X=${value}
Better databases (ie. not MySQL) will also support transactions with BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN. Using transactions you can actually batch all the statements into one atomic operation, where if part of it failed, all three would be rolled back. See http://www.sqlteam.com/article/introduction-to-transactions for some more information about those.
Most likely all you need is the semicolons between your SQL statements though!

if anyone got an error like
Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL
syntax; check the manual that corresponds to your MariaDB server
version for the right syntax to use near 'UPDATE mytable
You can fix this by allowing multi queries in your driver.
For mariadb it would be the same as MySQL
allowMultiQuery=true
described in following mybatis issue
https://github.com/mybatis/mybatis-3/issues/1497

This code works for multiple Select in one go in MSSQL:
<select id="selectMultipleQueries" resultSets="movies,genres" resultMap="multipleQueriesResult">
BEGIN
select M.ID_ as mId,
M.NAME_ as mName,
from TestMyBatis.dbo.Movie as M
where M.ID_ = #{id,jdbcType=INTEGER,mode=IN};
select G.ID_ as gId,
G.NAME_ as gName
from TestMyBatis.dbo.Genre as G
where G.ID_ = #{id,jdbcType=INTEGER,mode=IN};
END
</select>

Related

How I can Export ddl of all objects like table, index etc in SYBASE IQ/ SYBASE ASE without using any tools?

I have created a sample program in which I want to get ddl of all objects like table, trigger etc using get_ddl method. When I tried following queries in oracle it worked.
SELECT DBMS_METADATA.GET_DDL('TABLE', TABLE_NAME) FROM USER_TABLES;
SELECT DBMS_METADATA.GET_DDL('TRIGGER', TRIGGER_NAME) FROM USER_TRIGGERS;
SELECT DBMS_METADATA.GET_DDL('VIEW', VIEW_NAME) FROM USER_VIEWS;
SELECT DBMS_METADATA.GET_DDL('FUNCTION', OBJECT_NAME) FROM USER_PROCEDURES WHERE OBJECT_TYPE = 'FUNCTION';
SELECT DBMS_METADATA.GET_DDL('PROCEDURE', OBJECT_NAME) FROM USER_PROCEDURES WHERE OBJECT_TYPE = 'PROCEDURE';
SELECT DBMS_METADATA.GET_DDL('INDEX', INDEX_NAME) FROM USER_INDEXES ;
But when I try to create same sample for sybase to get ddl or script of all objects it doesn't work.because get_ddl not supported in sybase database. Can anyone help me to know that whether sybase Iq 15 supports get_ddl methods or there are any other method/way or queries for creating ddl/script of all objects.
I want to post it on SAP forums but all sites are unavailable can anyone suggest me link for post my problem.
Thanks in Advance!!
The ddl for triggers, stored procedures, and views can be pulled from sys.syssource. Unfortunately IQ doesn't store ddl for other objects
For ase,
Use sybsystemprocs
Go
sp_helptext (object)
Go
For views, stored procedures, and triggers

Hibernate multiple native SQL statements

I want to run a native SQL from a file using Hibernate. The SQL can contain several statements creating the database structure (i.e. tables, constraints but no insert/update/delete statements).
Example, very simple query is below (which contains the following two SQL statements)
CREATE DATABASE test;
CREATE TABLE test.testtbl( id int(5));
I am using MySQL db, and when I run the above query I am gettng syntax error returned. When I run them one by one, its ok.
Caused by: 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 TABLE test.testtbl( id int(5))' at line 1
The code to run the query is below (above statement is assigned to 'sql' variable):
session = sf.openSession();
session.beginTransaction();
Query qry = session.createSQLQuery(sql);
qry.executeUpdate();
session.getTransaction().commit();
Any help would be appreciated.
As others have explained
You must run these queries one by one.
The hibernate code gets translated into running one update statement on JDBC.
But you provided two update statements.
In addition,
I personally prefer to have the code that creates tables outside of the Java application, in some DB scripts.
The parameters of the method createSQLQuery is t-sql code;
t-sql code to ensure that in the mysql interface analyzer correctly.
You can try changed the sql :'CREATE TABLE testtbl(id int(5));'
by the way you can use JDBC Connection api (Don't recommend to do so)
Such as:
java.sql.Connection conn=session.connection();

Getting MySQLSyntaxErrorException?

I have this code :
String check="SELECT COUNT(*) as check FROM recordstudent WHERE STUDENT_ID="+T_STUDENT_ID+" AND COURSE_ID="+T_COURSE_ID+" AND PACKAGE_ID="+T_PACKAGE_ID+" AND ACTIVITY_ID="+T_ACTIVITY_ID+" AND DATE="+T_DATE+ ";";
rs=myStmt.executeQuery(check);
int ch=0;
while(rs.next()){
ch=Integer.parseInt(rs.getString("check"));
}
if(ch==0)
{
String insertRecord="insert into recordstudent"+
"(STUDENT_ID,COURSE_ID,PACKAGE_ID,ACTIVITY_ID,TEST_NAME,DATE,SCORE,TOTAL_MARKS,PERCENTAGE,CORRECT_ANSWER,TOTAL_QUESTIONS,STUDENT_NAME,SCORE_PER_DIVISION,ATTEMPTS)"+
"VALUES("+
"'"+T_STUDENT_ID+"',"+
"'"+T_COURSE_ID+"',"+
"'"+T_PACKAGE_ID+"',"+
"'"+T_ACTIVITY_ID+"',"+
"'"+T_TEST_NAME+"',"+
"'"+T_DATE+"',"+
"'"+T_SCORE+"',"+
"'"+T_TOTAL_MARKS+"',"+
"'"+T_PERCENTAGE+"',"+
"'"+T_CORRECT_ANSWERS+"',"+
"'"+T_TOTAL_QUESTIONS+"',"+
"'"+T_STUDENT_NAME+"',"+
"'"+T_SCORE_PER_DIVISION+"',"+
"'"+t+"'"
+");";
myStmt.execute(insertRecord);
}
This snippet should insert the data in database only if the ch=0 .But I am getting this 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
'check FROM recordstudent WHERE STUDENT_ID=11 AND COURSE_ID=2 AND PACKAGE_ID=11 A'
at line 1
Can Anyone help me and solve my problem ?
Fundamentally: don't build your SQL this way. I notice that you've put quotes round the values in the "insert" SQL statement - but not in the "select" one. That's the start of the problem - but you shouldn't be including values like this in your SQL to start with. You should use parameterized SQL via PreparedStatement, and set values for the parameters. Benefits:
You can see your actual SQL more easily, so you'll be able to spot syntax errors. (This is basically keeping your code separate from your data.)
(Very important) You won't be open to SQL injection attacks
You won't need to worry about conversion issues for numbers, dates and times etc
There are other problems in your SQL (such as spaces and check being a reserved word in MySQL), but the very first thing you should fix is how you use values. Until you've done that, your code is inviting security problems.
(You should then start using more conventional variable names than T_STUDENT_NAME etc, but that's a different matter.)
check is a reserved word. Surround it with backticks: `check`
Try this
SELECT COUNT(*) as 'check' FROM recordstudent....
instead of
SELECT COUNT(*) as check FROM recordstudent....
I think check is a keyword

JPA (Hibernate) Native Query for Prepared Statement SLOW

Having strange performance issue using Hibernate 3.3.2GA behind JPA (and the rest of the Hibernate packages included in JBoss 5.)
I'm using Native Query, and assembling SQL into a prepared statement.
EntityManager em = getEntityManager(MY_DS);
final Query query = em.createNativeQuery(fullSql, entity.getClass());
The SQL has a lot of joins, but is actually very basic, with a single parameter. Like:
SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on
WHERE stringId like ?
and the query runs in under a second on MSSQL Studio.
If I add
query.setParameter(0, "ABC123%");
The query will pause for 9 seconds
2012-01-20 14:36:21 - TRACE: - AbstractBatcher.getPreparedStatement:(484) | preparing statement
2012-01-20 14:36:21 - TRACE: - StringType.nullSafeSet:(133) | binding 'ABC123%' to parameter: 1
2012-01-20 14:36:30 - DEBUG: - AbstractBatcher.logOpenResults:(382) | about to open ResultSet (open ResultSets: 0, globally: 0)
However, if I just replace the "?" with the value (making it not a Prepared Statement, but just a straight SQL query.
fullSql = fullSql.replace("?", "'ABC123%'");
the query will complete in less that a second.
I would really prefer to us a Prepared Statement (the input for the parameters is being extracted from user data) to prevent injection attacks.
Tracing down the slow point in the code, I arrived deep within the jtds-1.2.2 package. The offending line seems to be SharedSocket line 841 "getIn().readFully(hdrBuf);" Nothing really obvious there though...
private byte[] readPacket(byte buffer[])
throws IOException {
//
// Read rest of header
try {
getIn().readFully(hdrBuf);
} catch (EOFException e) {
throw new IOException("DB server closed connection.");
}
Arrived to through this stack...
at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:841)
at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:722)
at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466)
at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103)
at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88)
at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3928)
at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1045)
at net.sourceforge.jtds.jdbc.TdsCore.microsoftPrepare(TdsCore.java:1178)
at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareSQL(ConnectionJDBC2.java:657)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:776)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1808)
at org.hibernate.loader.Loader.doQuery(Loader.java:697)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.doList(Loader.java:2228)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125)
at org.hibernate.loader.Loader.list(Loader.java:2120)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:312)
at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1722)
at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:175)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:67)
I'll leave this question and answer out here in case anyone has the same issue in the future.
The issue is in the way the JTDS drivers send the parameter strings to MSSQL. Apparently Java will attempt to send the parameters Unicode by default, and MSSQL will translate it to Ascii. Why that takes 9 seconds, I do not know.
Lot's of references to this out there, but nothing that helped my till I was able to isolate that it was an issue with the driver to MSSQL connection.
This link was helpful:
[http://server.pramati.com/blog/2010/06/02/perfissues-jdbcdrivers-mssqlserver/]
This is the string using the Microsoft driver.
jdbc:sqlserver://localhost\SQLEXPRESS;
DatabaseName=TESTDB;
sendStringParametersAsUnicode=false
You just need to get the sendStringParametersAsUnicode=false passed to your driver URL setup and you are good.
Check the query plans that SQL server is producing. Prepared statements can be especially problematic.
Let me explain...
If you do this:
SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on
WHERE stringId like 'ABC123%';
and you have an index on "stringId" SQL server knows it can use it.
However if you do this:
SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on
WHERE stringId like ?;
SQL server doesn't know it can use the index when it creates the prepared statement (as you could fill in the parameter with '%ABC123' instead of 'ABC123%') and thus may choose a completely different query plan.
And another answer for people potentially using Oracle with a similar Unicode problem...
Check to make sure someone hasn't set the property oracle.jdbc.defaultNChar=true
This is sometimes done to resolve unicode problems but it means all columns are treated as nvarchars. If you have an index on a varchar column, it won't be used because oracle has to use a function to convert the character encoding.

How can I generically detect if a database is 'empty' from Java

Can anyone suggest a good way of detecting if a database is empty from Java (needs to support at least Microsoft SQL Server, Derby and Oracle)?
By empty I mean in the state it would be if the database were freshly created with a new create database statement, though the check need not be 100% perfect if covers 99% of cases.
My first thought was to do something like this...
tables = metadata.getTables(null, null, null, null);
Boolean isEmpty = !tables.next();
return isEmpty;
...but unfortunately that gives me a bunch of underlying system tables (at least in Microsoft SQL Server).
There are some cross-database SQL-92 schema query standards - mileage for this of course varies according to vendor
SELECT COUNT(*) FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_TYPE] = <tabletype>
Support for these varies by vendor, as does the content of the columns for the Tables view. SQL implementation of Information Schema docs found here:
http://msdn.microsoft.com/en-us/library/aa933204(SQL.80).aspx
More specifically in SQL Server, sysobjects metadata predates the SQL92 standards initiative.
SELECT COUNT(*) FROM [sysobjects] WHERE [type] = 'U'
Query above returns the count of User tables in the database. More information about the sysobjects table here:
http://msdn.microsoft.com/en-us/library/aa260447(SQL.80).aspx
I don't know if this is a complete solution ... but you can determine if a table is a system table by reading the table_type column of the ResultSet returned by getTables:
int nonSystemTableCount = 0;
tables = metadata.getTables(null, null, null, null);
while( tables.next () ) {
if( !"SYSTEM TABLE".equals( tables.getString( "table_type" ) ) ) {
nonSystemTableCount++;
}
}
boolean isEmpty = nonSystemTableCount == 0;
return isEmpty;
In practice ... I think you might have to work pretty hard to get a really reliable, truly generic solution.
Are you always checking databases created in the same way? If so you might be able to simply select from a subset of tables that you are familiar with to look for data.
You also might need to be concerned about static data perhaps added to a lookup table that looks like 'data' from a cursory glance, but might in fact not really be 'data' in an interesting sense of the term.
Can you provide any more information about the specific problem you are trying to tackle? I wonder if with more data a simpler and more reliable answer might be provided.
Are you creating these databases?
Are you creating them with roughly the same constructor each time?
What kind of process leaves these guys hanging around, and can that constructor destruct?
There is certainly a meta data process to loop through tables, just through something a little more custom might exist.
In Oracle, at least, you can select from USER_TABLES to exclude any system tables.
I could not find a standard generic solution, so each database needs its own tests set.
For Oracle for instance, I used to check tables, sequences and indexes:
select count(*) from user_tables
select count(*) from user_sequences
select count(*) from user_indexes
For SqlServer I used to check tables, views and stored procedures:
SELECT * FROM sys.all_objects where type_desc in ('USER_TABLE', 'SQL_STORED_PROCEDURE', 'VIEW')
The best generic (and intuitive) solution I got, is by using ANT SQL task - all I needed to do is passing different parameters for each type of database.
i.e. The ANT build file looks like this:
<project name="run_sql_query" basedir="." default="main">
<!-- run_sql_query: -->
<target name="run_sql_query">
<echo message="=== running sql query from file ${database.src.file}; check the result in ${database.out.file} ==="/>
<sql classpath="${jdbc.jar.file}"
driver="${database.driver.class}"
url="${database.url}"
userid="${database.user}"
password="${database.password}"
src="${database.src.file}"
output="${database.out.file}"
print="yes"/>
</target>
<!-- Main: -->
<target name="main" depends="run_sql_query"/>
</project>
For more details, please refer to ANT:
https://ant.apache.org/manual/Tasks/sql.html

Categories