Solved

PreparedStatements over Servlet

Posted on 2006-07-11
6
538 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
  • 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
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

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…
Does the idea of dealing with bits scare or confuse you? Does it seem like a waste of time in an age where we all have terabytes of storage? If so, you're missing out on one of the core tools in every professional programmer's toolbox. Learn how to …
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

746 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now