I have following Java code that creates a stored procedure. To test it I am deliberately passing it invalid sql:
public void createStoredProcedure(#NotNull final StoredProcInfo storedProcInfo, #NotNull final ColumnInfoFactory columnInfoFactory, #NotNull final Connection con) {
Statement createProcStmt = null;
try {
List<String> parameterDefinitions = Lists.newArrayList();
for (StoredProcParmInfo parmInfo : storedProcInfo.getInParameters()) {
parameterDefinitions.add(columnInfoFactory.generateParameterDefinition(parmInfo));
}
for (StoredProcParmInfo parmInfo : storedProcInfo.getOutParameters()) {
parameterDefinitions.add(columnInfoFactory.generateParameterDefinition(parmInfo));
}
String createProcSql = String.format("CREATE Procedure %s (%s) AS BEGIN %s END;", storedProcInfo.getName(), StringUtils.join(parameterDefinitions.iterator(), ","), storedProcInfo.getBody());
createProcStmt = con.createStatement();
createProcStmt.execute(createProcSql);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Closer.close(createProcStmt, logger);
}
}
The method executes successfully. And when I look in the database, I see that a stored procedure was created but marked as an invalid object:
And the error is that an INTO clause was expected in the body of the procedure:
Why was an SQLException not thrown when the code that created the procedure was executed? Is this an Oracle driver setting? I have searched online but not found any helpful information.
Related
I'm trying to call a stored procedure using jdbc. My connection is being passed through namedParameterJdbcTemplate and that's what I have to use to call it, but when I attempt to do this:
public void storedProcedure(long fileId, String Action) {
String sql = "call procedureName(?)";
try {
namedParameterJdbcTemplate.update(sql, Long.valueOf(fileId) );
} catch (Exception e) {
logger.error("Error while running stored procedure {}", sql, e);
}
}
I get the following error:
Cannot resolve method 'update(java.lang.String, java.lang.Long)'
Sources I've tried looking at and can't make it work:
https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/jdbc.html
https://lalitjc.wordpress.com/2013/07/02/different-ways-of-calling-stored-procedure-using-spring/
https://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/spring-call-stored-procedure.html
Most of them are creating a connection from the beginning but I already have it (namedParameterJdbcTemplate), also some are using datasource which I don't need because again, I have already the connection.
How can I make the call with namedParameterJdbcTemplate?
Thank you
This is how it works:
final String sql = "call procedureName (:variable)";
SqlParameterSource namedParameters = new MapSqlParameterSource("variable", variable);
try {
namedParameterJdbcTemplate.update(sql, namedParameters);
} catch (Exception e){
...
}
Your syntax looks wrong. When calling update using NamedParameterJdbcTemplate, you need to call the method with either of these two method signatures.
update(String sql, Map<String,?> paramMap)
or
update(String sql, SqlParameterSource paramSource)
For further information - https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.html
Further example here - https://netjs.blogspot.com/2016/11/insert-update-using-namedparameterjdbctemplate-spring.html
Can someone review my code
This query is fetching two values address_id and postcode from table1. Here
AddressID class has two variable postcode(string) and address_id(integer) :
#Select("SELECT address_id,postcode FROM table1 WHERE custom_field_1 = #{caseid}")
public List<AddressID> getAddressIdPostCodeList(String caseid);
Here is how AddressID looks AddressID.java
private int addressId;
private String postcode;
//getters and setters of Pstcode and addressId
#Override
public String toString() {
return "PostCode : " + this.postcode;
}
while executing this query I get value of address_id as 0 and required postcode. Although DB has values of address_id which is not zero. Where my code is failing?
This is where in my main method I am calling
List<AddressID> addresses = new ArrayList<>();
addresses = mainClassObject.getAddressIdPostCodeList(address.getcaseId());
Ideally addresses object should have both address_id and postcode. I am getting both values but address_id I am getting 0 and correct values for postcode.
Since I cant comment because I dont have 50 reputation, I had to write it here, its hard to tell from the code you posted, to give you an answer I need more detail on what getAddressIdPostCodeList() does, since you said the data in the DB has no ceros the error must be in the method getAddressIdPostCodeList() and/or in how you are handling the resultset of the Query
EDIT: Solution using Oracle JDBC Driver
Since I dont know how to use Mybatis, heres a solution using JDBC.
to connect using JDBC to your Oracle DB here's a simple tutorial:
1. First you need to download de JDBC driver from Oracle depending your DB version (11g,12c,10g), the driver Its called ojdbcX.jar where X is a number of the version of the driver
2. After you have downloaded the driver you need to add the .jar to your project, if you are using Netbeans IDE you can add it like this:
if you are using Eclipse you can use the following Link to see how to add the .JAR file: How to import a jar in Eclipse
3. After adding the .JAR its pretty simple, you just need to connect to the DB using your credentials, here is an example on how to do it:
Connection connection = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
connection = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:xe", "system", "password"); } catch (Exception ex) {
ex.printStackTrace();
}
for more deatiled information on how to connect, you can check the oracle.jdbc
Class OracleDriver Documentation
4. After the connection has been made its pretty simple, Heres a short code example to get the result you want, you need to modify it with your connection details and as you see fit because im making a couple of assumptions, this code is just an example:
Main.Java
public class Main {
public static void main(String[] argv) {
List<AddressID> addresses;
SQLConnect conex= new SQLConnect();
String caseid="the id you want";
addresses=conex.getAddressIdPostCodeList(caseid);
}
AddressID.java
public class AddressID {
private String addressId;
private String postcode;
}
SQLConnect.Java
public class SQLConnect {
public static Connection connection;
public SQLConnect (){
createConnection();
}
public void CreateConnection(){
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
connection = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:xe", "system", "password")
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void closeConnection(){
if(connection!=null){
try {
connection.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
public ResultSet ExecuteQuery(String queryTXT) throws SQLException{
Statement query = connection.createStatement();
ResultSet table=query.executeQuery(queryTXT);
return table;
}
public List<AddressID> getAddressIdPostCodeList(String caseid) throws SQLException{
List<AddressID> addresses = new ArrayList <> ();
ResultSet table=ExecuteQuery("SELECT address_id,postcode FROM table1 WHERE custom_field_1 ='"+caseid+"';");
while (table.next()) {
AddressID aux;
aux.addressId=table.getString(1);
aux.postcode=table.getString(2);
addresses.add(aux);
}
return addresses;
}
}
I did forgot to wtite my result query which is actually binding data fetched
from select query to the AddressId class..
#Results(id = "result",
value = {
#Result(property = "addressId", column = "address_id"),
#Result(property = "postcode", column = "postcode")
}
)
I have an aplication which create a number of query (update or insert) and then each query is executed.
The whole code is working fine but I've saw that my server IO latency is too much during this proccess.
The code execute a loop which is taking arround 1 minute.
Then what I wanted to do is write each query in memory instead to execute it, and then, once I have the whole list of query to execute, use "LOAD DATA LOCAL INFILE" from mysql, which will take less time.
My question is: How can I write all my query (String object) in a "File" or "any other container" in java to use it after the loop?.
#user3283548 This is my example code:
Class1:
import java.util.ArrayList;
public class Class1 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ArrayList<String> Staff=new ArrayList<String>();
Staff.add("tom");
Staff.add("Laura");
Staff.add("Patricia");
for (int x = 0; x < Staff.size(); x++) {
System.out.println(Staff.get(x));
Class2 user = new Class2 (Staff.get(x));
user.checkUser();
}
}
}
Class2:
public class Class2 {
private String user;
public Class2(String user){
this.user=user;
}
public void checkUser() throws Exception{
if (user.equals("tom")){
String queryUser="update UsersT set userStatus='2' where UserName='"+user+"';";
Class3 updateUser = new Class3(queryUser);
updateUser.UpdateQuery();;
}else{
String queryUser="Insert into UsersT (UserName,userStatus)Values('"+user+"','1');";
Class3 updateUser = new Class3(queryUser);
updateUser.InsertQuery();
System.out.println(user+" is not ton doing new insert");
}
}
}
Class3:
public class Class3 {
public String Query;
public Class3(String Query){
this.Query = Query;
}
public void UpdateQuery() throws Exception{
/*// Accessing Driver From Jar File
Class.forName("com.mysql.jdbc.Driver");
//DB Connection
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/default","root","1234567");
String sql =Query;
PreparedStatement pst = con.prepareStatement(sql);*/
System.out.println(Query); //Just to test
//pst.execute();
}
public void InsertQuery() throws Exception{
/*// Accessing Driver From Jar File
Class.forName("com.mysql.jdbc.Driver");
//DB Connection
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/default","root","1234567");
String sql =Query;
PreparedStatement pst = con.prepareStatement(sql);*/
System.out.println(Query); //Just to test
//pst.execute();
}
}
Then, what I wanted to do is create an ArraList in Class1 and use it in Class3 to collect all the queries which has to be executed.
The idea is to execute the list of queries in one time, once the main process is finished, istead to do it for each element within in loop of the Class1. I wanted to do it, because I think it will be take less resource IO from the server HD
Your loop is probably too slow because you're building up Strings using String
I'd hazard a guess you're doing things like
String query = "SELECT * FROM " + variablea + " WHERE + variableb + " = " ...
If you're doing a lot of string concatenation then use StringBuilder as every time you change a string it is actually re-created which is expensive. Simply changing your code to use StringBuilder instead of string will probably cut your loop executed time to a couple of MS. Simply call .toString() method of StringBuilder obj to get the string.
Storing objects
If you want to store anything for later use you should store it in a Collection. If you want a a key-value relationship then use a Map (HashMap would suit you fine). If you just want the values use an List (ArrayList is most popular).
So for example if I wanted to store query strings for later use I would...
Construct the string using StringBuilder.
Put the string (by calling .toString() into a HashMap
Get the query string from the HashMap...
You should never store things on disk if you don't need them to be persistent over application restarts and even then I'd store them in a database not in a file.
Hope this helps.
Thanks
David
EDIT: UPDATE BASED ON YOU POSTING YOUR CODE:
OK this needs some major re-factoring!
I've kept it really simple because I don't have a lot of time to re-write comprehensively.
I've commented where I have made corrections.
Your major issue here is creating objects in loops. You should just create the object once as creating objects is expensive.
I've also corrected other coding issues and replaced the for loop as you shouldn't be writing it like that.I've also renamed the classes to something useful.
I've not tested this so you may need to do some work to get it to work. But this should be a lot faster.
OLD CLASS 1
import java.util.ArrayList;
import java.util.List;
public class StaffChecker {
public static void main(String[] args) throws Exception {
// Creating objects is expensive, you should do this as little as possible
StaffCheckBO staffCheckBO = new StaffCheckBO();
// variables should be Camel Cased and describe what they hold
// Never start with ArrayList start with List you should specific the interface on the left side.
List<String> staffList = new ArrayList<String>();
staffList.add("tom");
staffList.add("Laura");
staffList.add("Patricia");
// use a foreach loop not a (int x = 0 ... ) This is the preffered method.
for (String staffMember : staffList) {
// You now dont need to use .get() you can access the current variable using staffMember
System.out.println(staffMember);
// Do the work
staffCheckBO.checkUser(staffMember);
}
}
}
OLD CLASS 2
/**
* Probably not really any need for this class but I'll assume further business logic may follow.
*/
public class StaffCheckBO {
// Again only create our DAO once...CREATING OBJECTS IS EXPENSIVE.
private StaffDAO staffDAO = new StaffDAO();
public void checkUser(String staffMember) throws Exception{
boolean staffExists = staffDAO.checkStaffExists(staffMember);
if(staffExists) {
System.out.println(staffMember +" is not in database, doing new insert.");
staffDAO.insertStaff(staffMember);
} else {
System.out.println(staffMember +" has been found in the database, updating user.");
staffDAO.updateStaff(staffMember);
}
}
}
OLD CLASS 3
import java.sql.*;
/**
* You will need to do some work to get this class to work fully and this is obviously basic but its to give you an idea.
*/
public class StaffDAO {
public boolean checkStaffExists(String staffName) {
boolean staffExists = false;
try {
String query = "SELECT * FROM STAFF_TABLE WHERE STAFF_NAME = ?";
PreparedStatement preparedStatement = getDBConnection().prepareStatement(query);
// Load your variables into the string in order to be safe against injection attacks.
preparedStatement.setString(1, staffName);
ResultSet resultSet = preparedStatement.executeQuery();
// If a record has been found the staff member is in the database. This obviously doesn't account for multiple staff members
if(resultSet.next()) {
staffExists = true;
}
} catch (SQLException e) {
System.out.println("SQL Exception in getStaff: " + e.getMessage());
}
return staffExists;
}
// Method names should be camel cased
public void updateStaff(String staffName) throws Exception {
try {
String query = "YOUR QUERY";
PreparedStatement preparedStatement = getDBConnection().prepareStatement(query);
// Load your variables into the string in order to be safe against injection attacks.
preparedStatement.setString(1, staffName);
ResultSet resultSet = preparedStatement.executeQuery();
} catch (SQLException e) {
System.out.println("SQL Exception in getStaff: " + e.getMessage());
}
}
public void insertStaff(String staffName) throws Exception {
try {
String query = "YOUR QUERY";
PreparedStatement preparedStatement = getDBConnection().prepareStatement(query);
// Load your variables into the string in order to be safe against injection attacks.
preparedStatement.setString(1, staffName);
ResultSet resultSet = preparedStatement.executeQuery();
} catch (SQLException e) {
System.out.println("SQL Exception in getStaff: " + e.getMessage());
}
}
/**
* You need to abstract the connection logic away so you avoid code reuse.
*
* #return
*/
private Connection getDBConnection() {
Connection connection = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/default", "root", "1234567");
} catch (ClassNotFoundException e) {
System.out.println("Could not find class. DB Connection could not be created: " + e.getMessage());
} catch (SQLException e) {
System.out.println("SQL Exception. " + e.getMessage());
}
return connection;
}
}
Is it possible to store a database connection as a separate class, then call the database objects from a main code? ie;
public class main{
public static void main{
try{
Class.forName("com.jdbc.driver");
Database to = new Database(1,"SERVER1","DATABASE");
Database from = new Database(2,"SERVER2","DATABASE");
String QueryStr = String.format("SELECT * FROM TABLE WHERE Id = %i", to.id)
to.results = sql.executeQuery(QueryStr);
while (to.results.next()) {
String QueryStr = String.format("INSERT INTO Table (A,B) VALUES (%s,%s)",to.results.getString(1),to.results.getString(2));
from.sql.executeQuery("QueryStr");
}
to.connection.close()
from.connection.close()
} catch (Exception ex) {
ex.printStackTrace();
{ finally {
if (to.connection != null)
try {
to.connection.close();
} catch (SQLException x) {
}
if (from.connection != null)
try {
from.connection.close();
} catch (SQLException x) {
}
}
}
public static class Database {
public int id;
public String server;
public String database;
public Connection connection;
public ResultSet results;
public Statement sql;
public Database(int _id, String _server, String _database) {
id = _id;
server = _server;
database = _database;
String connectStr = String.format("jdbc:driver://SERVER=%s;port=6322;DATABASE=%s",server,database);
connection = DriverManager.getConnection(connectStr);
sql = connection.createStatement;
}
}
}
I keep getting a "Connection object is closed" error when I call to.results = sql.executeQuery("SELECT * FROM TABLE"); like the connection closes as soon as the Database is done initializing.
The reason I ask is I have multiple databases that are all about the same that I am dumping into a master database. I thought it would be nice to setup a loop to go through each from database and insert into each to database using the same class. Is this not possible? Database will also contain more methods than shown as well. I am pretty new to java, so hopefully this makes sense...
Also, my code is probably riddled with syntax errors as is, so try not to focus on that.
Connection object is closed doesn't mean that the connection is closed, but that the object relative to the connection is closed (it could be a Statement or a ResultSet).
It's difficult to see from your example, since it has been trimmed/re-arranged, but it looks like you may be trying to use a ResultSet after having re-used its corresponding Statement. See the documentation:
By default, only one ResultSet object per Statement object can be open
at the same time. Therefore, if the reading of one ResultSet object is
interleaved with the reading of another, each must have been generated
by different Statement objects. All execution methods in the Statement
interface implicitly close a statment's current ResultSet object if an
open one exists.
In your example, it may be because autoCommit is set to true by default. You can override this on the java.sql.Connection class. Better yet is to use a transaction framework if you're updating multiple tables.
I've been stuck with this for past two days. I've go java function stored in Oracle system which is supposed to copy image from local drive do remote database and store it in BLOB - it's called CopyBLOB and looks like this:
import java.sql.*;
import oracle.sql.*;
import java.io.*;
public class CopyBLOB
{
static int id;
static String fileName = null;
static Connection conn = null;
public CopyBLOB(int idz, String f)
{
id = idz;
fileName = f;
}
public static void copy(int ident, String path) throws SQLException, FileNotFoundException
{
CopyBLOB cpB = new CopyBLOB(ident, path);
cpB.getConnection();
cpB.callUpdate(id, fileName);
}
public void getConnection() throws SQLException
{
DriverManager.registerDriver (new oracle.jdbc.OracleDriver());
try
{
conn = DriverManager.getConnection("jdbc:oracle:thin:#oraserv.ms.mff.cuni.cz:1521:db", "xxx", "xxx");
}
catch (SQLException sqlex)
{
System.out.println("SQLException while getting db connection: "+sqlex);
if (conn != null) conn.close();
}
catch (Exception ex)
{
System.out.println("Exception while getting db connection: "+ex);
if (conn != null) conn.close();
}
}
public void callUpdate(int id, String file ) throws SQLException, FileNotFoundException
{
CallableStatement cs = null;
try
{
conn.setAutoCommit(false);
File f = new File(file);
FileInputStream fin = new FileInputStream(f);
cs = (CallableStatement) conn.prepareCall( "begin add_image(?,?); end;" );
cs.setInt(1, id );
cs.setBinaryStream(2, fin, (int) f.length());
cs.execute();
conn.setAutoCommit(true);
}
catch ( SQLException sqlex )
{
System.out.println("SQLException in callUpdateUsingStream method of given status : " + sqlex.getMessage() );
}
catch ( FileNotFoundException fnex )
{
System.out.println("FileNotFoundException in callUpdateUsingStream method of given status : " + fnex.getMessage() );
}
finally
{
try
{
if (cs != null) cs.close();
if (conn != null) conn.close();
}
catch ( Exception ex )
{
System.out.println("Some exception in callUpdateUsingStream method of given status : " + ex.getMessage( ) );
}
}
}
}
The wrapper function is defined in package "MyPackage" as folows:
procedure image_adder( id varchar2, path varchar2 )
AS
language java name 'CopyBLOB.copy(java.lang.String, java.lang.String)';
And the inserting function called image_add is as simple as this:
procedure add_image( id numeric(10), pic blob)
AS
BEGIN
insert into pictures values (seq_pic.nextval, id, pic);
END add_image;
Now the problem: When I type
call MyPackage.image_adder(1, 'd:\samples\img.jpg');
I get the ORA-29531 Error: No method copy in class CopyBLOB.
Can you help me, please?
The method in your class has this signature:
public static void copy(int ident, String path)
But in your Java Stored Procedure you have specified this signature:
'CopyBLOB.copy(java.lang.String, java.lang.String)'
I think if you change the first argument to java,lang.Integer your problem should resolve itself. You should probably change the datatype of the ID parameter in the IMAGE_ADDER() procedure as well.
edit
"Any ideas how to upload local files?"
Not unreasonably, the database can only interact with files which are visible to its server. Generally that limits matters to files and directories which are physically on the same box, unless the network admin has mapped some remote drives.
Transferring files from a local PC drive to a server is really a client i.e. application issue, it isn't the sort of thing the database should really get involved with.
I know that isn't what you were hoping to hear. If you really want to drive the file uploading from teh database, then the mechanism remains the same whenever we want to transfer files across a network: FTP. Tim Hall has published a PL/SQL implementation for FTP on his Oracle-Base site. Find out more.
"long as the file is smaler than 2000B
(WTF?)"
That is suspiciously close to the BINARY CHAR limit (2000). In older versions of Oracle we had to use a two-step process: insert a placeholder and then issue an update. Something like this:
procedure add_image( id numeric(10), pic blob)
AS
BEGIN
insert into pictures
values (seq_pic.nextval, id, empty_blob());
update pictures
set col_pic = pic
where id = seq_pic.currval;
END add_image;