Solved

PreparedStatements over Servlet

Posted on 2006-07-11
6
546 Views
Last Modified: 2010-08-05
Hello all,

I'm using a Servlet to access MySQL with JDBC. Most of my queries are stored inside the servlet and executed as PreparedStatements. However, I'm not sure how best to store and execute them.

For example, the Servlet now looks something like this:

public class TestServlet extends HttpServlet  {

     String myStmtSQL = "Select * from TestTable as TestQuery";

     public String processRequest() {
            
      //define a mysql connection
      Connection con = null;
      //try to connect
      try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
                con = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb?user=root&password=secret");

                if(!con.isClosed()) {
                      System.out.println("connected to mysql ok");
                }

      } catch(Exception e) {
                System.err.println("could not connect to mysql: " + e.getMessage());
      }

        //now con is defined, we can create the statements
        PreparedStatement myStmt = con.prepareStatement(myStmtSQL);

        //and execute them
      myStmt.execute();

        //get string from resultset etc.

        //now disconnect from mysql...
        //etc.

    }
}

I'm storing the query as a string within the Servlet because the connection con is defined until someone calls processRequest(). Is this the right way of doing things? Servlets don't have a constructor, so I have to put the connection inside some method.

In addition, when is TestQuery still available as a Query? If my understanding is correct, the purpose of a PreparedStatement is that it only needs to get compiled once, and after that is available for faster processing. Does using them via a Servlet mean they're just recompiled every time someone makes a request?

Hope that all makes sense - my understanding of this topic is pretty shakey.

Thanks,
Matt.


0
Comment
Question by:mb2297
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
6 Comments
 
LVL 30

Accepted Solution

by:
todd_farmer earned 500 total points
ID: 17087912
I would not do it this way, no.  I would define a context (application) scope attribute as a DataSource (even better yet, use a JNDI lookup).  Creating a new connection is a heavyweight process - you don't want to do this with every request.  Far better to use connection pooling (again, DataSource objects).  You probably have DataSource support built into the Servlet container you are using (Tomcat? other?).  I would definately use that.

Prepared Statements on the server side only have to be compiled once by the SQL parser.  This is different - you're talking about client (driver)-side Prepared Statements.  Most likely, there isn't an appreciable performance benefit to using them - the driver takes the SQL string and parameters and sends it to the server as inline SQL.  However, there is a VERY significant security benefit to using them - they help prevent SQL injection attacks.  This is where a malicious user sends data to your application as a parameter that you use in a SQL statement without checking.  For example, if you build a query like so:

String sql = "SELECT * FROM products WHERE productid = " + productId;

Imagine what happens when somebody sends a productId value of:

3 OR (1=1)   [gets all products back]
3; DROP TABLE products  [drops the entire products table]

Bad stuff - all of it avoidable by using Prepared Statements.
0
 

Author Comment

by:mb2297
ID: 17093961
Todd,

Thanks for the reply. I'm using Tomcat and the JNDI support is there.

I understand the concept of connection pooling, but I'm struggling a little to wrap my head around an 'application scope' DataSource. I think I really need an example of using one with Servlets. In addition, what's the difference between 'DataSource' and JNDI Lookup? I looked up JNDI and came up with a bunch of results for 'JNDI DataSources', which doesn't really help differentiate them.

I understand the security implications of using PreparedStatements. My existing, naive, solution just passes SQL as a String from the client to the server, and security was my main motivation to move it server-side.

Regarding your server- vs driver-side arguement: doesn't the sample servlet above use server-side preparedstatements? I know they are created from strings, but there is no client involvement. If there was, it would be using the proper (secure) method, whereby the preparedstatement reads something like:

"SELECT * FROM products WHERE productid = ?"

When I'd use statement.setInt(1,somevaluefromclient) to set the actual parameter. If the client sent "3 OR (1=1)" in this case, wouldn't the statement fail?

If you know of an example of Servlets using an 'application scope' (JNDI)DataSource, then I'd really appreciate the link.

Thanks,
Matt.

0
 

Author Comment

by:mb2297
ID: 17140725
Todd,

I now have a JDBC DataSource setup working. At the moment, I embed the queries in Strings within the Servlet, and then create a PreparedStatement from that String once I've got a connection from the DataSource.

However, this doesn't seem like the correct way of doing things. Isn't there a way I can compile the PreparedStatements once, when the Servlet initializes, and then have each 'connection' use them? At the moment, it seems to recompile them every time someone connects.

The reason I did it the way I have, is that a PreparedStatement takes a Connection object in it's constructor - this seems to force the method that I've used, and therefore undermine the point of PreparedStatements in the first place. Am I confused somehow?

