java.lang.OutOfMemoryError with hibernate - java

I get the below error when I ran the below code. May I know what is wrong in my code. Thanks in advance . I am using hibernate and java
HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
#Autowired
SessionFactory sessionFactory;
Session session = null;
Transaction tx = null;
public String getEntityList(String userIds, String callerID) throws Exception {
session = sessionFactory.openSession();
tx = session.beginTransaction();
List<User> userList = session.createCriteria(User.class)
.list();
//Query the database
CallableStatement stmt = null;
String returnVal = "";
ResultSet rs = null;
try {
stmt = ((Connection) session).prepareCall("{?=call WS_Distributionlist(?,?)}");
} catch (SQLException e) {
e.printStackTrace();
}
try {
// left over accounts processing
stmt.executeUpdate();
returnVal += stmt.getString(1) + "|";
System.out.println("RETURN VALUE in tail end :::::: "
+ returnVal);
//returnVal = returnVal.substring(0, returnVal.length() - 1);
System.out.println("Return Value " + returnVal);
session.close();
//return returnVal;
} catch (SQLException e) {
System.out.println("Error while executing the database function ");
} finally {
session.close();
}
tx.commit();
session.close();
return returnVal;
}

A lot of useful details are missing:
userIds is an input parameter of the function where is it used?
userList, how many users are there and where are they used?
WS_Distributionlist(?,?) has 2 input parameters, where do you set them?
I cannot understand the following line of code:
((Connection) session).prepareCall("{?=call WS_Distributionlist(?,?)}");
I've seen and used many technique to get a connection out of an hibernate session, none of which where casting the session itself to connection, but this is more or less a personal curiosity.
The point is that there is a lot of missing code, so I can just speculate that one of this calls produces an huge dataset, or that you are passing some huge parameter to a statement.
Either way, if you deal with HUGE dataset you should probably use a StatelessSession , because stateless session have nearly zero memory overhead (no caching).
If you build your own statement from connection , use FORWARD_ONLY_CURSOR.
Try setting the fetchSize on the statements that returns resultset.
Consider using either a ScrollableResult or an Iterator instead of a List as a result.

Related

Spring 5 JdbcTemplate cannot reuse PreparedStatement?

