bwLehrpool Masterserver
Manages authentication and sharing of virtual machines between participating institutions
MysqlStatement.java
Go to the documentation of this file.
1 package org.openslx.imagemaster.db;
2 
3 import java.io.Closeable;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.sql.SQLFeatureNotSupportedException;
9 import java.sql.Statement;
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15 
22 public class MysqlStatement implements Closeable
23 {
24 
25  private static final QueryCache cache = new QueryCache();
26 
27  private final PreparsedQuery query;
28 
29  private final PreparedStatement statement;
30 
31  private final List<ResultSet> openResultSets = new ArrayList<>();
32 
33  MysqlStatement( Connection con, String sql ) throws SQLException
34  {
36  synchronized ( cache ) {
37  query = cache.get( sql );
38  }
39  if ( query == null ) {
40  query = parse( sql );
41  synchronized ( cache ) {
42  cache.put( sql, query );
43  }
44  }
45  this.query = query;
46  this.statement = con.prepareStatement( query.sql, Statement.RETURN_GENERATED_KEYS );
47  }
48 
49  public String getQuery()
50  {
51  return query.sql;
52  }
53 
61  private List<Integer> getIndexes( String name )
62  {
63  List<Integer> indexes = query.indexMap.get( name );
64  if ( indexes == null ) {
65  throw new IllegalArgumentException( "Parameter not found: " + name );
66  }
67  return indexes;
68  }
69 
79  public void setObject( String name, Object value ) throws SQLException
80  {
81  List<Integer> indexes = getIndexes( name );
82  for ( Integer index : indexes ) {
83  statement.setObject( index, value );
84  }
85  }
86 
96  public void setString( String name, String value ) throws SQLException
97  {
98  List<Integer> indexes = getIndexes( name );
99  for ( Integer index : indexes ) {
100  statement.setString( index, value );
101  }
102  }
103 
113  public void setInt( String name, int value ) throws SQLException
114  {
115  List<Integer> indexes = getIndexes( name );
116  for ( Integer index : indexes ) {
117  statement.setInt( index, value );
118  }
119  }
120 
130  public void setLong( String name, long value ) throws SQLException
131  {
132  List<Integer> indexes = getIndexes( name );
133  for ( Integer index : indexes ) {
134  statement.setLong( index, value );
135  }
136  }
137 
147  public void setBoolean( String name, boolean value ) throws SQLException
148  {
149  List<Integer> indexes = getIndexes( name );
150  for ( Integer index : indexes ) {
151  statement.setBoolean( index, value );
152  }
153  }
154 
164  public void setBinary( String name, byte[] value ) throws SQLException
165  {
166  List<Integer> indexes = getIndexes( name );
167  for ( Integer index : indexes ) {
168  statement.setBytes( index, value );
169  }
170  }
171 
179  public boolean execute() throws SQLException
180  {
181  return statement.execute();
182  }
183 
191  public ResultSet executeQuery() throws SQLException
192  {
193  ResultSet rs = statement.executeQuery();
194  openResultSets.add( rs );
195  return rs;
196  }
197 
207  public int executeUpdate() throws SQLException
208  {
209  return statement.executeUpdate();
210  }
211 
229  public ResultSet getGeneratedKeys() throws SQLException
230  {
231  ResultSet rs = statement.getGeneratedKeys();
232  openResultSets.add( rs );
233  return rs;
234  }
235 
236  public int lastInsertId() throws SQLException
237  {
238  int result = -1;
239  try ( ResultSet rs = statement.getGeneratedKeys() ) {
240  if ( rs.next() ) {
241  result = rs.getInt( 1 );
242  }
243  }
244  return result;
245  }
246 
252  @Override
253  public void close()
254  {
255  for ( ResultSet rs : openResultSets ) {
256  try {
257  rs.close();
258  } catch ( SQLException e ) {
259  //
260  }
261  }
262  try {
263  statement.cancel();
264  } catch ( SQLException e ) {
265  // Nothing to do
266  }
267  try {
268  statement.close();
269  } catch ( SQLException e ) {
270  // Nothing to do
271  }
272  }
273 
279  public void addBatch() throws SQLException
280  {
281  statement.addBatch();
282  }
283 
292  public int[] executeBatch() throws SQLException
293  {
294  return statement.executeBatch();
295  }
296 
297  // static methods
298 
299  private static PreparsedQuery parse( String query )
300  {
301  int length = query.length();
302  StringBuffer parsedQuery = new StringBuffer( length );
303  Map<String, List<Integer>> paramMap = new HashMap<>();
304  boolean inSingleQuote = false;
305  boolean inDoubleQuote = false;
306  boolean hasBackslash = false;
307  int index = 1;
308 
309  for ( int i = 0; i < length; i++ ) {
310  char c = query.charAt( i );
311  if ( hasBackslash ) {
312  // Last char was a backslash, so we ignore the current char
313  hasBackslash = false;
314  } else if ( c == '\\' ) {
315  // This is a backslash, next char will be escaped
316  hasBackslash = true;
317  } else if ( inSingleQuote ) {
318  // End of quoted string
319  if ( c == '\'' ) {
320  inSingleQuote = false;
321  }
322  } else if ( inDoubleQuote ) {
323  // End of quoted string
324  if ( c == '"' ) {
325  inDoubleQuote = false;
326  }
327  } else {
328  // Not in string, look for named params
329  if ( c == '\'' ) {
330  inSingleQuote = true;
331  } else if ( c == '"' ) {
332  inDoubleQuote = true;
333  } else if ( c == ':' && i + 1 < length && Character.isJavaIdentifierStart( query.charAt( i + 1 ) ) ) {
334  int j = i + 2;
335  while ( j < length && Character.isJavaIdentifierPart( query.charAt( j ) ) ) {
336  j++;
337  }
338  String name = query.substring( i + 1, j );
339  c = '?'; // replace the parameter with a question mark
340  i += name.length(); // skip past the end of the parameter
341 
342  List<Integer> indexList = paramMap.get( name );
343  if ( indexList == null ) {
344  indexList = new ArrayList<>();
345  paramMap.put( name, indexList );
346  }
347  indexList.add( Integer.valueOf( index ) );
348 
349  index++;
350  }
351  }
352  parsedQuery.append( c );
353  }
354 
355  return new PreparsedQuery( parsedQuery.toString(), paramMap );
356  }
357 
358  // private helper classes
359 
360  private static class PreparsedQuery
361  {
362  private final Map<String, List<Integer>> indexMap;
363  private final String sql;
364 
365  public PreparsedQuery( String sql, Map<String, List<Integer>> indexMap )
366  {
367  this.sql = sql;
368  this.indexMap = indexMap;
369  }
370  }
371 
372  private static class QueryCache extends LinkedHashMap<String, PreparsedQuery>
373  {
374  private static final long serialVersionUID = 1L;
375 
376  public QueryCache()
377  {
378  super( 30, (float)0.75, true );
379  }
380 
381  @Override
382  protected boolean removeEldestEntry( Map.Entry<String, PreparsedQuery> eldest )
383  {
384  return size() > 40;
385  }
386  }
387 
388 }
boolean execute()
Executes the statement.
ResultSet getGeneratedKeys()
Retrieves any auto-generated keys created as a result of executing this Statement object...
ResultSet executeQuery()
Executes the statement, which must be a query.
boolean removeEldestEntry(Map.Entry< String, PreparsedQuery > eldest)
int executeUpdate()
Executes the statement, which must be an SQL INSERT, UPDATE or DELETE statement; or an SQL statement ...
void addBatch()
Adds the current set of parameters as a batch entry.
void setInt(String name, int value)
Sets a parameter.
PreparsedQuery(String sql, Map< String, List< Integer >> indexMap)
void setBoolean(String name, boolean value)
Sets a parameter.
int[] executeBatch()
Executes all of the batched statements.
void setString(String name, String value)
Sets a parameter.
static PreparsedQuery parse(String query)
Class for creating PreparedStatements with named parameters.
void setBinary(String name, byte[] value)
Sets a parameter.
void setObject(String name, Object value)
Sets a parameter.
List< Integer > getIndexes(String name)
Returns the indexes for a parameter.
void setLong(String name, long value)
Sets a parameter.
MysqlStatement(Connection con, String sql)