Should JDBC Blob (not) be free()'d after use? - java

Just whacking together an export from an old DB that contains binary data, I stumbled over an exception in one of our utility methods:
java.lang.AbstractMethodError: net.sourceforge.jtds.jdbc.BlobImpl.free()
After checking our codebase, I found that utility method was never used until now, bascially it looks like this:
public BinaryHolder getBinary(final int columnIndex) throws SQLException {
Blob blob = null;
try {
blob = resultSet.getBlob(columnIndex);
final BinaryHolder binary = BinaryHolderUtil.create(blob);
return binary;
} finally {
if (blob != null)
blob.free();
}
}
BinaryHolder is just a wrapper that holdes the binary data (and before you ask, the code executes fine until it reaches the finally clause - BinaryHolderUtil.create(blob) does not attempt to free the blob).
Investigating further I found that everywhere else we access Blob's, the blob is just obtained using getBlob() and not free'd at all (The Javadoc says it will be automatically disposed of when the result set is closed).
Question now: Should the blob be free()'d manually (after all the ResultSet may be held for more than just accessing the blob), and if yes how can it be free()'d in a way that works even with a driver that does not implement it?
(We are using SQL-Server with JTDS1.25, if that wasn't already obvious from the exception)

The Blob.free() was introduced in JDBC 4.0 / Java 6. So you are most likely using a JDBC 3.0 or earlier JDBC driver.
As with most (JDBC) resources, closing them as soon as possible has its advantages (eg GC can collect it earlier, database resources are freed etc). That is also why you can close a ResultSet even though it is closed when you close the statement (or execute the statement again), just like you can close a Statement even though it is closed when the Connection is closed.
So a Blob does not need to be freed, but it is - in general - a good idea to free it when you are done with it.
BTW: JTDS is only JDBC 3.0, you would be better off using the Microsoft SQL Server JDBC driver of Microsoft itself.

Related

Getting CPU 100 percent when I am trying to downloading CSV in Spring

I am getting CPU performance issue on server when I am trying to download CSV in my project, CPU goes 100% but SQL returns the response within 1 minute. In the CSV we are writing around 600K records for one user it is working fine but for concurrent users we are getting this issue.
Environment
Spring 4.2.5
Tomcat 7/8 (RAM 2GB Allocated)
MySQL 5.0.5
Java 1.7
Here is the Spring Controller code:-
#RequestMapping(value="csvData")
public void getCSVData(HttpServletRequest request,
HttpServletResponse response,
#RequestParam(value="param1", required=false) String param1,
#RequestParam(value="param2", required=false) String param2,
#RequestParam(value="param3", required=false) String param3) throws IOException{
List<Log> logs = service.getCSVData(param1,param2,param3);
response.setHeader("Content-type","application/csv");
response.setHeader("Content-disposition","inline; filename=logData.csv");
PrintWriter out = response.getWriter();
out.println("Field1,Field2,Field3,.......,Field16");
for(Log row: logs){
out.println(row.getField1()+","+row.getField2()+","+row.getField3()+"......"+row.getField16());
}
out.flush();
out.close();
}}
Persistance Code:- I am using spring JDBCTemplate
#Override
public List<Log> getCSVLog(String param1,String param2,String param3) {
String sql =SqlConstants.CSV_ACTIVITY.toString();
List<Log> csvLog = JdbcTemplate.query(sql, new Object[]{param1, param2, param3},
new RowMapper<Log>() {
#Override
public Log mapRow(ResultSet rs, int rowNum)
throws SQLException {
Log log = new Log();
log.getField1(rs.getInt("field1"));
log.getField2(rs.getString("field2"));
log.getField3(rs.getString("field3"));
.
.
.
log.getField16(rs.getString("field16"));
}
return log;
}
});
return csvLog;
}
I think you need to be specific on what you meant by "100% CPU usage" whether it's the Java process or MySQL server. As you have got 600K records, trying to load everything in to memory would easily end up in OutOfMemoryError. Given that this works for one user means that you've got enough heap space to process this number of records for just one user and symptoms surface when there are multiple users trying to use the same service.
First issue I can see in your posted code is that you try to load everything into one big list and the size of the list varies based on the content of the Log class. Using a list like this also means that you have to have enough memory to process JDBC result set and generate new list of Log instances. This can be a major problem with a growing number of users. This type of short-lived objects will cause frequent GC and once GC cannot keep up with the amount of garbage being collected it fails obviously. To solve this major issue my suggestion is to use ScrollableResultSet. Additionally you can make this result set read-only, for example below is code fragment for creating a scrollable result set. Take a look at the documentation for how to use it.
Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
Above option is suitable if you're using pure JDBC or SpringJDBC template. If Hibernate is already used in your project you can still achieve the same this with the below code fragment. Again please check the documentation for more information and you have a different JPA provider.
StatelessSession session = sessionFactory.openStatelessSession();
Query query = session.createSQLQuery(queryStr).setCacheable(false).setFetchSize(Integer.MIN_VALUE).setReadOnly(true);
query.setParameter(query_param_key, query_paramter_value);
ScrollableResults resultSet = query.scroll(ScrollMode.FORWARD_ONLY);
This way you're not loading all the records to Java process in one go, instead you they're loaded on demand and will have small memory footprint at any given time. Note that JDBC connection will be open until you're done with processing the entire record set. This also means that your DB connection pool can be exhausted if many users are going to download CSV files from this endpoint. You need to take measures to overcome this problem (i.e use of an API manager to rate limit the calls to this endpoint, reading from a read-replica or whatever viable option).
My other suggestion is to stream data which you have already done, so that any records fetched from the DB are processed and sent to client before the next set of records are processed. Again I would suggest you to use a CSV library such as SuperCSV to handle this as these libraries are designed to handle a good load of data.
Please note that this answer may not exactly answer your question as you haven't provided necessary parts of your source such as how to retrieve data from DB but will give the right direction to solve this issue
Your problem in loading all data on application server from database at once, try to run query with limit and offset parameters (with mandatory order by), push loaded records to client and load next part of data with different offset. It help you decrease memory footprint and will not required keep connection to database open all the time. Of course, database will loaded a bit more, but maybe whole situation will better. Try different limit values, for example 5K-50K and monitor cpu usage - on both app server and database.
If you can allow keep many open connection to database #Bunti answer is very good.
http://dev.mysql.com/doc/refman/5.7/en/select.html

