EMMA Coverage Report (generated Sun May 02 20:42:29 CEST 2010)
[all classes][hu.netmind.beankeeper.db.impl]

COVERAGE SUMMARY FOR SOURCE FILE [DatabaseBase.java]

nameclass, %method, %block, %line, %
DatabaseBase.java100% (1/1)96%  (22/23)85%  (1257/1476)89%  (261.2/293)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DatabaseBase100% (1/1)96%  (22/23)85%  (1257/1476)89%  (261.2/293)
remove (Transaction, String, Map): void 0%   (0/1)0%   (0/21)0%   (0/4)
translateName (String): String 100% (1/1)62%  (23/37)89%  (8/9)
init (Map): void 100% (1/1)63%  (64/101)66%  (13.8/21)
isRealTableNameTaken (Transaction, String): boolean 100% (1/1)77%  (30/39)75%  (6/8)
transformTableName (Transaction, String): String 100% (1/1)78%  (351/451)84%  (64/76)
handle (PersistenceEvent): void 100% (1/1)85%  (56/66)96%  (15.4/16)
getTableName (Transaction, String): String 100% (1/1)89%  (41/46)88%  (7/8)
reverseName (String): String 100% (1/1)89%  (17/19)86%  (6/7)
search (Transaction, QueryStatement, Limits): SearchResult 100% (1/1)91%  (142/156)96%  (27/28)
readReservedWords (): void 100% (1/1)92%  (78/85)88%  (15/17)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
DatabaseBase (): void 100% (1/1)100% (17/17)100% (5/5)
ensureTable (Transaction, String, Map, List, boolean): void 100% (1/1)100% (25/25)100% (4/4)
getConnectionSource (): ConnectionSource 100% (1/1)100% (3/3)100% (1/1)
insert (Transaction, String, Map): void 100% (1/1)100% (24/24)100% (5/5)
release (): void 100% (1/1)100% (15/15)100% (5/5)
replaceOrderTableNames (Transaction, List): List 100% (1/1)100% (53/53)100% (11/11)
replaceTableName (Transaction, TableTerm): TableTerm 100% (1/1)100% (146/146)100% (29/29)
replaceTableNames (Transaction, Collection): List 100% (1/1)100% (24/24)100% (7/7)
replaceTableNames (Transaction, Expression): Expression 100% (1/1)100% (66/66)100% (12/12)
save (Transaction, String, Map, Map): void 100% (1/1)100% (27/27)100% (5/5)
transformAttributes (List): List 100% (1/1)100% (23/23)100% (7/7)
transformAttributes (Map): Map 100% (1/1)100% (28/28)100% (7/7)