Thanks,
Matt.
0
Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

 
LVL 30

Expert Comment

by:todd_farmer
ID: 17140964
It really is a client-side Prepared Statement you are using - not a server-side Prepared Statement.  At the MySQL server level, the query is going to be parsed again each time it is executed, regardless of whether you use the same Java PreparedStatement object or not.  Reusing a single PreparedStatement will likely have some (small) performance benefits at the Java application layer (not at the database layer), but is really only useful in the context where you have to perform many similar queries within a single process.  It really is not good for increasing performance across invocations of a servlet, which is what I think you are talking about here.

So, what good are Prepared Statements?  The security they offer is key.
0
 

Author Comment

by:mb2297
ID: 17141332
Yes ok, I see your point.

I have another problem, which I had assumed could be rectified by using PreparedStatements in the correct way, but since I'm already doing that I guess I'll have to solve it some other way.

I think the problem stems from not closing or purging the PreparedStatements correctly. Following is an overview of the setup and problem - it looks daunting but I don't think it will be too hard to solve:

The Servlet accepts an XML document, which defines a number of PreparedStatements to run and their parameters. The Servlet parses this XML file and builds the PreparedStatements appropriately, and then adds them to a List.

One of the PreparedStatements has a variable number of parameters (anywhere from 1 to 51), so it just has a long list of ?s, and I fill in the ones that need values. All the remaining ones get given a value which is unfindable in the database (in this case, it's set to the integer -1).

Another function iterates over the list of PreparedStatements, executing them and parsing the results into another XML document which is returned to the caller. I call PreparedStatement.close() at the same time as I call ResultSet.close(), when the query has completed.

This seems to work ok the first time it gets called. But in subsequent calls the database query doesn't return what it should. A couple of examples:

PreparedStatement:
 SELECT * FROM (SELECT JOBS.JOB_ID, @rowid := COALESCE( @rowid +1, 0 ) WHERE TECHNIQUE=? AS rowid FROM JOBS) AS myquery  WHERE rowid IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

Call 1 on tech1 creates:
 SELECT * FROM (SELECT JOBS.JOB_ID, @rowid := COALESCE( @rowid +1, 0 ) WHERE TECHNIQUE='tech1' AS rowid FROM JOBS) AS myquery  WHERE rowid IN (0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1)";

When this runs, it brings back the 1st 10 rows from jobs with TECHNIQUE = tech1, which is correct.

Call 2 on tech1 creates:
 SELECT * FROM (SELECT JOBS.JOB_ID, @rowid := COALESCE( @rowid +1, 0 ) WHERE TECHNIQUE='tech1' AS rowid FROM JOBS) AS myquery  WHERE rowid IN (10,11,12,13,14,15,16,17,18,19,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1)";

If I run call 2 before call 1, this works as it should. If I run it afterwards, I get no records in the RecordSet (while(RecordSet.next() never runs). Similarly, if I run call 1 after call 2, call 1 comes back with no records in the RecordSet.


If I choose a technique with less than 10 jobs entries, I get a more enlightening response. Say there are 6 entries in JOBS with TECHNIQUE = 'tech2'

Call 1 on tech2 creates:
 SELECT * FROM (SELECT JOBS.JOB_ID, @rowid := COALESCE( @rowid +1, 0 ) WHERE TECHNIQUE='tech2' AS rowid FROM JOBS) AS myquery  WHERE rowid IN (0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1)";

This call correctly brings back the 1st 6 results from JOBS with TECHNIQUE = tech2 . Now if I run Call 1 on tech1, I get results 7-10 from JOBS with TECHNIQUE = 'tech1'.

If I run any calls after this, I get nothing. So basically I get 10 'slots' in my PreparedStatement, and once I've used them all I get nothing. This seems to me like the PreparedStatement is not getting refreshed between calls. What I am doing wrong?

Thanks in advance,
Matt.
0
 

Author Comment

by:mb2297
ID: 17237921
I found the problem with the above, but have forgotten what it was - something dumb from my end.

Thanks for the JNDI info - works like a charm.
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
running code or pseudo code of table structure 5 41
Mysql Left Join Case 10 114
MYSQL responding very slow 3 65
Get data from two MySQL tables 6 62
All XML, All the Time; More Fun MySQL Tidbits – Dynamically Generate XML via Stored Procedure in MySQL Extensible Markup Language (XML) and database systems, a marriage we are seeing more and more of.  So the topics of parsing and manipulating XM…
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

738 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question