OrmLite: Difference between Dao.callBatchTasks() and TransactionManager.callInTransaction()

Which is the difference between these methods? I have readed the docs but I don't understand what callBatchTasks method do. Documentation says:
This will turn off what databases call "auto-commit" mode, run the
call-able and then re-enable "auto-commit".
Is't it a transaction?
Thanks.
Difference between Dao.callBatchTasks() and TransactionManager.callInTransaction()
The difference depends on the database you are using. Under Android, there is no difference. The javadocs for callBatchTasks(...) says:
Call the call-able that will perform a number of batch tasks. This is for performance when you want to run a number of database operations at once -- maybe loading data from a file. This will turn off what databases call "auto-commit" mode, run the call-able, and then re-enable "auto-commit". If auto-commit is not supported then a transaction will be used instead.
Android's SQLite is one of the databases. Inside the internal ORMLite code you see:
private <CT> CT doCallBatchTasks(DatabaseConnection connection, boolean saved,
Callable<CT> callable) throws SQLException {
if (databaseType.isBatchUseTransaction()) {
return TransactionManager.callInTransaction(connection, saved, databaseType,
callable);
}
...
So internally, when using under Android, dao.callBatchTasks(...) is a call through to TransactionManager.callInTransaction(...).

Opening database managing handles

I am using berkeley database....
Generally at this type of database you open an environment which is a bunch of files to control locking and transactions etc and then you open your database in this environment...
The problem is that there are LOTS of databases to open....
The method to open a database is opendatabase()
However opening and closing the database always is slow... The documentation says
Opening a database is a relatively
expensive operation, and maintaining a
set of open databases will normally be
preferable to repeatedly opening and
closing the database for each new
query.
The problem is how to maintain that set ??????
A simple solution i thought was lazy loading
private static Database db;
public CustomerDAO() {
if (db == null) {
try {
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setType(DatabaseType.BTREE);
db = BDBEnvironment.DEFAULT.getEnvironment().openDatabase(null, "C:\\xxxx\\CUSTOMERS",
null, dbConfig);
But this has a problem with double check locking.. Right???
Another issue is that i want to have a default file name or a user specified one.. Of course it is easy to create a DatabaseManager but always double check lock issue would occur.
Any ideas how to maintain a set of database handles??
Use basic Java synchronization techniques and a thread-safe data structure such as a ConcurrentHashMap to store your DB handles. You should probably read this book if you haven't already, as it covers a lot of what you'll need for this kind of issue.

Postgresql 8.4 reading OID style BLOBs with Hibernate

I am getting this weird case when querying Postgres 8.4 for some records with Blobs (of type OIDs) with Hibernate. The query does return all right but when my code wants to read the content of the BLOB with the simple code below, it gets 0 bytes back
public static byte[] readBlob(Blob blob) throws Exception {
InputStream is = null;
try {
is = blob.getBinaryStream();
return org.apache.commons.io.IOUtils.toByteArray(is);
}
finally {
if (is != null)
try {
is.close();
}
catch(Exception e) {}
}
}
Funny think is that I am getting this behavior only since I've started adding more then one such records to the table.
The underlying JDBC library is type 3 (postgresq 8.4-701).
Can someone give me a hint as to how to solve this issue?
Thanks
Peter
Looks like you may have found this bug:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-4876
It was a while since I have run into similar issue, and since I've refreshed my memories about this topic I am thinking of sharing the results. The problem is that Postgres (and a few versions back Oracle too) will not handle the Blob content at the record creation time in the same transaction. Funny think is that one needs to pass the content after the external file (where the content gets stored eventually) has been well created and reserved for the operation. Yes, the record gets created but the Blob is blank. To have the Blob filled out with whatever you need to put in, you need that operation in a second transaction (sort of an update record). That's a funny business (maybe a major bug), ehe

integrate ms access and mysql in java

I have a problem connecting to MS Access and MySQL using Java. My problem is that I cannot find the driver for MySQL. Here is my code:
<%# page import="java.sql.*" %>
<%
Connection odbcconn = null;
Connection jdbcconn = null;
PreparedStatement readsms = null;
PreparedStatement updsms = null;
ResultSet rsread = null;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //load database driver
odbcconn = DriverManager.getConnection("jdbc:odbc:SMS"); //connect to database
readsms = odbcconn.prepareStatement("select * from inbox where Status='New'");
rsread = readsms.executeQuery();
while(rsread.next()){
Class.forName("com.mysql.jdbc.Driver");
jdbcconn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bakme", "root", ""); //connect to database
updsms = jdbcconn.prepareStatement("insert into inbox(sms,phone) values (?,?)");
updsms.setString(1, rsread.getString("Message"));
updsms.setString(2, rsread.getString("Phone"));
updsms.executeUpdate();
}
%>
Thus, you get a ClassNotFoundException on the MySQL JDBC driver class? Then you need to put the MySQL JDBC driver JAR file containing that class in the classpath. In case of a JSP/Servlet application, the classpath covers under each the webapplication's /WEB-INF/lib folder. Just drop the JAR file in there. Its JDBC driver is also known as Connector/J. You can download it here.
That said, that's really not the way how to use JDBC and JSP together. This doesn't belong in a JSP file. You should be doing this in a real Java class. The JDBC code should also be more robust written, now it's leaking resources.
BalusC is spot on: this is not the way you should write something like this.
Connection, Statement, and ResultSet all represent finite resources. They are not like memory allocation; the garbage collector does not clean them up. You have to do that in your code, like this:
// Inside a method
Connection connection = null;
Statement statement = null;
ResultSet rs = null;
try
{
// interact with the database using connection, statement, and rs
}
finally
{
// clean up resources in a finally block using static methods,
// called in reverse order of creation, that don't throw exceptions
close(rs);
close(statement);
close(connection);
}
But if you do decide to move this to a server-side component, you're bound to have huge problems with code like this:
while(rsread.next())
{
Class.forName("com.mysql.jdbc.Driver");
jdbcconn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bakme", "root", ""); //connect to database
updsms = jdbcconn.prepareStatement("insert into inbox(sms,phone) values (?,?)");
updsms.setString(1, rsread.getString("Message"));
updsms.setString(2, rsread.getString("Phone"));
updsms.executeUpdate();
}
Registering the driver, creating a connection, without closing it, and repeatedly preparing a statement inside a loop for every row that you get out of Access shows a serious misunderstanding of relational databases and JDBC.
You should register the driver and create connections once, do what needs to be done, and clean up all the resources you've used.
If you absolutely MUST do this in a JSP, I'd recommend using JNDI data sources for both databases so you don't have to set up connections inside the page. You should not be writing scriptlet code - better to learn JSTL and use its <sql> tags.
You can use this link to download the MySql Driver. Once you download it, you need to make sure it is on the class path for the web server that you are using. The particulars of configuring JDBC drivers for a server vary from server to server. You may want to edit your question to include more details to get a better answer.

Categories

Resources