1/**
2 * Copyright (C) 2006 NetMind Consulting Bt.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19package hu.netmind.beankeeper.db.impl;
20 
21import java.util.*;
22import java.io.*;
23import java.sql.Connection;
24import java.sql.DatabaseMetaData;
25import org.apache.log4j.Logger;
26import hu.netmind.beankeeper.service.StoreContext;
27import hu.netmind.beankeeper.parser.*;
28import hu.netmind.beankeeper.common.StoreException;
29import hu.netmind.beankeeper.transaction.*;
30import hu.netmind.beankeeper.transaction.event.TransactionEvent;
31import hu.netmind.beankeeper.transaction.event.TransactionCommittedEvent;
32import hu.netmind.beankeeper.transaction.event.TransactionRolledbackEvent;
33import hu.netmind.beankeeper.db.*;
34import hu.netmind.beankeeper.management.ManagementTracker;
35import hu.netmind.beankeeper.event.EventDispatcher;
36import hu.netmind.beankeeper.event.PersistenceEventListener;
37import hu.netmind.beankeeper.event.PersistenceEvent;
38 
39/**
40 * This is a database superclass offers basic functions that
41 * will need to be addressed in every database implementation.
42 * The following tasks are currently handled by this superclass:<br>
43 * <ul>
44 *    <li>Transaction handling. Implementations only receive connections
45 *    from here on.</li>
46 *    <li>Table name handling. All table names are transformed to match
47 *    the maximum length supported by database software. Implementations
48 *    are guaranteed to receive only good table names.</li>
49 *    <li>Table column name handling. All attribute names are transformed
50 *    to suitable column names. Reserved words will be escaped.</li>
51 *    <li>Keeping track of transaction statistics.</li>
52 * </ul>
53 * @author Brautigam Robert
54 * @version Revision: $Revision$
55 */
56public abstract class DatabaseBase implements PersistenceEventListener, Database
57{
58   private static Logger logger = Logger.getLogger(DatabaseBase.class);
59   
60   private Map reservedWords; // Reserved words of database
61   private Map reverseReservedWords; // Reverse of translated words
62   
63   private ConnectionSource connectionSource;
64   private int maxTableNameLength;
65 
66   private Object tableNameMutex = new Object(); // Mutex for accessing table names
67   private Map tableNames; // Contains alias->realname mappings
68   private Map transactionNames; // Contains mapping for specific transaction
69 
70   private SQLStatistics sqlStatistics = null;
71   private EventDispatcher eventDispatcher = null; // Injected
72   private ManagementTracker managementTracker = null; // Injected
73 
74   /**
75    * Initialize this implementation.
76    */
77   public void init(Map parameters)
78   {
79      connectionSource=(ConnectionSource) parameters.get(
80            StoreContext.PARAM_CONNECTIONSOURCE);
81      // Init reserved words
82      readReservedWords();
83      // Register listener
84      eventDispatcher.registerListener(this);
85      // Create database table name mappings, or read them, if 
86      // they exist
87      Connection connection = null;
88      try
89      {
90         // Determine max lengths
91         connection = connectionSource.getConnection();
92         DatabaseMetaData dmd = connection.getMetaData();
93         maxTableNameLength = dmd.getMaxTableNameLength();
94         logger.debug("database says it can handle "+maxTableNameLength+" character table names.");
95         if ( maxTableNameLength == 0 )
96            maxTableNameLength = Integer.MAX_VALUE;
97         if ( maxTableNameLength < 10 )
98            throw new StoreException("database can't handle 10 charachter length table names (only "+maxTableNameLength+"). Must be Oracle or something.");
99      } catch ( StoreException e ) {
100         throw e;
101      } catch ( Exception e ) {
102         throw new StoreException("database table name mapping table could not be created.",e);
103      } finally {
104         if ( connection != null )
105            connectionSource.releaseConnection(connection);
106      }
107      // Create and register mbean
108      sqlStatistics = new SQLStatistics();
109      managementTracker.registerBean("SQLStatistics",sqlStatistics);
110   }
111 
112   /**
113    * Release all resources.
114    */
115   public void release()
116   {
117      logger.debug("releasing all connections...");
118      eventDispatcher.unregisterListener(this);
119      connectionSource.release();
120      managementTracker.deregisterBean("SQLStatistics");
121   }
122 
123   /**
124    * Get the connection source of this database.
125    */
126   public ConnectionSource getConnectionSource()
127   {
128      return connectionSource;
129   }
130 
131   /**
132    * Modifies an object already in database with given fields.
133    * @param tableName The table to save attributes to.
134    * @param keys The keys of object to save (All object entries have keys).
135    * @param attributes The attributes in form of name:value pairs.
136    */
137   public void save(Transaction transaction, String tableName, 
138         Map keys, Map attributes)
139   {
140      if ( attributes.size() != 0 )
141      {
142         TransactionStatistics stats=save(transaction.getConnection(), 
143               transformTableName(transaction,tableName), transformAttributes(keys), transformAttributes(attributes));
144         transaction.getStats().add(stats);
145         sqlStatistics.add(stats); // To accumulated sql stats
146      }
147   }
148 
149   /**
150    * Insert an object into the database.
151    * @param tableName The table to save attributes to.
152    * @param id The id of object to save (All object entries have an id).
153    * @param attributes The attributes in form of name:value pairs.
154    */
155   public void insert(Transaction transaction, String tableName, 
156         Map attributes)
157   {
158      if ( attributes.size() != 0 )
159      {
160         TransactionStatistics stats=insert(transaction.getConnection(), 
161               transformTableName(transaction,tableName), transformAttributes(attributes));
162         transaction.getStats().add(stats);
163         sqlStatistics.add(stats); // To accumulated sql stats
164      }
165   }
166 
167   /**
168    * Remove an entry from database.
169    * @param tableName The table to remove object from.
170    * @param attributes The attributes which identify the object.
171    * Equality is assumed with each attribute and it's value.
172    */
173   public void remove(Transaction transaction, String tableName,
174         Map attributes)
175   {
176      TransactionStatistics stats=remove(transaction.getConnection(), 
177            transformTableName(transaction,tableName), transformAttributes(attributes));
178      transaction.getStats().add(stats);
179      sqlStatistics.add(stats); // To accumulated sql stats
180   }
181 
182   /**
183    * Get the reverse translation of a column name.
184    */
185   private String reverseName(String name)
186   {
187      // If empty, nop
188      if ( name == null )
189         return null;
190      name = name.toLowerCase();
191      // If it's in the reverse map, return reverse name
192      String result = (String) reverseReservedWords.get(name);
193      if ( result != null )
194         return result;
195      // Fall-through
196      return name;
197   }
198 
199   /**
200    * Translate name of a column in given table.
201    */
202   private String translateName(String name)
203   {
204      // If name is empty, don't do anything
205      if ( name == null )
206         return null;
207      name = name.toLowerCase();
208      // If name is in the reverse map, than this name
209      // should not be used, because it would make the
210      // given reverse name not unique
211      if ( name.endsWith("_underscore") )
212         throw new StoreException("can not use the field name: "+name+", it is reserved for translating names");
213      // Translate name, if it's in the reserved words list
214      String result = (String) reservedWords.get(name);
215      if ( result != null )
216         return result;
217      // Fall-through, return original string
218      return name;
219   }
220 
221   /**
222    * Ensure that table exists in database.
223    * @param tableName The table to check.
224    * @param attributeTypes The attribute names together with which
225    * java class they should hold.
226    * @param create If true, create table physically, if false, only
227    * update internal representations, but do not create table.
228    */
229   public void ensureTable(Transaction transaction, String tableName,
230         Map attributeTypes, List keyAttributeNames, boolean create)
231   {
232      TransactionStatistics stats=ensureTable(transaction.getConnection(), 
233            transformTableName(transaction,tableName), transformAttributes(attributeTypes), transformAttributes(keyAttributeNames),create);
234      transaction.getStats().add(stats);
235      sqlStatistics.add(stats); // To accumulated sql stats
236   }
237 
238   /**
239    * Select objects from database as ordered list of attribute maps.
240    * @param transaction The transaction to run in.
241    * @param stmt The query statement.
242    * @param limits The limits of the result. (Offset, maximum result count)
243    * @return The result object.
244    */
245   public SearchResult search(Transaction transaction, 
246         QueryStatement stmt, Limits limits)
247   {
248      QueryStatement newStmt = new QueryStatement(stmt);
249      newStmt.setSpecifiedTerms(new HashSet(replaceTableNames(transaction,newStmt.getSpecifiedTerms())));
250      newStmt.setQueryExpression(replaceTableNames(transaction,newStmt.getQueryExpression()));
251      newStmt.setSelectTerms(replaceTableNames(transaction,newStmt.getSelectTerms()));
252      newStmt.setOrderByList(replaceOrderTableNames(transaction,newStmt.getOrderByList()));
253      if ( newStmt.getGroupByList() != null )
254         newStmt.setGroupByList(replaceTableNames(transaction,newStmt.getGroupByList()));
255      if ( newStmt.getHavingExpression() != null )
256         newStmt.setHavingExpression(replaceTableNames(transaction,newStmt.getHavingExpression()));
257      // Run query
258      SearchResult rawResult = new SearchResult();
259      TransactionStatistics stats = search(transaction.getConnection(), 
260            newStmt, limits, rawResult);
261      transaction.getStats().add(stats);
262      sqlStatistics.add(stats); // To accumulated sql stats
263      // Transform result. As the names of the select terms were altered,
264      // go through the original select terms and populate a new map based
265      // on the original names.
266      SearchResult result = new SearchResult();
267      result.setResultSize(rawResult.getResultSize());
268      List<Map> rawResultList = rawResult.getResult();
269      List<Map> resultList = new ArrayList<Map>();
270      for ( Map rawResultMap : rawResultList )
271      {
272         Map resultMap = new HashMap();
273         Set<Map.Entry<String,Object>> rawResultMapEntries = rawResultMap.entrySet();
274         for ( Map.Entry<String,Object> rawResultEntry : rawResultMapEntries )
275            resultMap.put(reverseName(rawResultEntry.getKey()),rawResultEntry.getValue());
276         resultList.add(resultMap);
277         if ( logger.isTraceEnabled() )
278            logger.trace("transforming result: "+rawResultMap+", into: "+resultMap);
279      }
280      result.setResult(resultList);
281      // Return result transformed
282      return result;
283   }
284 
285   /**
286    * Replace all table names in the list of terms.
287    */
288   private List replaceTableNames(Transaction transaction, Collection tableTerms)
289   {
290      List result = new ArrayList();
291      Iterator iterator = tableTerms.iterator();
292      while ( iterator.hasNext() )
293      {
294         TableTerm term = (TableTerm) iterator.next();
295         result.add(replaceTableName(transaction,term));
296      }
297      return result;
298   }
299 
300   /**
301    * Replace the term with a translated term.
302    */
303   private TableTerm replaceTableName(Transaction transaction, TableTerm term)
304   {
305      TableTerm newTerm = null;
306      if ( term instanceof ReferenceTerm )
307      {
308         newTerm = new ReferenceTerm((ReferenceTerm)term);
309         ((ReferenceTerm)newTerm).setColumnName(translateName(((ReferenceTerm)term).getColumnName()));
310      } else if ( term instanceof SpecifiedTableTerm ) {
311         SpecifiedTableTerm specifiedTerm = (SpecifiedTableTerm) term;
312         SpecifiedTableTerm specifiedNewTerm = new SpecifiedTableTerm(term);
313         specifiedNewTerm.setReferencedLeftTerms(new ArrayList());
314         specifiedNewTerm.setRelatedLeftTerms(new ArrayList());
315         newTerm=specifiedNewTerm;
316         // Recursively replace left terms
317         for ( int i=0; (specifiedTerm.getRelatedLeftTerms()!=null) && 
318               (i<specifiedTerm.getRelatedLeftTerms().size()); i++ )
319         {
320            SpecifiedTableTerm.LeftjoinEntry entry = (SpecifiedTableTerm.LeftjoinEntry) 
321               specifiedTerm.getRelatedLeftTerms().get(i);
322            SpecifiedTableTerm.LeftjoinEntry newEntry = new SpecifiedTableTerm.LeftjoinEntry();
323            newEntry.term = replaceTableName(transaction,entry.term);
324            newEntry.expression = replaceTableNames(transaction,entry.expression);
325            specifiedNewTerm.getRelatedLeftTerms().add(newEntry);
326         }
327         for ( int i=0; (specifiedTerm.getReferencedLeftTerms()!=null) && 
328               (i<specifiedTerm.getReferencedLeftTerms().size()); i++ )
329         {
330            SpecifiedTableTerm.LeftjoinEntry entry = (SpecifiedTableTerm.LeftjoinEntry) 
331               specifiedTerm.getReferencedLeftTerms().get(i);
332            SpecifiedTableTerm.LeftjoinEntry newEntry = new SpecifiedTableTerm.LeftjoinEntry();
333            newEntry.term = replaceTableName(transaction,entry.term);
334            newEntry.expression = replaceTableNames(transaction,entry.expression);
335            specifiedNewTerm.getReferencedLeftTerms().add(newEntry);
336         }
337      } else {
338         newTerm = new TableTerm(term);
339      }
340      newTerm.setTableName(transformTableName(transaction,term.getTableName()));
341      newTerm.setAlias(translateName(newTerm.getAlias()));
342      // Return with translated term
343      return newTerm;
344   }
345   
346   /**
347    * Replace all tables names in order by statement.
348    */
349   private List replaceOrderTableNames(Transaction transaction, List orderbys)
350   {
351      if ( orderbys == null )
352         return null;
353      ArrayList result = new ArrayList();
354      for ( int i=0; i<orderbys.size(); i++ )
355      {
356         OrderBy orderby = (OrderBy) orderbys.get(i);
357         ReferenceTerm refTerm = (ReferenceTerm) orderby.getReferenceTerm();
358         ReferenceTerm newTerm = new ReferenceTerm(refTerm);
359         newTerm.setTableName(transformTableName(transaction,refTerm.getTableName()));
360         newTerm.setColumnName(translateName(refTerm.getColumnName()));
361         result.add(new OrderBy(newTerm,orderby.getDirection()));
362      }
363      return result;
364   }
365   
366   /**
367    * Replace all table names in the expression recursively.
368    */
369   private Expression replaceTableNames(Transaction transaction, Expression expr)
370   {
371      if ( expr == null )
372         return null;
373      Expression result = new Expression();
374      for ( int i=0; i<expr.size(); i++ )
375      {
376         Object term = expr.get(i);
377         if ( term instanceof ReferenceTerm )
378         {
379            ReferenceTerm refTerm = (ReferenceTerm) term;
380            result.add(new ReferenceTerm(
381                     transformTableName(transaction,refTerm.getTableName()),
382                     translateName(refTerm.getAlias()),
383                     translateName(refTerm.getColumnName()),
384                     refTerm.getFunction()));
385         } else if ( term instanceof Expression ) {
386            result.add(replaceTableNames(transaction,(Expression) term));
387         } else
388            result.add(term);
389      }
390      return result;
391   }
392 
393   /**
394    * Transform the keys of the given list as if they were attribute names
395    * for the given table.
396    * @return A list with names transformed.
397    */
398   private List transformAttributes(List attributes)
399   {
400      ArrayList result = new ArrayList();
401      Iterator entryIterator = attributes.iterator();
402      while ( entryIterator.hasNext() )
403      {
404         String entry = (String) entryIterator.next();
405         result.add(translateName(entry));
406      }
407      return result;
408   }
409 
410   /**
411    * Transform the keys of the given map as if they were attribute names
412    * for the given table.
413    * @return A map with the same values as the given map, but the keys
414    * transformed possibly to new names.
415    */
416   private Map transformAttributes(Map attributes)
417   {
418      Map result = new HashMap();
419      Iterator entryIterator = attributes.entrySet().iterator();
420      while ( entryIterator.hasNext() )
421      {
422         Map.Entry entry = (Map.Entry) entryIterator.next();
423         result.put(translateName((String) entry.getKey()),entry.getValue());
424      }
425      return result;
426   }
427 
428   /**
429    * Get the real table name for use with database. This method
430    * transforms the name to fit database table max name length, and
431    * makes the name lower case.
432    */
433   private String transformTableName(Transaction transaction, String tableName)
434   {
435      if ( tableName.indexOf('_') < 0 ) 
436         return tableName; // Must be already translated, because no packages
437      // Check if table exists, and load
438      synchronized ( tableNameMutex )
439      {
440         if ( (tableNames==null) || (transactionNames==null) )
441         {
442            // No table yet, so check if it exists
443            HashMap tableMapAttributes = new HashMap();
444            tableMapAttributes.put("realname",String.class);
445            tableMapAttributes.put("alias",String.class);
446            ArrayList tableMapKeys = new ArrayList();
447            tableMapKeys.add("alias");
448            TransactionStatistics stats = ensureTable(transaction.getConnection(),"tablemap",tableMapAttributes,
449                  tableMapKeys, true);
450            transaction.getStats().add(stats);
451            sqlStatistics.add(stats); // To accumulated sql stats
452            // Now read the whole thing
453            QueryStatement stmt = new QueryStatement("tablemap",null,null);
454            SearchResult result = new SearchResult();
455            stats = search(transaction.getConnection(),stmt,null,result);
456            transaction.getStats().add(stats);
457            sqlStatistics.add(stats); // To accumulated sql stats
458            tableNames = new HashMap();
459            for ( int i=0; i<result.getResult().size(); i++ )
460            {
461               Map attributes = (Map) result.getResult().get(i);
462               tableNames.put(attributes.get("alias"),attributes.get("realname"));
463            }
464            // Add self
465            tableNames.put("tablemap","tablemap");
466            transactionNames = new HashMap();
467         }
468      }
469      // Check table, whether this alias exists
470      // First check in transaction table map,
471      // then in global transaction map
472      String tableNameCooked = tableName.toLowerCase();
473      String realName = getTableName(transaction,tableNameCooked);
474      if ( realName != null )
475         return realName;
476      if ( logger.isDebugEnabled() )
477      {
478         synchronized ( tableNames )
479         {
480            logger.debug("could not find table alias: "+tableNameCooked+" from: "+tableNames+", will create it.");
481         }
482      }
483      // Ok, name does not exist yet, so create real name
484      // for this alias.
485      // First check whether simple names are approriate
486      // so hu.netmind.beankeeper_Book becomes simply 'book'.
487      logger.debug("could not find computed name for preliminary table name: "+tableNameCooked+", calculating one.");
488      String tableNameSimple;
489      int lastIndex = tableNameCooked.length();
490      if ( tableNameCooked.endsWith("_") )
491      {
492         // This is a subtable, so inlcude the previous tag too
493         if ( tableNameCooked.length() < 2 )
494            throw new StoreException("table name too short: "+tableNameCooked);
495         lastIndex = tableNameCooked.lastIndexOf('_',tableNameCooked.length()-2);
496         if ( lastIndex <= 0 )
497            throw new StoreException("table name ends with '_', but has no parent: "+tableNameCooked);
498      }
499      lastIndex = tableNameCooked.lastIndexOf('_',lastIndex-1);
500      if ( lastIndex != -1 )
501         tableNameSimple = tableNameCooked.substring(lastIndex+1);
502      else
503         tableNameSimple = tableNameCooked;
504      // Check, whether simple name is a reserved word. If it
505      // is, then translate it.
506      tableNameSimple = translateName(tableNameSimple);
507      // Check now, whether simple name is good, if not, then
508      // extend it with package names.
509      // If name becomes too long, then use numbers to distinguish
510      logger.debug("trying simple name: "+tableNameSimple);
511      while ( (tableNameSimple.length()<maxTableNameLength) &&
512            (!tableNameSimple.startsWith("_")) &&
513            (isRealTableNameTaken(transaction,tableNameSimple)) &&
514            (lastIndex > 0) )
515      {
516         // This means table name is still not unambigous,
517         // but at least it's short, so add another package
518         // back to the simple name
519         lastIndex = tableNameCooked.lastIndexOf('_',lastIndex-1);
520         tableNameSimple = tableNameCooked.substring(lastIndex+1);
521      }
522      logger.debug("final simple name: "+tableNameSimple);
523      // If name became too long, or still not unambigous, then
524      // add number to the end
525      String newTableName = tableNameSimple;
526      for ( int index=0; 
527            (newTableName.length()>maxTableNameLength) ||
528            (getTableName(transaction,newTableName)!=null) ; 
529            index++ )
530      {
531         if ( index>1000 )
532            throw new StoreException("something is wrong, could not calculate unabigous name for: "+tableNameCooked);
533         newTableName = tableNameSimple.substring(0,maxTableNameLength-3)+index;
534      }
535      tableNameSimple = newTableName;
536      // Ok, so far so good. tableNameSimple now contains an unambigous
537      // appropriately short name for given alias, now only insert, then
538      // append to table map and return.
539      // There is a little dirty trick though. Before inserting a class,
540      // first remove it from the table. This work arounds a problem:
541      // if two nodes are active, both start without knowning a class,
542      // then the first inserts it, the second can not, because now it
543      // already is contained in the database.
544      logger.debug("translated table name: "+tableNameCooked+" to: "+tableNameSimple);
545      Map insertTableName = new HashMap();
546      insertTableName.put("alias",tableNameCooked);
547      TransactionStatistics stats = remove(transaction.getConnection(),"tablemap",insertTableName);
548      transaction.getStats().add(stats);
549      sqlStatistics.add(stats); // To accumulated sql stats
550      insertTableName.put("realname",tableNameSimple);
551      stats = insert(transaction.getConnection(),"tablemap",insertTableName);
552      transaction.getStats().add(stats);
553      sqlStatistics.add(stats); // To accumulated sql stats
554      synchronized ( tableNameMutex )
555      {
556         Map transactionTable = (Map) transactionNames.get(transaction);
557         if ( transactionTable == null )
558         {
559            transactionTable = new HashMap();
560            transactionNames.put(transaction,transactionTable);
561         }
562         transactionTable.put(tableNameCooked,tableNameSimple);
563      }
564      // Return already
565      return tableNameSimple;
566   }
567 
568   /**
569    * Check if table name is taken.
570    */
571   private boolean isRealTableNameTaken(Transaction transaction, String tableName)
572   {
573      synchronized ( tableNameMutex )
574      {
575         if ( tableNames.containsValue(tableName) )
576            return true;
577         Map transactionTable = (Map) transactionNames.get(transaction);
578         if ( (transactionTable!=null) && (transactionTable.containsValue(tableName)) )
579            return true;
580         return false;
581      }
582   }
583   
584   /**
585    * Check whether that alias is already assigned a real table name,
586    * and returns that name.
587    */
588   private String getTableName(Transaction transaction, String alias)
589   {
590      synchronized ( tableNameMutex )
591      {
592         Map transactionTable = (Map) transactionNames.get(transaction);
593         if ( (transactionTable!=null) && (transactionTable.get(alias)!=null) )
594            return (String) transactionTable.get(alias);
595         if ( tableNames.get(alias) != null )
596            return (String) tableNames.get(alias);
597         return null;
598      }
599   }
600 
601   /**
602    * Activate or discard table names added in the transaction.
603    */
604   public void handle(PersistenceEvent event)
605   {
606      if ( ! (event instanceof TransactionEvent) )
607         return; // Quick exit
608      Transaction transaction = ((TransactionEvent) event).getTransaction();
609      if ( event instanceof TransactionCommittedEvent )
610      {
611         synchronized ( tableNameMutex )
612         {
613            Map transactionTables = (Map) transactionNames.get(transaction);
614            if ( transactionTables == null )
615               return;
616            tableNames.putAll(transactionTables);
617            transactionNames.remove(transaction);
618         }
619      }
620      if ( event instanceof TransactionRolledbackEvent )
621      {
622         synchronized ( tableNameMutex )
623         {
624            transactionNames.remove(transaction);
625         }
626      }
627   }
628 
629   /**
630    * Read the reserved word list.
631    */
632   private void readReservedWords()
633   {
634      reverseReservedWords = new HashMap();
635      reservedWords = new HashMap();
636      // Read from list
637      try
638      {
639         ClassLoader loader = Database.class.getClassLoader();
640         BufferedReader reader = new BufferedReader(new InputStreamReader(loader.getResourceAsStream("reserved.words")));
641         String line = null;
642         String source;
643         String target;
644         String obscure;
645         while ( (line=reader.readLine()) != null )
646         {
647            source = line.toLowerCase();
648            target = source+"_";
649            // Put in the normal fields
650            reservedWords.put(source,target);
651            reverseReservedWords.put(target,source);
652            // Make a transation for the reverse words, maybe user wants
653            // to use those, escape them to an obscure name. If user wants
654            // to use the obscure name, she'll get an error.
655            obscure = source+"_underscore";
656            reservedWords.put(target,obscure);
657            reverseReservedWords.put(obscure,target);
658         }
659      } catch ( Exception e ) {
660         throw new StoreException("error while reading reserved words list",e);
661      }
662   }
663 
664   
665   /**
666    * Modifies an object already in database with given fields.
667    * @param tableName The table to save attributes to.
668    * @param id The id of object to save (All object entries have an id).
669    * @param attributes The attributes in form of name:value pairs.
670    */
671   protected abstract TransactionStatistics save(Connection connection, String tableName, 
672         Map keys, Map attributes);
673 
674   /**
675    * Insert an object into the database.
676    * @param tableName The table to save attributes to.
677    * @param attributes The attributes in form of name:value pairs.
678    */
679   protected abstract TransactionStatistics insert(Connection connection, String tableName, 
680         Map attributes);
681 
682   /**
683    * Remove an entry from database.
684    * @param tableName The table to remove object from.
685    * @param attributes The attributes which identify the object.
686    * Equality is assumed with each attribute and it's value.
687    */
688   protected abstract TransactionStatistics remove(Connection connection, String tableName,
689         Map attributes);
690   
691   /**
692    * Ensure that table exists in database. It is the responsibility of
693    * the implementation to ensure that the named table with given
694    * parameters exists. If the table exists, but is not defined
695    * as in 'attributeTypes', the implementation <strong>must</strong>
696    * retain all common attributes during the re-structuring of that
697    * table. Of course, if no common attributes exist, the implementation
698    * is free to drop the table and recreate it.<br>
699    * In every database, the field named 'persistence_id' is the primary
700    * key if it exists, or no primary key if the attributeTypes do not
701    * contain it.<br>
702    * All columns will be indexed by default.
703    * @param tableName The table to check.
704    * @param attributeTypes The attribute names together with which
705    * java class they should hold.
706    * @param create If true, create table physically, if false, only
707    * update internal representations, but do not create table.
708    */
709   protected abstract TransactionStatistics ensureTable(Connection connection, String tableName,
710         Map attributeTypes, List keyAttributeNames, boolean create);
711   
712   /**
713    * Select objects from database as ordered list of attribute maps.
714    * @param connection The connection to use.
715    * @param stmt The query statement.
716    * @param limits The limits of the result. (Offset, maximum result count)
717    * @param result The result object. It will be filled with data.
718    */
719   protected abstract TransactionStatistics search(Connection connection, QueryStatement stmt, 
720         Limits limits, SearchResult result);
721}
722 
723 

[all classes][hu.netmind.beankeeper.db.impl]
EMMA 2.0.5312debian (C) Vladimir Roubtsov