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
Related
I'am writing a method to retrieve an element of a DB giving a Long id parameter.
This id is unique, so the method should just return one element, and I want to create a class instance with this element retrived.
I've made the following method that works perfectly fine:
#Override
public ElementEntity getElement(final long id)
{
final MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("element_id", id);
final List<ElementEntity > listOfElements =
namedParameterJdbcOperations.query(SQL_RETURN_ELEMENT_BY_ID, paramSource, ROW_MAPPER_ELEMENT);
return !listOfElements .isEmpty() ? listOfElements.get(0) : null;
}
The ROW_MAPPER is implemented this way:
private static final RowMapper<ElementEntity > ROW_MAPPER_INSTALLER =
(rs, RowNum) ->
new ElementEntityBuilder().setElement(rs.getBytes("ELEMENT")).build();
The element is a byte array. I repeat, works perfectly. But I would like to avoid using a list and retrieving the first position and, instead, create directly the ElementEntity. So I tried the following approach:
#Override
public ElementEntity getElement(final long id)
{
final MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("element_id", id);
return namedParameterJdbcOperations.query(SQL_RETURN_ELEMENT_BY_ID, paramSource, (ResultSet rs) -> new DesktopAppInstallerEntity(rs.getBytes("ELEMENT")));
}
Althought I have't made any more changes, it gives me the following error:org.h2.jdbc.JdbcSQLNonTransientException: No data is available.
That's the full error (I changed some words to avoid showing the real SQL query):
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [SELECT SOMETHING FROM SOMEWHERE WHERE id = ?]; No data is available [2000-200]; nested exception is org.h2.jdbc.JdbcSQLNonTransientException: No data is available [2000-200]
I'm pretty new to all this JDBC, but it seems to me that the paramSource is not working properly when I apply this changes and I don't have the foggiest idea why is happening because I'm just changing the result extractor.
Well, I figured it out myself. I will share my answer to help the community. I used NamedParameterJdbcOperations.queryForObject instead of namedParameterJdbcOperations.query and used the same ROW_MAPPER:
#Override
public ElementEntity getElement(final long id)
{
final MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("id", id);
try
{
return namedParameterJdbcOperations.queryForObject(SQL_RETURN_ELEMENT, paramSource, ROW_MAPPER_ELEMENT);
}
catch (EmptyResultDataAccessException e)
{
return null;
}
}
In my particular case I'm interested to catch when the SQL Query attempt to find an Element that doesn't exist in the DB. For this purpose I catch and handle the EmptyResultDataAccessException thrown.
More info could be found here: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.html#queryForObject-java.lang.String-org.springframework.jdbc.core.namedparam.SqlParameterSource-org.springframework.jdbc.core.RowMapper-
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.
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;
}
}
I'm still new to the ehcache API so I may be missing something obvious but here's my current issue.
I currently have a persistent-disk cache that's being stored on my server. I'm currently implementing a passive write-behind cache method that saves key/value pairs to a database table. In the event the persistent-disk cache is lost, I'd like to restore the cache from the database table.
Example I'm using for my write-behind logic:
http://scalejava.blogspot.com/2011/10/ehcache-write-behind-example.html
I'm building a disk persistent using the following method:
import com.googlecode.ehcache.annotations.Cacheable;
import com.googlecode.ehcache.annotations.KeyGenerator;
import com.googlecode.ehcache.annotations.PartialCacheKey;
#Cacheable(cacheName = "readRuleCache", keyGenerator=#KeyGenerator(name="StringCacheKeyGenerator"))
public Rule read(#PartialCacheKey Rule rule,String info) {
System.out.print("Cache miss: "+ rule.toString());
//code to manipulate Rule object using info
try{
String serialziedRule =objectSerializer.convertToString(Rule);
readRuleCache.putWithWriter(new Element(rule.toString(),serialziedRule ));
}
catch(IOException ioe)
{
System.out.println("error serializing rule object");
ioe.printStackTrace();
}
return rule;
}
The write method I'm overriding in my CacheWriter implementation works fine. Things are getting saved to the database.
#Override
public void write(final Element element) throws CacheException {
String insertKeyValuePair ="INSERT INTO RULE_CACHE (ID, VALUE) VALUES " +
"('"+element.getObjectKey().toString()+"','"
+element.getObjectValue().toString()+"')";
Statement statement;
try
{
statement = connection.createStatement();
statement.executeUpdate(insertKeyValuePair);
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Querying and De-serializing the string back in to an object works fine too. I've validated that all the values of the object are present. The disk persistent cache is also being populated when I delete the *.data file and restart the application:
public void preLoadCache()
{
CacheManager cacheManager = CacheManager.getInstance();
readRuleCache = cacheManager.getCache("readRuleCache");
Query query=em.createNativeQuery("select * from RULE_CACHE");
#SuppressWarnings("unchecked")
List<Object[]> resultList = query.getResultList();
for(Object[] row:resultList)
{
try {
System.out.println("Deserializing: "+row[1].toString());
Rule rule = objectSerializer.convertToObject((String)row[1]);
rule= RuleValidator.verify(rule);
if(rule!=null)
{
readAirRuleCache.putIfAbsent(new Element(row[0], rule));
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Question
Everything looks OK. However when I pass Rule objects with keys that should exist in the cache the "read" method is called regardless and the *.data file size is increased. Though the write method for the database doesn't attempt to insert existing keys again. Any ideas on what I'm doing wrong?
It turns out this was the culprit:
keyGenerator=#KeyGenerator(name="StringCacheKeyGenerator")
The source material I read on this suggested that the "toString()" method I overrode would be used as the key for the cache key/value pair. After further research it turns out that this is not true. Though the "toString()" key is used. It is nested within class information to create a much larger key.
Reference:
http://code.google.com/p/ehcache-spring-annotations/wiki/StringCacheKeyGenerator
Example Expected key:
"[49931]"
Example Actual Key:
"[class x.y.z.WeatherDaoImpl, getWeather class x.y.z.Weather, [class java.lang.String], [49931]]"
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.