View Javadoc

1   // 
2   //Copyright (c) 2003, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
3   //All rights reserved. 
4   //   
5   //Redistribution and use in source and binary forms, with or without modification,  
6   //are permitted provided that the following conditions are met: 
7   //   
8   //* Redistributions of source code must retain the above copyright notice,  
9   //this list of conditions and the following disclaimer. 
10  //* Redistributions in binary form must reproduce the above copyright notice,  
11  //this list of conditions and the following disclaimer in the documentation  
12  //and/or other materials provided with the distribution. 
13  //* Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.  
14  //nor the names of its contributors may be used to endorse or promote products  
15  //derived from this software without specific prior written permission. 
16  // 
17  //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
18  //AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
19  //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
20  //IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  
21  //INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  
22  //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
23  //OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
24  //WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
25  //ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
26  //POSSIBILITY OF SUCH DAMAGE. 
27  //
28  
29  package org.objectledge.database.persistence;
30  
31  import java.sql.Connection;
32  import java.sql.PreparedStatement;
33  import java.sql.ResultSet;
34  import java.sql.Statement;
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  import org.jcontainer.dna.Logger;
39  import org.objectledge.database.Database;
40  import org.objectledge.database.DatabaseUtils;
41  
42  /**
43   * Provides Object-Relational DB mapping.
44   * 
45   * @author <a href="mailto:rafal@caltha.pl">Rafal Krzewski</a>
46   * @author <a href="mailto:pablo@caltha.pl">Pawel Potempski</a>
47   * @version $Id: DefaultPersistence.java,v 1.5 2005/05/05 11:13:02 rafal Exp $
48   */
49  public class DefaultPersistence implements Persistence
50  {
51      /*** The Database. */
52      private Database database;
53      
54      /*** The logger */
55      private Logger logger;
56  
57      /***
58       * Component constructor.
59       * 
60       * @param database the database.
61       * @param logger the logger.
62       */
63      public DefaultPersistence(Database database, Logger logger)
64      {
65          this.logger = logger;
66          this.database = database;
67      }
68  
69      // PersistenceSystem interface //////////////////////////////////////////
70  
71      /***
72       * Loads an object from the database.
73       *
74       * @param id the identifier of the object.
75       * @param factory the object instance factory.
76       * @return the presistent object.
77       * @throws PersistenceException if any exception occured.
78       */
79      public Persistent load(long id, PersistentFactory factory) throws PersistenceException
80      {
81          Connection conn = null;
82          try
83          {
84              conn = database.getConnection();
85              Persistent obj = factory.newInstance();
86              PreparedStatement statement = DefaultInputRecord.getSelectStatement(id, obj, conn);
87              ResultSet rs = statement.executeQuery();
88              if (!rs.next())
89              {
90                  return null;
91              }
92              InputRecord record = new DefaultInputRecord(rs);
93              obj.setData(record);
94              obj.setSaved(record.getLong(obj.getKeyColumns()[0]));
95              return obj;
96          }
97          catch (Exception e)
98          {
99              throw new PersistenceException("Failed to retrieve object", e);
100         }
101         finally
102         {
103             DatabaseUtils.close(conn);
104         }
105     }
106 
107     /***
108      * Loads objects from the database.
109      *
110      * <p>Note that joins are not supported. This package provides a means of
111      * converting objects to rows in a table and vice versa. If you want more,
112      * you need some different tool.</p>
113      *
114      * @param where the where clause to be used in the query
115      * @param factory the object instance factory.
116      * @return the list of presistent objects.
117      * @throws PersistenceException if any exception occured.
118      */
119     public List load(String where, PersistentFactory factory) throws PersistenceException
120     {
121         Connection conn = null;
122         try
123         {
124             conn = database.getConnection();
125             Persistent obj = factory.newInstance();
126             PreparedStatement statement = DefaultInputRecord.getSelectStatement(where, obj, conn);
127             ResultSet rs = statement.executeQuery();
128             InputRecord record = new DefaultInputRecord(rs);
129             ArrayList list = new ArrayList();
130             while (rs.next())
131             {
132                 obj.setData(record);
133                 obj.setSaved(record.getLong(obj.getKeyColumns()[0]));
134                 list.add(obj);
135                 obj = factory.newInstance();
136             }
137             return list;
138         }
139         catch (Exception e)
140         {
141             throw new PersistenceException("Failed to retrieve object", e);
142         }
143         finally
144         {
145             DatabaseUtils.close(conn);
146         }
147     }
148 
149     /***
150      * Saves an object in the database.
151      *
152      * @param object the object to be saved.
153      * @throws PersistenceException if any exception occured.
154      */
155     public void save(Persistent object) throws PersistenceException
156     {
157         synchronized (object)
158         {
159             OutputRecord record = new DefaultOutputRecord(object);
160             String table = object.getTable();
161             String[] keys = object.getKeyColumns();
162             object.getData(record);
163             Connection conn = null;
164 
165             try
166             {
167                 conn = database.getConnection();
168                 PreparedStatement statement;
169                 if (object.getSaved())
170                 {
171                     statement = record.getUpdateStatement(conn);
172                     statement.execute();
173                 }
174                 else
175                 {
176                     long id;
177                     if (keys.length == 1)
178                     {
179                         id = database.getNextId(table);
180                         record.setLong(keys[0], id);
181                     }
182                     else
183                     {
184                         id = -1;
185                     }
186                     statement = record.getInsertStatement(conn);
187                     statement.execute();
188                     object.setSaved(id);
189                 }
190             }
191             catch (Exception e)
192             {
193                 throw new PersistenceException("Failed to save object", e);
194             }
195             finally
196             {
197                 DatabaseUtils.close(conn);
198             }
199         }
200     }
201 
202     /***
203      * Reverts the object to the saved state.
204      *
205      * @param object the object to have it's state restored.
206      * @throws PersistenceException if any exception occured.
207      * @throws IllegalStateException if no state has been saved yet for the
208      *         object in question.
209      */
210     public void revert(Persistent object) throws PersistenceException, IllegalStateException
211     {
212         synchronized (object)
213         {
214             if (!object.getSaved())
215             {
216                 throw new IllegalStateException("no state has been saved yet");
217             }
218             Connection conn = null;
219             try
220             {
221                 conn = database.getConnection();
222                 PreparedStatement statement = DefaultInputRecord.getSelectStatements(object, conn);
223                 ResultSet rs = statement.executeQuery();
224                 if (!rs.next())
225                 {
226                     throw new PersistenceException("saved state was lost");
227                 }
228                 InputRecord irecord = new DefaultInputRecord(rs);
229                 object.setData(irecord);
230             }
231             catch (Exception e)
232             {
233                 throw new PersistenceException("failed to revert object's state", e);
234             }
235             finally
236             {
237                 DatabaseUtils.close(conn);
238             }
239         }
240     }
241 
242     /***
243      * Removes an object from the database.
244      *
245      * @param object the object to be removed.
246      * @throws PersistenceException if any exception occured.
247      */
248     public void delete(Persistent object) throws PersistenceException
249     {
250         synchronized (object)
251         {
252             Connection conn = null;
253             try
254             {
255                 conn = database.getConnection();
256                 OutputRecord record = new DefaultOutputRecord(object);
257                 object.getData(record);
258                 PreparedStatement statement = record.getDeleteStatement(conn); 
259                 statement.execute();
260                 if(statement.getUpdateCount() != 1)
261                 {
262                     throw new PersistenceException("unsuccessful DELETE statement");
263                 }
264             }
265             catch (Exception e)
266             {
267                 if(e instanceof PersistenceException)
268                 {
269                     throw (PersistenceException)e;
270                 }
271                 throw new PersistenceException("Failed to delete object", e);
272             }
273             finally
274             {
275                 DatabaseUtils.close(conn);
276             }
277         }
278     }
279 
280     /***
281      * Removes the objects from the database.
282      *
283      * @param where the where clause to be used in the query
284      * @param factory the object instance factory.
285      * @throws PersistenceException if any exception occured.
286      */
287     public void delete(String where, PersistentFactory factory) throws PersistenceException
288     {
289         Connection conn = null;
290         try
291         {
292             conn = database.getConnection();
293             Persistent obj = factory.newInstance();
294             PreparedStatement statement = conn.prepareStatement("DELETE FROM " + obj.getTable() + 
295                                                                 " WHERE " + where);
296             statement.executeUpdate();
297         }
298         catch (Exception e)
299         {
300             throw new PersistenceException("Failed to retrieve object", e);
301         }
302         finally
303         {
304             DatabaseUtils.close(conn);
305         }        
306     }
307     
308     /***
309      * An utility method for checking for existence of rows.
310      *
311      * @param table the table to be checked.
312      * @param where the condition.
313      * @return <code>true</code> if the <code>condition</code> is true for one
314      *         or more rows in the <code>table</code>.
315      * @throws PersistenceException if any exception occured.
316      */
317     public boolean exists(String table, String where) throws PersistenceException
318     {
319         Connection conn = null;
320         try
321         {
322             conn = database.getConnection();
323             Statement statement = conn.createStatement();
324             ResultSet rs;
325             if (where != null)
326             {
327                 rs = statement.executeQuery("SELECT DISTINCT 1 FROM " + table + " WHERE " + where);
328             }
329             else
330             {
331                 rs = statement.executeQuery("SELECT DISTINCT 1 FROM " + table);
332             }
333             return (rs.next());
334         }
335         catch (Exception e)
336         {
337             throw new PersistenceException("Failed to execute query", e);
338         }
339         finally
340         {
341             DatabaseUtils.close(conn);
342         }
343     }
344 
345     /***
346      * An utility method for checking the number of matching rows.
347      *
348      * @param table the table to be chcked.
349      * @param where the condition.
350      * @return the number of <code>table</code> matching the condition.
351      * @throws PersistenceException if any exception occured.
352      */
353     public int count(String table, String where) throws PersistenceException
354     {
355         Connection conn = null;
356         try
357         {
358             conn = database.getConnection();
359             Statement statement = conn.createStatement();
360             ResultSet rs;
361             if (where != null)
362             {
363                 rs = statement.executeQuery("SELECT COUNT(*) FROM " + table + " WHERE " + where);
364             }
365             else
366             {
367                 rs = statement.executeQuery("SELECT COUNT(*) FROM " + table);
368             }
369             if (!rs.next())
370             {
371                 throw new PersistenceException("internal error - no data???");
372             }
373             return (rs.getInt(1));
374         }
375         catch (Exception e)
376         {
377             throw new PersistenceException("Failed to execute query", e);
378         }
379         finally
380         {
381             DatabaseUtils.close(conn);
382         }
383     }
384     
385     /***
386      * {@inheritDoc}
387      */
388     public Database getDatabase()
389     {
390         return database;
391     }
392 }