% Sign in Java PreparedStatement

PreparedStatement ps = con.createStatement("select * from table1 where last_name like ?");
ps.setString(1, "'%"+lastName+"'");

      

Will this work the same as ...

Statement s = con.createStatement("select * from table1 where last_name like %"+ lastName);

      

Or does PreparedStatement strike out the% sign?

+1


a source to share


6 answers


% is a wildcard (at least in Oracle), so in theory both should work the same (assuming you add missing single quotes)



However, the former will be seen as a better practice because it may allow the database optimizer not to re-parse the statement. The former should also protect you from SQL injection, while the latter may not.

+3


a source


The second one won't work because you forgot the quotes around the line! next to this you need to run away and be careful about SQL injection.

Suppose SQL

lastName = "and a quote' or a bracket()";
Statement s = con.createStatement("select * from table1 where last_name like '%"+ lastName + "'");

      

resulting SQL:



select * from table1 where last_name like '%and a quote' or a bracket()'

      

which will fail

Variable bindings make the work always safer.

+3


a source


Short answer: Yes, if you correct the quote, then the two should give the same results. The percent sign will not be "stripped" of the prepared statement, more than any other symbol.

Longer answer: The question of prepared statement and one-time statement can be tricky. If you are only going to execute it once, the prepared statement will take longer because the database engine has to do all the settings for the prepared statement and then insert the values ​​and then turn it into the cache until the engine decides to flush it. In addition, the optimizer often cannot process the prepared statement as efficiently. The whole point of a prepared statement is that the optimizer parses the query and designs the query plan once. Suppose you say something like "pick a customer_name from a customer where customer_type =? And customer_zip =?". You have indexes for both type and zip. With a one-off statement (with real values ​​filled in,not question marks, of course) the query optimizer in many database engines can look at the distribution statistics for two fields and choose an index that yields a smaller set of records, then read it all sequentially and exclude records that fail the second test. With a prepared statement, it must select an index before knowing what values ​​will be supplied, so it can choose a less efficient index.what values ​​will be provided, so it can choose a less efficient index.what values ​​will be provided, so it can choose a less efficient index.

You should never ever feel the pain of death, ever write code that simply clicks quotes around an unknown value and inserts it into an SQL statement. Either use prepared statements or write a function that properly escapes any inline quotes. This function is trivial to write. I don't understand why JDBC doesn't include it, so you have to write it yourself and include it in every application. (This is especially true given that some SQL dialects have characters other than single quote that must be escaped.)

Here's an example of such a function in Java:

public static String q(String s)
{
  if (s==null)
    return "null";
  if (s.indexOf('\'')<0)
    return "'"+s+"'";
  int sl=s.length();
  char[] c2=new char[sl*2+2];
  c2[0]='\''; 
  int p2=1;
  for (int p=0;p<sl;++p)
  {
    char c=s.charAt(p);
    if (c=='\'')
      c2[p2++]=c;
    c2[p2++]=c;
  }
  c2[p2++]='\'';
  return new String(c2,0,p2);
}

      

(Note: I just edited this function from the version I pulled from my code to exclude some special cases that are not relevant here. Sorry if I brought up some minor bugs while doing this.)

I usually give it a very short name like "q", so I can just write:

String sql="select customer_name from customer where customer_type="+q(custType)
  +" and customer_zip="+q(custZip);

      

or something quick and easy. This is a violation of "give functions full and meaningful names", but I think it stands here where I can use the same function ten times in a single statement.

Then I overload it to accept dates and numbers and other special types and handle them accordingly.

+3


a source


Using prepared statements with bind variables is much faster because it means Oracle doesn't have to parse (compile) SQL statements over and over again. Oracle stores all executed statements along with execution plans in a shared hash table for reuse. However, Oracle only reuses the prepared statement execution plan with bind variables. When you do:

"select * from table1 where last_name is%" + lastName

Oracle does not reuse execution plan.

(Oracle hashes every sql statement and when you use select ... where last_name is% "+ lastName, every sql statement has a different hash value because the lastname variable almost always has a different value, so Oracle cannot find sql in the hash- table and Oracle cannot reuse the execution plan.)

In a multi-concurrency situation, the impact is even greater because Oracle locks this shared hash table. These locks don't last long, but in multi concurrency mode, the lock really starts to hurt. When you use prepared statements with bind variables, almost no locking is required. Oracle, by the way, calls these padlocks.

Only when you have a data object and your queries are taking minutes (reporting) and not divided seconds can you use unprepared statements.

+1


a source


We often use the first approach with no problem. For instance:

String sql = "SELECT * FROM LETTER_BIN WHERE LTR_XML Like ' (?) ' AND LTR_BIN_BARCODE_ID = (?)";
try
{
    // Cast a prepared statement into an OralcePreparedStatement
    opstmt = (OraclePreparedStatement) conn.prepareStatement(sql);
    // Set the clob using a string
    opstmt.setString(1,fX.toString());
    // for this barcode
    opstmt.setLong(2,lbbi);
    // Execute the OraclePreparedStatement
    opstmt.execute();
} catch(java.sql.SQLException e)
{
    System.err.println(e.toString());
} finally
{
    if(opstmt != null)
    {
        try
        {
            opstmt.close();
        } catch(java.sql.SQLException ignore)
        {
            System.err.println("PREPARED STMT ERROR: "+ignore.toString());
        }
    }

}

      

0


a source


Okay, I'll take your word for Oracle. This is, unsurprisingly, database dependent. Postgres behaves as I described. When using MySQL from JDBC - at least a couple of years ago when I last looked at this, there is quite a lot of zero difference between prepared statements and one-off statements because the MySQL JDBC driver stores prepared statements on the CLIENT side when you execute prepared statement, it fills in the values ​​as text and passes it to the database engine. As far as the engine is concerned, there really is no such thing as a prepared statement. I would not be surprised to learn that other engines have completely different behavior.

0


a source







All Articles