One simple optimization for SQL is the reuse of prepared statements. You incur the parsing cost once and can then reuse the PreparedStatement object within a loop, just changing the parameters as needed. This is clearly documented in Oracle's JDBC tutorial and many other places.
Spring 5 when using JdbcTemplate seems to make this impossible. All JdbcTemplate query and update methods that deal with PreparedStatementCreators funnel down to one execute method. Here's the code of that method in its entirety.
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}
Connection con = DataSourceUtils.getConnection(obtainDataSource());
PreparedStatement ps = null;
try {
ps = psc.createPreparedStatement(con);
applyStatementSettings(ps);
T result = action.doInPreparedStatement(ps);
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
The "interesting" bit is in the finally block:
JdbcUtils.closeStatement(ps);
This makes it completely impossible to reuse a prepared statement with JdbcTemplate.
It's been a long time (5 years) since I've had occasion to work with Spring JDBC, but I don't recall this ever being a problem. I worked on a large SQL backend with literally hundreds of prepared statements and I clearly remember not having to re-prepare them for every execution.
What I want to do is this:
private static final String sqlGetPDFFile = "select id,root_dir,file_path,file_time,file_size from PDFFile where digest=?";
private PreparedStatement psGetPDFFile;
#Autowired
public void setDataSource(DataSource dataSource) throws SQLException
{
Connection con = dataSource.getConnection();
psGetPDFFile = con.prepareStatement(sqlGetPDFFile);
this.tmpl = new JdbcTemplate(dataSource);
}
...
...
List<PDFFile> files =
tmpl.query(
// PreparedStatementCreator
c -> {
psGetPDFFile.setBytes(1, fileDigest);
return psGetPDFFile;
},
// RowMapper
(rs, n)->
{
long id = rs.getLong(1);
Path rootDir = Paths.get(rs.getString(2));
Path filePath = Paths.get(rs.getString(3));
FileTime fileTime = FileTime.from(rs.getTimestamp(4).toInstant());
long fileSize = rs.getLong(5);
return new PDFFile(id,fileDigest,rootDir,filePath,fileTime,fileSize);
}
);
But of course this fails the second time because of the hardcoded statement close call.
The question: Assuming I want to continue using Spring JDBC, what is the correct way to reuse prepared statements?
Also, if anyone knows why Spring does this (i.e. there's a good reason for it) I'd like to know.

Java List can not retrieve over 90.000 elements

I am dealing with an issue when I attempt to retrieve a large amount of records from a database. It seems that when the amount of records exceed 90.000, the elements can not be retrieved.
When that happens I get the following exception:
com.sun.jdi.ObjectCollectedException occurred while retrieving value.
The code that I am using is the following one:
Session objSession;
List<GroupEntity> colResults;
objSession = this.objSessionFactory.openSession();
try
{
objQuery = objSession.createQuery("FROM GroupEntity WHERE (strDomain = :Domain)")
.setParameter("Domain", strDomain)
.list();
}
catch (Exception objException)
{
throw new GroupException("Could not retrieve the list of WebFiltering groups to scan");
}
objSession.close();
return colResults;
I attempt to page the results retrieved by sets of 1.000, using this method when I insert up to 89.999 records the list is fine. however when I exceed 90.000 I get the same exception.
Any idea about how to face this issue?
In case you process such a big amount of data I'd recommend that you use batch processing with ScrollableResults: https://grokonez.com/hibernate/resolve-hibernate-outofmemoryerror-problem-hibernate-batch-processing
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
ScrollableResults dataCursor = session.createQuery("FROM Data").scroll();
int count = 1;
while (dataCursor.next()) {
Data data = (Data) dataCursor.get(0);
String newText = Utilities.generatedRandomString();
data.setText(newText);
session.update(data);
if (count % 50 == 0) {
System.out.println("============================log: count = " + count);
session.flush();
session.clear();
}
count++;
}
tx.commit();
} catch (Exception e) {
if (null != tx) {
tx.rollback();
}
} finally {
session.close();
}
In this case session will not keep all 90000 records in memory.
"com.sun.jdi.ObjectCollectedException" happens when the object you referring to is garbage collected.
there is no such limit of size on java arrayList.

How to get result size from an SQL query and check size

Hi I'm trying to write a piece of code for a simple verification method as part of a MVC.
At present the SQL is not written as a prepared statement so obviously it is at risk to a SQL injection so any help in regards to writing the SQL as a prepared statement would be really helpful.
The method which is in the User model.
public boolean getInfo() {
try {
DBAccess dbAccess = new DBAccess();
String sql = "SELECT username, password FROM owner WHERE username = '" + this.username
+ "'AND password = '" + this.password + "';";
dbAccess.close();dbAccess.executeQuery(sql);
dbAccess.close();
return true;
} catch (Exception e) {
return false;
}
}
I want to get the size of the result set which is generated by the SQL query and if the size of it is 1 return true else it's false.
If you need more info on the rest of the MVC just post and I'll get it up here.
Just return the result of ResultSet#next(), assuming that there's an UNIQUE constraint on the username. It returns false if there is no next record.
Here's a concrete kickoff example, slightly rewritten to fix potential SQL injection attack hole, resource leaking and threadsafety problems as shown so far in your code. Also, the altered SQL query should force you to MD5-hash the passwords before saving in DB (you don't want to store passwords plaintext in DB).
public boolean exist(String username, String password) throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
boolean exist = false;
try {
connection = database.getConnection();
statement = connection.prepareStatement("SELECT id FROM owner WHERE username = ? AND password = MD5(?)");
statement.setString(1, username);
statement.setString(2, password);
resultSet = statement.executeQuery();
exist = resultSet.next();
} finally {
if (resultSet != null) try { resultSet.close(); } catch (SQLException ignore) {}
if (statement != null) try { statement.close(); } catch (SQLException ignore) {}
if (connection != null) try { connection.close(); } catch (SQLException ignore) {}
}
return exist;
}
Rather select the fields username and password, you could select the count of them and then reference that value.
So your SQL query would be:
SELECT count(*) FROM owner WHERE username = '" + this.username
+ "'AND password = '" + this.password + "';
That would return the number of matched records, where if number is greater than 0, or equals one, validate them.
Without knowing the details of your DBAccess class, we can't tell you how to do this. I'm guessing it returns a List (but it's a guess, nothing more). If that's the case, you could check the size of the List via list.size() , or see if it returned at least 1 result with !list.isEmpty(). Of course, if it's not a list then this won't work.
And you definitely need to switch to prepared statements. For an example, see this SO post.
Side note: if this method is returning a boolean indicating whether a user exists, it shouldn't be called getInfo()! Something like userExists() would make more sense.
For your question about preventing sql injection, and if you want to start getting your feet wet with an "ORM like" library, you could use myibatis to create prepared statements. Myibatis is a data mapper which you can create a relatively simple ORM from. As you get more brave, you could move to hibernate or JPA.
http://www.mybatis.org/

Best way to get Session and Release session in HIbernate/SQL

Consider the below two codes.
Session session = null;
query = "update Employee set EMPLOYEE_NAME = 'Jay' where EMPLOYEE_ID = 1";
try {
session = getSession();
Query query = session.createSQLQuery(dlquery);
query.executeUpdate();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if(session != null) {
releaseSession(session);
}
}
And....
Session session = getSession();
query = "update Employee set EMPLOYEE_NAME = 'Jay' where EMPLOYEE_ID = 1";
try {
Query query = session.createSQLQuery(dlquery);
query.executeUpdate();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if(session != null) {
releaseSession(session);
}
}
Which among the two is better to use? Or is there a better way? What wrong I might be doing here?
Also should we do a null check before calling releaseSession(session) in finally?
Truly, the best way is to leverage Spring and its JPA/Hibernate support.... You'll never have to deal with it in your code.
To be honest, I'd say there wasn't much difference at all in the two, but you should always check if the session is null, just so you don't cause another NullReferenceException.
Unfortunately this is probably the cleanest way to do this in Java, as there is no such thing as the "using" statement as there is in C#, which allows you to dispose the object after the using block has finished executing.
I would suggest using a transaction. If tomorrow you change your code to add another update for instance, you won't have to worry about any consistency problem in case something happens while executing an update. Aside from that, I would suggest using parameters in your query rather than hard-coding the values. This way you could reuse the query.
If getSession() throws an Exception for some reason, would not it be problem in second solution?
Use the below syntax to get and release session.
session = getSession();
Query query = session.createSQLQuery(dlquery);
query.executeUpdate();
session.close();

delete sql not deleting

I'm trying to delete an event from my table. However I can't seem to get it to work.
My SQL statement is:
public void deleteEvent(String eventName){
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
db.update(query);
System.out.println (query);
}
Using MySQL db
Try using the following :
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
try {
Connection con = getConnection();
Statement s = con.createStatement();
s.execute(query);
} catch (Exception ex) {
ex.printStackTrace();
}
You have to code your getConnection() method to return a valid Database Connection.
I would suggest using Statement.executeUpdate method, since it returns an integer. So after performing this delete query you will also have information if you really deleted any records (in this case you would expect this method to return 1, since you are using LIMIT=1). I would also suggest closing Statement as soon as you don't need it, here is skeleton implementation:
private void performDelete(Connection conn, String deleteQuery, int expectedResult) throws SQLException {
Statement stmt = conn.createStatement();
int result = -1;
try {
result = stmt.executeUpdate(deleteQuery);
if(result != expectedResult) {
//Here you can check the result. Perhaps you don't need this part
throw new IllegalStateException("Develete query did not return expected value");
}
} catch(SQLException e) {
//Good practice if you use loggers - log it here and rethrow upper.
//Or perhaps you don't need to bother in upper layer if the operation
//was successful or not - in such case, just log it and thats it.
throw e;
} finally {
//This should be always used in conjunction with ReultSets.
//It is not 100% necessary here, but it will not hurt
stmt.close();
}
}

Categories