KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc2 > schema > EntityTable


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc2.schema;
23
24 import org.jboss.deployment.DeploymentException;
25 import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
26 import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
27 import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
28 import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
29 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
30 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
31 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
32 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCTypeMappingMetaData;
33 import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCEntityBridge2;
34 import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMPFieldBridge2;
35 import org.jboss.logging.Logger;
36 import org.jboss.mx.util.MBeanServerLocator;
37 import org.jboss.mx.util.MBeanProxyExt;
38 import org.jboss.system.ServiceControllerMBean;
39 import org.jboss.system.Registry;
40 import org.jboss.metadata.ConfigurationMetaData;
41 import org.jboss.metadata.MetaData;
42 import org.jboss.metadata.EntityMetaData;
43 import org.jboss.cache.invalidation.InvalidationManagerMBean;
44 import org.jboss.cache.invalidation.InvalidationGroup;
45 import org.w3c.dom.Element JavaDoc;
46
47 import javax.naming.InitialContext JavaDoc;
48 import javax.naming.NamingException JavaDoc;
49 import javax.sql.DataSource JavaDoc;
50 import javax.ejb.DuplicateKeyException JavaDoc;
51 import javax.ejb.EJBException JavaDoc;
52 import javax.ejb.NoSuchEntityException JavaDoc;
53 import javax.ejb.NoSuchObjectLocalException JavaDoc;
54 import javax.transaction.Transaction JavaDoc;
55 import javax.management.MBeanServer JavaDoc;
56 import javax.management.ObjectName JavaDoc;
57 import java.sql.Connection JavaDoc;
58 import java.sql.PreparedStatement JavaDoc;
59 import java.sql.SQLException JavaDoc;
60 import java.sql.ResultSet JavaDoc;
61 import java.util.Map JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.ArrayList JavaDoc;
64 import java.util.List JavaDoc;
65
66
67 /**
68  * todo refactor optimistic locking
69  *
70  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
71  * @version <tt>$Revision: 58402 $</tt>
72  */

73 public class EntityTable
74    implements Table
75 {
76    private static final byte UNREFERENCED = 0;
77    private static final byte CLEAN = 1;
78    private static final byte DIRTY = 2;
79    private static final byte CREATED = 4;
80    private static final byte DELETED = 8;
81    private static final byte DIRTY_RELATIONS = 16;
82
83    private static final Object JavaDoc NOT_LOADED = new Object JavaDoc();
84
85    private JDBCEntityBridge2 entity;
86    private String JavaDoc tableName;
87    private int fieldsTotal;
88    private int relationsTotal;
89    private DataSource JavaDoc dataSource;
90    private Schema schema;
91    private int tableId;
92    private boolean dontFlushCreated;
93
94    private String JavaDoc deleteSql;
95    private String JavaDoc updateSql;
96    private String JavaDoc insertSql;
97    private String JavaDoc selectSql;
98    private String JavaDoc duplicatePkSql;
99
100    private final CommitStrategy insertStrategy;
101    private final CommitStrategy deleteStrategy;
102    private final CommitStrategy updateStrategy;
103
104    private Logger log;
105
106    private Cache cache;
107    private ServiceControllerMBean serviceController;
108    private ObjectName JavaDoc cacheName;
109
110    private int[] references;
111    private int[] referencedBy;
112
113    private ForeignKeyConstraint[] fkConstraints;
114
115    private CacheInvalidator cacheInvalidator;
116
117    public EntityTable(JDBCEntityMetaData metadata, JDBCEntityBridge2 entity, Schema schema, int tableId)
118       throws DeploymentException
119    {
120       try
121       {
122          InitialContext JavaDoc ic = new InitialContext JavaDoc();
123          dataSource = (DataSource JavaDoc) ic.lookup(metadata.getDataSourceName());
124       }
125       catch(NamingException JavaDoc e)
126       {
127          throw new DeploymentException("Filed to lookup: " + metadata.getDataSourceName(), e);
128       }
129
130       this.entity = entity;
131       tableName = SQLUtil.fixTableName(metadata.getDefaultTableName(), dataSource);
132       log = Logger.getLogger(getClass().getName() + "." + tableName);
133
134       this.schema = schema;
135       this.tableId = tableId;
136
137       final EntityMetaData entityMetaData = ((EntityMetaData)entity.getContainer().getBeanMetaData());
138       final ConfigurationMetaData containerConf = entityMetaData.getContainerConfiguration();
139       dontFlushCreated = containerConf.isInsertAfterEjbPostCreate();
140
141       // create cache
142
final Element JavaDoc cacheConf = containerConf.getContainerCacheConf();
143       final Element JavaDoc cachePolicy = cacheConf == null ? null : MetaData.getOptionalChild(cacheConf, "cache-policy-conf");
144
145       int minCapacity;
146       int maxCapacity;
147       if(cachePolicy != null)
148       {
149          String JavaDoc str = MetaData.getOptionalChildContent(cachePolicy, "min-capacity");
150          minCapacity = (str == null ? 1000 : Integer.parseInt(str));
151          str = MetaData.getOptionalChildContent(cachePolicy, "max-capacity");
152          maxCapacity = (str == null ? 10000 : Integer.parseInt(str));
153       }
154       else
155       {
156          minCapacity = 1000;
157          maxCapacity = 10000;
158       }
159
160       final Element JavaDoc otherConf = cacheConf == null ? null : MetaData.getOptionalChild(cacheConf, "cache-policy-conf-other");
161
162       int partitionsTotal;
163       final boolean invalidable;
164       final Element JavaDoc batchCommitStrategy;
165       if(otherConf != null)
166       {
167          String JavaDoc str = MetaData.getOptionalChildContent(otherConf, "partitions");
168          partitionsTotal = (str == null ? 10 : Integer.parseInt(str));
169          batchCommitStrategy = MetaData.getOptionalChild(otherConf, "batch-commit-strategy");
170          invalidable = MetaData.getOptionalChild(otherConf, "invalidable") == null ? false : true;
171       }
172       else
173       {
174          partitionsTotal = 10;
175          batchCommitStrategy = null;
176          invalidable = false;
177       }
178
179       if(cachePolicy != null)
180       {
181          cache = new PartitionedTableCache(minCapacity, maxCapacity, partitionsTotal);
182
183          String JavaDoc periodStr = MetaData.getOptionalChildContent(cachePolicy, "overager-period");
184          String JavaDoc maxAgeStr = MetaData.getOptionalChildContent(cachePolicy, "max-bean-age");
185          if(periodStr != null && maxAgeStr == null || maxAgeStr != null && periodStr == null)
186          {
187             throw new DeploymentException(
188                "Failed to initialize age-out thread for entity " + entity.getEntityName() +
189                ": overager-period or max-bean-age is missing!");
190          }
191          else if(periodStr != null && maxAgeStr != null)
192          {
193             long period = Long.parseLong(periodStr);
194             long maxAge = Long.parseLong(maxAgeStr);
195             ((PartitionedTableCache)cache).initOverager(period, maxAge, entity.getEntityName() + " overager");
196
197             if(log.isTraceEnabled())
198             {
199                log.trace("initialized age-out thread for " + entity.getEntityName() +
200                   ": overager-period=" + period + ", max-bean-age=" + maxAge);
201             }
202          }
203
204          final MBeanServer JavaDoc server = MBeanServerLocator.locateJBoss();
205          serviceController = (ServiceControllerMBean)
206             MBeanProxyExt.create(ServiceControllerMBean.class,
207                ServiceControllerMBean.OBJECT_NAME,
208                server);
209          try
210          {
211             cacheName =
212                new ObjectName JavaDoc("jboss.cmp:service=tablecache,ejbname=" + metadata.getName() + ",table=" + tableName);
213             server.registerMBean(cache, cacheName);
214             serviceController.create(cacheName);
215          }
216          catch(Exception JavaDoc e)
217          {
218             throw new DeploymentException("Failed to register table cache for " + tableName, e);
219          }
220       }
221       else
222       {
223          cache = Cache.NONE;
224       }
225
226       if(invalidable)
227       {
228          String JavaDoc groupName = entityMetaData.getDistributedCacheInvalidationConfig().getInvalidationGroupName();
229          String JavaDoc imName = entityMetaData.getDistributedCacheInvalidationConfig().getInvalidationManagerName();
230
231          InvalidationManagerMBean im = (InvalidationManagerMBean) Registry.lookup(imName);
232          InvalidationGroup invalidationGroup = im.getInvalidationGroup(groupName);
233
234          cacheInvalidator = new CacheInvalidator(cache, entity.getContainer().getTransactionManager(), invalidationGroup);
235       }
236
237       if(batchCommitStrategy == null)
238       {
239          insertStrategy = NON_BATCH_UPDATE;
240          deleteStrategy = NON_BATCH_UPDATE;
241          updateStrategy = NON_BATCH_UPDATE;
242       }
243       else
244       {
245          log.debug("batch-commit-strategy enabled");
246          insertStrategy = BATCH_UPDATE;
247          deleteStrategy = BATCH_UPDATE;
248          updateStrategy = BATCH_UPDATE;
249       }
250    }
251
252    public void start() throws DeploymentException
253    {
254       final JDBCAbstractCMRFieldBridge[] cmrFields = entity.getCMRFields();
255       relationsTotal = (cmrFields != null ? cmrFields.length : 0);
256
257       JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
258       JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
259
260       // DELETE SQL
261
deleteSql = "delete from " + tableName + " where ";
262       deleteSql += pkFields[0].getColumnName() + "=?";
263       for(int i = 1; i < pkFields.length; ++i)
264       {
265          deleteSql += " and " + pkFields[i].getColumnName() + "=?";
266       }
267       log.debug("delete sql: " + deleteSql);
268
269       // INSERT SQL
270
insertSql = "insert into " + tableName + "(";
271       insertSql += tableFields[0].getColumnName();
272       for(int i = 1; i < tableFields.length; ++i)
273       {
274          insertSql += ", " + tableFields[i].getColumnName();
275       }
276       insertSql += ") values (?";
277       for(int i = 1; i < tableFields.length; ++i)
278       {
279          insertSql += ", ?";
280       }
281       insertSql += ")";
282       log.debug("insert sql: " + insertSql);
283
284       // UPDATE SQL
285
updateSql = "update " + tableName + " set ";
286       int setFields = 0;
287       for(int i = 0; i < tableFields.length; ++i)
288       {
289          JDBCCMPFieldBridge2 field = tableFields[i];
290          if(!field.isPrimaryKeyMember())
291          {
292             if(setFields++ > 0)
293             {
294                updateSql += ", ";
295             }
296             updateSql += field.getColumnName() + "=?";
297          }
298       }
299       updateSql += " where ";
300       updateSql += pkFields[0].getColumnName() + "=?";
301       for(int i = 1; i < pkFields.length; ++i)
302       {
303          updateSql += " and " + pkFields[i].getColumnName() + "=?";
304       }
305
306       if(entity.getVersionField() != null)
307       {
308          updateSql += " and " + entity.getVersionField().getColumnName() + "=?";
309       }
310       log.debug("update sql: " + updateSql);
311
312       // SELECT SQL
313
String JavaDoc selectColumns = tableFields[0].getColumnName();
314       for(int i = 1; i < tableFields.length; ++i)
315       {
316          JDBCCMPFieldBridge2 field = tableFields[i];
317          selectColumns += ", " + field.getColumnName();
318       }
319
320       String JavaDoc whereColumns = pkFields[0].getColumnName() + "=?";
321       for(int i = 1; i < pkFields.length; ++i)
322       {
323          whereColumns += " and " + pkFields[i].getColumnName() + "=?";
324       }
325
326       if(entity.getMetaData().hasRowLocking())
327       {
328          JDBCEntityPersistenceStore manager = entity.getManager();
329          JDBCTypeFactory typeFactory = manager.getJDBCTypeFactory();
330          JDBCTypeMappingMetaData typeMapping = typeFactory.getTypeMapping();
331          JDBCFunctionMappingMetaData rowLockingTemplate = typeMapping.getRowLockingTemplate();
332          if(rowLockingTemplate == null)
333          {
334             throw new DeploymentException("Row locking template is not defined for mapping: " + typeMapping.getName());
335          }
336
337          selectSql = rowLockingTemplate.getFunctionSql(new Object JavaDoc[]{selectColumns, tableName, whereColumns, null},
338             new StringBuffer JavaDoc()).toString();
339       }
340       else
341       {
342          selectSql = "select ";
343          selectSql += selectColumns;
344          selectSql += " from " + tableName + " where ";
345          selectSql += whereColumns;
346       }
347       log.debug("select sql: " + selectSql);
348
349       // DUPLICATE KEY
350
if(dontFlushCreated)
351       {
352          duplicatePkSql = "select ";
353          duplicatePkSql += pkFields[0].getColumnName();
354          for(int i = 1; i < pkFields.length; ++i)
355          {
356             duplicatePkSql += ", " + pkFields[i].getColumnName();
357          }
358          duplicatePkSql += " from " + tableName + " where ";
359          duplicatePkSql += pkFields[0].getColumnName() + "=?";
360          for(int i = 1; i < pkFields.length; ++i)
361          {
362             duplicatePkSql += " and " + pkFields[i].getColumnName() + "=?";
363          }
364          log.debug("duplicate pk sql: " + duplicatePkSql);
365       }
366
367       if(cacheName != null)
368       {
369          try
370          {
371             serviceController.start(cacheName);
372          }
373          catch(Exception JavaDoc e)
374          {
375             throw new DeploymentException("Failed to start table cache.", e);
376          }
377       }
378    }
379
380    public void stop() throws Exception JavaDoc
381    {
382       if(cacheInvalidator != null)
383       {
384          cacheInvalidator.unregister();
385       }
386
387       if(cacheName != null)
388       {
389          serviceController.stop(cacheName);
390          serviceController.destroy(cacheName);
391          serviceController.remove(cacheName);
392       }
393       serviceController = null;
394    }
395
396    public StringBuffer JavaDoc appendColumnNames(JDBCCMPFieldBridge2[] fields, String JavaDoc alias, StringBuffer JavaDoc buf)
397    {
398       for(int i = 0; i < fields.length; ++i)
399       {
400          if(i > 0)
401          {
402             buf.append(", ");
403          }
404
405          if(alias != null)
406          {
407             buf.append(alias).append(".");
408          }
409
410          buf.append(fields[i].getColumnName());
411       }
412
413       return buf;
414    }
415
416    public void addField()
417    {
418       ++fieldsTotal;
419    }
420
421    public int addVersionField()
422    {
423       return fieldsTotal++;
424    }
425
426    public ForeignKeyConstraint addFkConstraint(JDBCCMPFieldBridge2[] fkFields, EntityTable referenced)
427    {
428       addReference(referenced);
429       referenced.addReferencedBy(this);
430
431       if(fkConstraints == null)
432       {
433          fkConstraints = new ForeignKeyConstraint[1];
434       }
435       else
436       {
437          ForeignKeyConstraint[] tmp = fkConstraints;
438          fkConstraints = new ForeignKeyConstraint[tmp.length + 1];
439          System.arraycopy(tmp, 0, fkConstraints, 0, tmp.length);
440       }
441       final int fkindex = fkConstraints.length - 1;
442       final ForeignKeyConstraint fkc = new ForeignKeyConstraint(fkindex, fkFields);
443       fkConstraints[fkindex] = fkc;
444       return fkc;
445    }
446
447    public DataSource JavaDoc getDataSource()
448    {
449       return dataSource;
450    }
451
452    public Object JavaDoc loadRow(ResultSet JavaDoc rs, boolean searchableOnly)
453    {
454       View view = getView();
455       Object JavaDoc pk = view.loadPk(rs);
456       if(pk != null)
457       {
458          view.loadRow(rs, pk, searchableOnly);
459       }
460       else if(log.isTraceEnabled())
461       {
462          log.trace("loaded pk is null.");
463       }
464       return pk;
465    }
466
467    public Row getRow(Object JavaDoc id)
468    {
469       return getView().getRow(id);
470    }
471
472    public boolean hasRow(Object JavaDoc id)
473    {
474       return getView().hasRow(id);
475    }
476
477    public Row loadRow(Object JavaDoc id) throws SQLException JavaDoc
478    {
479       View view = getView();
480
481       Row row = view.getRowByPk(id, false);
482       if(row != null)
483       {
484          if(log.isTraceEnabled())
485          {
486             log.trace("row is already loaded: pk=" + id);
487          }
488          return row;
489       }
490
491       JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
492
493       Connection JavaDoc con = null;
494       PreparedStatement JavaDoc ps = null;
495       ResultSet JavaDoc rs = null;
496       try
497       {
498          if(log.isDebugEnabled())
499          {
500             log.debug("executing sql: " + selectSql);
501          }
502
503          con = dataSource.getConnection();
504          ps = con.prepareStatement(selectSql);
505
506          int paramInd = 1;
507          for(int i = 0; i < pkFields.length; ++i)
508          {
509             JDBCCMPFieldBridge2 pkField = pkFields[i];
510             Object JavaDoc pkValue = pkField.getPrimaryKeyValue(id);
511             paramInd = pkField.setArgumentParameters(ps, paramInd, pkValue);
512          }
513
514          rs = ps.executeQuery();
515
516          if(!rs.next())
517          {
518             throw new NoSuchEntityException JavaDoc("Row not found: " + id);
519          }
520
521          return view.loadRow(rs, id, false);
522       }
523       catch(SQLException JavaDoc e)
524       {
525          log.error("Failed to load row: table=" + tableName + ", pk=" + id);
526          throw e;
527       }
528       finally
529       {
530          JDBCUtil.safeClose(rs);
531          JDBCUtil.safeClose(ps);
532          JDBCUtil.safeClose(con);
533       }
534    }
535
536    // Table implementation
537

538    public int getTableId()
539    {
540       return tableId;
541    }
542
543    public String JavaDoc getTableName()
544    {
545       return tableName;
546    }
547
548    public Table.View createView(Transaction JavaDoc tx)
549    {
550       return new View(tx);
551    }
552
553    // Private
554

555    private void addReference(EntityTable table)
556    {
557       boolean wasRegistered = false;
558       if(references != null)
559       {
560          for(int i = 0; i < references.length; ++i)
561          {
562             if(references[i] == table.getTableId())
563             {
564                wasRegistered = true;
565                break;
566             }
567          }
568
569          if(!wasRegistered)
570          {
571             int[] tmp = references;
572             references = new int[references.length + 1];
573             System.arraycopy(tmp, 0, references, 0, tmp.length);
574             references[tmp.length] = table.getTableId();
575          }
576       }
577       else
578       {
579          references = new int[1];
580          references[0] = table.getTableId();
581       }
582
583       if(!wasRegistered)
584       {
585          if(log.isTraceEnabled())
586          {
587             log.trace("references " + table.getTableName());
588          }
589       }
590    }
591
592    private void addReferencedBy(EntityTable table)
593    {
594       boolean wasRegistered = false;
595       if(referencedBy != null)
596       {
597          for(int i = 0; i < referencedBy.length; ++i)
598          {
599             if(referencedBy[i] == table.getTableId())
600             {
601                wasRegistered = true;
602                break;
603             }
604          }
605
606          if(!wasRegistered)
607          {
608             int[] tmp = referencedBy;
609             referencedBy = new int[referencedBy.length + 1];
610             System.arraycopy(tmp, 0, referencedBy, 0, tmp.length);
611             referencedBy[tmp.length] = table.getTableId();
612          }
613       }
614       else
615       {
616          referencedBy = new int[1];
617          referencedBy[0] = table.getTableId();
618       }
619
620       if(!wasRegistered)
621       {
622          if(log.isTraceEnabled())
623          {
624             log.trace("referenced by " + table.getTableName());
625          }
626       }
627    }
628
629    private void delete(View view) throws SQLException JavaDoc
630    {
631       JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
632
633       Connection JavaDoc con = null;
634       PreparedStatement JavaDoc ps = null;
635       try
636       {
637          if(log.isDebugEnabled())
638          {
639             log.debug("executing : " + deleteSql);
640          }
641
642          con = dataSource.getConnection();
643          ps = con.prepareStatement(deleteSql);
644
645          int batchCount = 0;
646          while(view.deleted != null)
647          {
648             Row row = view.deleted;
649
650             int paramInd = 1;
651             for(int pkInd = 0; pkInd < pkFields.length; ++pkInd)
652             {
653                JDBCCMPFieldBridge2 pkField = pkFields[pkInd];
654                Object JavaDoc fieldValue = row.fields[pkField.getRowIndex()];
655                paramInd = pkField.setArgumentParameters(ps, paramInd, fieldValue);
656             }
657
658             deleteStrategy.executeUpdate(ps);
659
660             ++batchCount;
661             row.flushStatus();
662          }
663
664          deleteStrategy.executeBatch(ps);
665
666          if(view.deleted != null)
667          {
668             throw new IllegalStateException JavaDoc("There are still rows to delete!");
669          }
670
671          if(log.isTraceEnabled())
672          {
673             log.trace("deleted rows: " + batchCount);
674          }
675       }
676       catch(SQLException JavaDoc e)
677       {
678          log.error("Failed to delete view: " + e.getMessage(), e);
679          throw e;
680       }
681       finally
682       {
683          JDBCUtil.safeClose(ps);
684          JDBCUtil.safeClose(con);
685       }
686    }
687
688    private void update(View view) throws SQLException JavaDoc
689    {
690       JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
691       JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
692
693       Connection JavaDoc con = null;
694       PreparedStatement JavaDoc ps = null;
695       try
696       {
697          if(log.isDebugEnabled())
698          {
699             log.debug("executing : " + updateSql);
700          }
701
702          con = dataSource.getConnection();
703          ps = con.prepareStatement(updateSql);
704
705          int batchCount = 0;
706          while(view.dirty != null)
707          {
708             Row row = view.dirty;
709
710             int paramInd = 1;
711             for(int fInd = 0; fInd < tableFields.length; ++fInd)
712             {
713                JDBCCMPFieldBridge2 field = tableFields[fInd];
714                if(!field.isPrimaryKeyMember())
715                {
716                   Object JavaDoc fieldValue = row.fields[field.getRowIndex()];
717                   paramInd = field.setArgumentParameters(ps, paramInd, fieldValue);
718                }
719             }
720
721             for(int fInd = 0; fInd < pkFields.length; ++fInd)
722             {
723                JDBCCMPFieldBridge2 pkField = pkFields[fInd];
724                Object JavaDoc fieldValue = row.fields[pkField.getRowIndex()];
725                paramInd = pkField.setArgumentParameters(ps, paramInd, fieldValue);
726             }
727
728             JDBCCMPFieldBridge2 versionField = entity.getVersionField();
729             if(versionField != null)
730             {
731                int versionIndex = versionField.getVersionIndex();
732                Object JavaDoc curVersion = row.fields[versionIndex];
733                paramInd = versionField.setArgumentParameters(ps, paramInd, curVersion);
734
735                Object JavaDoc newVersion = row.fields[versionField.getRowIndex()];
736                row.fields[versionIndex] = newVersion;
737             }
738
739             updateStrategy.executeUpdate(ps);
740
741             ++batchCount;
742             row.flushStatus();
743          }
744
745          updateStrategy.executeBatch(ps);
746
747          if(log.isTraceEnabled())
748          {
749             log.trace("updated rows: " + batchCount);
750          }
751       }
752       catch(SQLException JavaDoc e)
753       {
754          log.error("Failed to update: table=" + tableName, e);
755          throw e;
756       }
757       finally
758       {
759          JDBCUtil.safeClose(ps);
760          JDBCUtil.safeClose(con);
761       }
762    }
763
764    private void insert(View view) throws SQLException JavaDoc
765    {
766       JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
767       Connection JavaDoc con = null;
768       PreparedStatement JavaDoc ps = null;
769       try
770       {
771          if(log.isDebugEnabled())
772          {
773             log.debug("executing : " + insertSql);
774          }
775
776          con = dataSource.getConnection();
777          ps = con.prepareStatement(insertSql);
778
779          int batchCount = 0;
780          while(view.created != null)
781          {
782             Row row = view.created;
783
784             int paramInd = 1;
785             for(int fInd = 0; fInd < tableFields.length; ++fInd)
786             {
787                JDBCCMPFieldBridge2 field = tableFields[fInd];
788                Object JavaDoc fieldValue = row.fields[field.getRowIndex()];
789                paramInd = field.setArgumentParameters(ps, paramInd, fieldValue);
790             }
791
792             insertStrategy.executeUpdate(ps);
793
794             ++batchCount;
795             row.flushStatus();
796          }
797
798          insertStrategy.executeBatch(ps);
799
800          if(log.isTraceEnabled())
801          {
802             log.trace("inserted rows: " + batchCount);
803          }
804       }
805       catch(SQLException JavaDoc e)
806       {
807          log.error("Failed to insert new rows: " + e.getMessage(), e);
808          throw e;
809       }
810       finally
811       {
812          JDBCUtil.safeClose(ps);
813          JDBCUtil.safeClose(con);
814       }
815    }
816
817    private EntityTable.View getView()
818    {
819       return (EntityTable.View) schema.getView(this);
820    }
821
822    public class View implements Table.View
823    {
824       private final Transaction JavaDoc tx;
825
826       private Map JavaDoc rowByPk = new HashMap JavaDoc();
827       private Row created;
828       private Row deleted;
829       private Row dirty;
830       private Row dirtyRelations;
831       private Row clean;
832
833       private Row cacheUpdates;
834
835       private List JavaDoc rowsWithNullFks;
836
837       private boolean inFlush;
838
839       public View(Transaction JavaDoc tx)
840       {
841          this.tx = tx;
842       }
843
844       public Row getRow(Object JavaDoc pk)
845       {
846          Row row;
847          if(pk == null)
848          {
849             row = new Row(this);
850          }
851          else
852          {
853             row = getRowByPk(pk, false);
854             if(row == null)
855             {
856                row = createCleanRow(pk);
857             }
858          }
859          return row;
860       }
861
862       public Row getRowByPk(Object JavaDoc pk, boolean required)
863       {
864          /*
865          Row cursor = clean;
866          while(cursor != null)
867          {
868             if(pk.equals(cursor.pk))
869             {
870                return cursor;
871             }
872             cursor = cursor.next;
873          }
874
875          cursor = dirty;
876          while(cursor != null)
877          {
878             if(pk.equals(cursor.pk))
879             {
880                return cursor;
881             }
882             cursor = cursor.next;
883          }
884
885          cursor = created;
886          while(cursor != null)
887          {
888             if(pk.equals(cursor.pk))
889             {
890                return cursor;
891             }
892             cursor = cursor.next;
893          }
894          */

895
896          Row row = (Row) rowByPk.get(pk);
897
898          if(row == null)
899          {
900             Object JavaDoc[] fields;
901             Object JavaDoc[] relations = null;
902             try
903             {
904                cache.lock(pk);
905
906                fields = cache.getFields(pk);
907                if(fields != null && relationsTotal > 0)
908                {
909                   relations = cache.getRelations(pk);
910                   if(relations == null)
911                   {
912                      relations = new Object JavaDoc[relationsTotal];
913                   }
914                }
915             }
916             finally
917             {
918                cache.unlock(pk);
919             }
920
921             if(fields != null)
922             {
923                row = createCleanRow(pk, fields, relations);
924             }
925          }
926
927          if(row == null && required)
928          {
929             throw new IllegalStateException JavaDoc("row not found: pk=" + pk);
930          }
931
932          return row;
933       }
934
935       public void addClean(Row row)
936       {
937          /*
938          if(getRowByPk(row.pk, false) != null)
939          {
940             throw new IllegalStateException("View already contains the row: key=" + row.pk);
941          }
942          */

943
944          if(clean != null)
945          {
946             row.next = clean;
947             clean.prev = row;
948          }
949
950          clean = row;
951          row.state = CLEAN;
952
953          rowByPk.put(row.pk, row);
954       }
955
956       public void addCreated(Row row) throws DuplicateKeyException JavaDoc
957       {
958          //if(getRowByPk(row.pk, false) != null)
959
//{
960
// throw new DuplicateKeyException("Table " + tableName + ", key=" + row.pk);
961
//}
962

963          if(created != null)
964          {
965             row.next = created;
966             created.prev = row;
967          }
968
969          created = row;
970          row.state = CREATED;
971
972          rowByPk.put(row.pk, row);
973
974          JDBCCMPFieldBridge2 versionField = entity.getVersionField();
975          if(versionField != null)
976          {
977             row.fields[versionField.getVersionIndex()] = row.fields[versionField.getRowIndex()];
978          }
979       }
980
981       public Row loadRow(ResultSet JavaDoc rs, Object JavaDoc pk, boolean searchableOnly)
982       {
983          Row row = getRowByPk(pk, false);
984          if(row != null)
985          {
986             if(log.isTraceEnabled())
987             {
988                log.trace("row is already loaded: pk=" + pk);
989             }
990             return row;
991          }
992          else if(log.isTraceEnabled())
993          {
994             log.trace("reading result set: pk=" + pk);
995          }
996
997          row = createCleanRow(pk);
998          JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
999          // this rsOffset is kind of a hack
1000
// but since tableIndex and rowIndex of a field are the same
1001
// this should work ok
1002
int rsOffset = 1;
1003         for(int i = 0; i < tableFields.length; ++i)
1004         {
1005            JDBCCMPFieldBridge2 field = tableFields[i];
1006            if(searchableOnly && !field.getJDBCType().isSearchable())
1007            {
1008               row.fields[field.getRowIndex()] = NOT_LOADED;
1009               --rsOffset;
1010               continue;
1011            }
1012
1013            Object JavaDoc columnValue = field.loadArgumentResults(rs, field.getRowIndex() + rsOffset);
1014            row.fields[field.getRowIndex()] = columnValue;
1015
1016            if(field.getVersionIndex() != -1)
1017            {
1018               row.fields[field.getVersionIndex()] = columnValue;
1019            }
1020         }
1021
1022         Object JavaDoc[] relations = (relationsTotal > 0 ? new Object JavaDoc[relationsTotal] : null);
1023
1024         try
1025         {
1026            cache.lock(row.pk);
1027            cache.put(tx, row.pk, row.fields, relations);
1028         }
1029         finally
1030         {
1031            cache.unlock(row.pk);
1032         }
1033
1034         return row;
1035      }
1036
1037      public Object JavaDoc loadPk(ResultSet JavaDoc rs)
1038      {
1039         Object JavaDoc pk = null;
1040         JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
1041         //int rsInd = 1;
1042
for(int i = 0; i < pkFields.length; ++i)
1043         {
1044            JDBCCMPFieldBridge2 field = pkFields[i];
1045            //Object columnValue = field.loadArgumentResults(rs, rsInd++);
1046
Object JavaDoc columnValue = field.loadArgumentResults(rs, field.getRowIndex() + 1);
1047            pk = field.setPrimaryKeyValue(pk, columnValue);
1048         }
1049         return pk;
1050      }
1051
1052      public boolean hasRow(Object JavaDoc id)
1053      {
1054         boolean has = rowByPk.containsKey(id);
1055         if(!has)
1056         {
1057            try
1058            {
1059               cache.lock(id);
1060               has = cache.contains(tx, id);
1061            }
1062            finally
1063            {
1064               cache.unlock(id);
1065            }
1066         }
1067         return has;
1068      }
1069
1070      public void addRowWithNullFk(Row row)
1071      {
1072         if(rowsWithNullFks == null)
1073         {
1074            rowsWithNullFks = new ArrayList JavaDoc();
1075         }
1076         rowsWithNullFks.add(row);
1077      }
1078
1079      private Row createCleanRow(Object JavaDoc pk)
1080      {
1081         Row row = new Row(this);
1082         row.pk = pk;
1083         addClean(row);
1084         return row;
1085      }
1086
1087      private Row createCleanRow(Object JavaDoc pk, Object JavaDoc[] fields, Object JavaDoc[] relations)
1088      {
1089         Row row = new Row(this, fields, relations);
1090         row.pk = pk;
1091         addClean(row);
1092         return row;
1093      }
1094
1095      // Table.View implementation
1096

1097      public void flushDeleted(Schema.Views views) throws SQLException JavaDoc
1098      {
1099         if(rowsWithNullFks != null)
1100         {
1101            nullifyForeignKeys();
1102            rowsWithNullFks = null;
1103         }
1104
1105         if(deleted == null)
1106         {
1107            if(log.isTraceEnabled())
1108            {
1109               log.trace("no rows to delete");
1110            }
1111            return;
1112         }
1113
1114         if(referencedBy != null)
1115         {
1116            if(inFlush)
1117            {
1118               if(log.isTraceEnabled())
1119               {
1120                  log.trace("inFlush, ignoring flushDeleted");
1121               }
1122               return;
1123            }
1124
1125            inFlush = true;
1126
1127            try
1128            {
1129               for(int i = 0; i < referencedBy.length; ++i)
1130               {
1131                  final Table.View view = views.entityViews[referencedBy[i]];
1132                  if(view != null)
1133                  {
1134                     view.flushDeleted(views);
1135                  }
1136               }
1137            }
1138            finally
1139            {
1140               inFlush = false;
1141            }
1142         }
1143
1144         delete(this);
1145      }
1146
1147      public void flushCreated(Schema.Views views) throws SQLException JavaDoc
1148      {
1149         if(created == null || dontFlushCreated)
1150         {
1151            if(log.isTraceEnabled())
1152            {
1153               log.trace("no rows to insert");
1154            }
1155            return;
1156         }
1157
1158         if(references != null)
1159         {
1160            if(inFlush)
1161            {
1162               if(log.isTraceEnabled())
1163               {
1164                  log.trace("inFlush, ignorning flushCreated");
1165               }
1166               return;
1167            }
1168            else if(log.isTraceEnabled())
1169            {
1170               log.trace("flushing created references");
1171            }
1172
1173            inFlush = true;
1174            try
1175            {
1176               for(int i = 0; i < references.length; ++i)
1177               {
1178                  final Table.View view = views.entityViews[references[i]];
1179                  if(view != null)
1180                  {
1181                     view.flushCreated(views);
1182                  }
1183               }
1184            }
1185            finally
1186            {
1187               inFlush = false;
1188            }
1189         }
1190
1191         insert(this);
1192      }
1193
1194      public void flushUpdated() throws SQLException JavaDoc
1195      {
1196         if(dirtyRelations != null)
1197         {
1198            while(dirtyRelations != null)
1199            {
1200               Row row = dirtyRelations;
1201               row.flushStatus();
1202            }
1203         }
1204
1205         if(dirty == null)
1206         {
1207            if(log.isTraceEnabled())
1208            {
1209               log.trace("no rows to update");
1210            }
1211            return;
1212         }
1213
1214         update(this);
1215      }
1216
1217      public void beforeCompletion()
1218      {
1219         /* There is no sense in the current impl of lock-for-update.
1220         if(cacheUpdates != null)
1221         {
1222            Row cursor = cacheUpdates;
1223
1224            while(cursor != null)
1225            {
1226               cache.lock(cursor.pk);
1227               try
1228               {
1229                  cache.lockForUpdate(tx, cursor.pk);
1230               }
1231               catch(Exception e)
1232               {
1233                  throw new EJBException("Table " + entity.getQualifiedTableName() + ": " + e.getMessage());
1234               }
1235               finally
1236               {
1237                  cache.unlock(cursor.pk);
1238               }
1239
1240               cursor.lockedForUpdate = true;
1241               cursor = cursor.nextCacheUpdate;
1242            }
1243         }
1244         */

1245      }
1246
1247      public void committed()
1248      {
1249         if(cacheUpdates != null)
1250         {
1251            Row cursor = cacheUpdates;
1252
1253            while(cursor != null)
1254            {
1255               //if(cursor.lockedForUpdate)
1256
//{
1257
cache.lock(cursor.pk);
1258               try
1259               {
1260                  switch(cursor.state)
1261                  {
1262                     case CLEAN:
1263                        cache.put(tx, cursor.pk, cursor.fields, cursor.relations);
1264                        break;
1265                     case DELETED:
1266                        try
1267                        {
1268                           cache.remove(tx, cursor.pk);
1269                        }
1270                        catch(Cache.RemoveException e)
1271                        {
1272                           log.warn(e.getMessage());
1273                        }
1274                        break;
1275                     default:
1276                        throw new IllegalStateException JavaDoc("Unexpected row state: table=" +
1277                           entity.getQualifiedTableName() +
1278                           ", pk=" + cursor.pk + ", state=" + cursor.state);
1279                  }
1280               }
1281               finally
1282               {
1283                  cache.unlock(cursor.pk);
1284               }
1285               //cursor.lockedForUpdate = false;
1286
//}
1287
cursor = cursor.nextCacheUpdate;
1288            }
1289         }
1290      }
1291
1292      public void rolledback()
1293      {
1294         /* There is no sense in the current impl of lock-for-update.
1295         if(cacheUpdates != null)
1296         {
1297            Row cursor = cacheUpdates;
1298
1299            while(cursor != null)
1300            {
1301               if(cursor.lockedForUpdate)
1302               {
1303                  cache.lock(cursor.pk);
1304                  try
1305                  {
1306                     cache.releaseLock(tx, cursor.pk);
1307                  }
1308                  catch(Exception e)
1309                  {
1310                     log.warn("Table " + entity.getQualifiedTableName() + ": " + e.getMessage());
1311                  }
1312                  finally
1313                  {
1314                     cache.unlock(cursor.pk);
1315                  }
1316                  cursor.lockedForUpdate = false;
1317               }
1318               cursor = cursor.nextCacheUpdate;
1319            }
1320         }
1321          */

1322      }
1323
1324      private void nullifyForeignKeys()
1325         throws SQLException JavaDoc
1326      {
1327         if(log.isTraceEnabled())
1328         {
1329            log.trace("nullifying foreign keys");
1330         }
1331
1332         Connection JavaDoc con = null;
1333         PreparedStatement JavaDoc[] ps = new PreparedStatement JavaDoc[fkConstraints.length];
1334
1335         try
1336         {
1337            final JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
1338            con = dataSource.getConnection();
1339
1340            for(int i = 0; i < rowsWithNullFks.size(); ++i)
1341            {
1342               final Row row = (Row) rowsWithNullFks.get(i);
1343               if(row.state != DELETED)
1344               {
1345                  final ForeignKeyConstraint[] cons = row.fkUpdates;
1346                  for(int c = 0; c < fkConstraints.length; ++c)
1347                  {
1348                     if(cons[c] != null)
1349                     {
1350                        PreparedStatement JavaDoc s = ps[c];
1351                        if(s == null)
1352                        {
1353                           if(log.isDebugEnabled())
1354                           {
1355                              log.debug("nullifying fk: " + cons[c].nullFkSql);
1356                           }
1357                           s = con.prepareStatement(cons[c].nullFkSql);
1358                           ps[c] = s;
1359                        }
1360
1361                        int paramInd = 1;
1362                        for(int fInd = 0; fInd < pkFields.length; ++fInd)
1363                        {
1364                           JDBCCMPFieldBridge2 pkField = pkFields[fInd];
1365                           Object JavaDoc fieldValue = row.fields[pkField.getRowIndex()];
1366                           paramInd = pkField.setArgumentParameters(s, paramInd, fieldValue);
1367                        }
1368
1369                        final int affected = s.executeUpdate();
1370                        if(affected != 1)
1371                        {
1372                           throw new EJBException JavaDoc("Affected " + affected + " rows while expected just one");
1373                        }
1374                     }
1375                  }
1376               }
1377            }
1378         }
1379         finally
1380         {
1381            for(int i = 0; i < ps.length; ++i)
1382            {
1383               JDBCUtil.safeClose(ps[i]);
1384            }
1385            JDBCUtil.safeClose(con);
1386         }
1387      }
1388   }
1389
1390   public class Row
1391   {
1392      private EntityTable.View view;
1393      private Object JavaDoc pk;
1394      private final Object JavaDoc[] fields;
1395      private final Object JavaDoc[] relations;
1396
1397      private byte state;
1398
1399      private Row prev;
1400      private Row next;
1401
1402      private boolean cacheUpdateScheduled;
1403      private Row nextCacheUpdate;
1404      //private boolean lockedForUpdate;
1405

1406      private ForeignKeyConstraint[] fkUpdates;
1407
1408      public Row(EntityTable.View view)
1409      {
1410         this.view = view;
1411         fields = new Object JavaDoc[fieldsTotal];
1412         relations = (relationsTotal == 0 ? null : new Object JavaDoc[relationsTotal]);
1413         state = UNREFERENCED;
1414      }
1415
1416      public Row(EntityTable.View view, Object JavaDoc[] fields, Object JavaDoc[] relations)
1417      {
1418         this.view = view;
1419         this.fields = fields;
1420         this.relations = relations;
1421         state = UNREFERENCED;
1422      }
1423
1424      public Object JavaDoc getPk()
1425      {
1426         return pk;
1427      }
1428
1429      public void loadCachedRelations(int index, Cache.CacheLoader loader)
1430      {
1431         if(relations != null)
1432         {
1433            final Object JavaDoc cached = relations[index];
1434            relations[index] = loader.loadFromCache(cached);
1435         }
1436      }
1437
1438      public void cacheRelations(int index, Cache.CacheLoader loader)
1439      {
1440         relations[index] = loader.getCachedValue();
1441         scheduleCacheUpdate();
1442      }
1443
1444      public void insert(Object JavaDoc pk) throws DuplicateKeyException JavaDoc
1445      {
1446         this.pk = pk;
1447         view.addCreated(this);
1448      }
1449
1450      public Object JavaDoc getFieldValue(int i)
1451      {
1452         if(state == DELETED)
1453         {
1454            throw new NoSuchObjectLocalException JavaDoc("The instance was removed: " + pk);
1455         }
1456
1457         Object JavaDoc value = fields[i];
1458         if(value == NOT_LOADED)
1459         {
1460            value = loadField(i);
1461         }
1462
1463         return value;
1464      }
1465
1466      public void setFieldValue(int i, Object JavaDoc value)
1467      {
1468         fields[i] = value;
1469      }
1470
1471      public boolean isDirty()
1472      {
1473         return state != CLEAN && state != DIRTY_RELATIONS;
1474      }
1475
1476      public void setDirty()
1477      {
1478         if(state == CLEAN || state == DIRTY_RELATIONS)
1479         {
1480            updateState(DIRTY);
1481         }
1482      }
1483
1484      public void setDirtyRelations()
1485      {
1486         if(state == CLEAN)
1487         {
1488            updateState(DIRTY_RELATIONS);
1489         }
1490      }
1491
1492      public void delete()
1493      {
1494         if(state == CLEAN || state == DIRTY || state == DIRTY_RELATIONS)
1495         {
1496            updateState(DELETED);
1497         }
1498         else if(state == CREATED)
1499         {
1500            dereference();
1501            state = DELETED;
1502            view.rowByPk.remove(pk);
1503         }
1504         else if(state == DELETED)
1505         {
1506            throw new IllegalStateException JavaDoc("The row is already deleted: pk=" + pk);
1507         }
1508      }
1509
1510      public void nullForeignKey(ForeignKeyConstraint constraint)
1511      {
1512         if(fkUpdates == null)
1513         {
1514            fkUpdates = new ForeignKeyConstraint[fkConstraints.length];
1515            view.addRowWithNullFk(this);
1516         }
1517
1518         fkUpdates[constraint.index] = constraint;
1519      }
1520
1521      public void nonNullForeignKey(ForeignKeyConstraint constraint)
1522      {
1523         if(fkUpdates != null)
1524         {
1525            fkUpdates[constraint.index] = null;
1526         }
1527      }
1528
1529      private void flushStatus()
1530      {
1531         if(state == CREATED || state == DIRTY)
1532         {
1533            updateState(CLEAN);
1534         }
1535         else if(state == DELETED)
1536         {
1537            dereference();
1538         }
1539         else if(state == DIRTY_RELATIONS)
1540         {
1541            updateState(CLEAN);
1542         }
1543
1544         scheduleCacheUpdate();
1545      }
1546
1547      private void scheduleCacheUpdate()
1548      {
1549         if(!cacheUpdateScheduled)
1550         {
1551            if(view.cacheUpdates == null)
1552            {
1553               view.cacheUpdates = this;
1554            }
1555            else
1556            {
1557               nextCacheUpdate = view.cacheUpdates;
1558               view.cacheUpdates = this;
1559            }
1560            cacheUpdateScheduled = true;
1561         }
1562      }
1563
1564      private void updateState(byte state)
1565      {
1566         dereference();
1567
1568         if(state == CLEAN)
1569         {
1570            if(view.clean != null)
1571            {
1572               next = view.clean;
1573               view.clean.prev = this;
1574            }
1575            view.clean = this;
1576         }
1577         else if(state == DIRTY)
1578         {
1579            if(view.dirty != null)
1580            {
1581               next = view.dirty;
1582               view.dirty.prev = this;
1583            }
1584            view.dirty = this;
1585         }
1586         else if(state == CREATED)
1587         {
1588            if(view.created != null)
1589            {
1590               next = view.created;
1591               view.created.prev = this;
1592            }
1593            view.created = this;
1594         }
1595         else if(state == DELETED)
1596         {
1597            if(view.deleted != null)
1598            {
1599               next = view.deleted;
1600               view.deleted.prev = this;
1601            }
1602            view.deleted = this;
1603         }
1604         else if(state == DIRTY_RELATIONS)
1605         {
1606            if(view.dirtyRelations != null)
1607            {
1608               next = view.dirtyRelations;
1609               view.dirtyRelations.prev = this;
1610            }
1611            view.dirtyRelations = this;
1612         }
1613         else
1614         {
1615            throw new IllegalStateException JavaDoc("Can't update to state: " + state);
1616         }
1617
1618         this.state = state;
1619      }
1620
1621      private void dereference()
1622      {
1623         if(state == CLEAN && view.clean == this)
1624         {
1625            view.clean = next;
1626         }
1627         else if(state == DIRTY && view.dirty == this)
1628         {
1629            view.dirty = next;
1630         }
1631         else if(state == CREATED && view.created == this)
1632         {
1633            view.created = next;
1634         }
1635         else if(state == DELETED && view.deleted == this)
1636         {
1637            view.deleted = next;
1638         }
1639         else if(state == DIRTY_RELATIONS && view.dirtyRelations == this)
1640         {
1641            view.dirtyRelations = next;
1642         }
1643
1644         if(next != null)
1645         {
1646            next.prev = prev;
1647         }
1648
1649         if(prev != null)
1650         {
1651            prev.next = next;
1652         }
1653
1654         prev = null;
1655         next = null;
1656      }
1657
1658      public void flush() throws SQLException JavaDoc, DuplicateKeyException JavaDoc
1659      {
1660         // todo needs refactoring
1661

1662         if(state != CREATED)
1663         {
1664            if(log.isTraceEnabled())
1665            {
1666               log.trace("The row is already inserted: pk=" + pk);
1667            }
1668            return;
1669         }
1670
1671         Connection JavaDoc con = null;
1672         PreparedStatement JavaDoc duplicatePkPs = null;
1673         PreparedStatement JavaDoc insertPs = null;
1674         ResultSet JavaDoc rs = null;
1675         try
1676         {
1677            int paramInd;
1678            con = dataSource.getConnection();
1679
1680            // check for duplicate key
1681
/*
1682            if(log.isDebugEnabled())
1683            {
1684               log.debug("executing : " + duplicatePkSql);
1685            }
1686
1687            duplicatePkPs = con.prepareStatement(duplicatePkSql);
1688
1689            paramInd = 1;
1690            JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
1691            for(int i = 0; i < pkFields.length; ++i)
1692            {
1693               JDBCCMPFieldBridge2 pkField = pkFields[i];
1694               Object fieldValue = fields[pkField.getRowIndex()];
1695               paramInd = pkField.setArgumentParameters(duplicatePkPs, paramInd, fieldValue);
1696            }
1697
1698            rs = duplicatePkPs.executeQuery();
1699            if(rs.next())
1700            {
1701               throw new DuplicateKeyException("Table " + tableName + ", pk=" + pk);
1702            }
1703            */

1704
1705            // insert
1706
if(log.isDebugEnabled())
1707            {
1708               log.debug("executing : " + insertSql);
1709            }
1710
1711            insertPs = con.prepareStatement(insertSql);
1712
1713            paramInd = 1;
1714            JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
1715            for(int fInd = 0; fInd < tableFields.length; ++fInd)
1716            {
1717               JDBCCMPFieldBridge2 field = tableFields[fInd];
1718               Object JavaDoc fieldValue = fields[field.getRowIndex()];
1719               paramInd = field.setArgumentParameters(insertPs, paramInd, fieldValue);
1720            }
1721
1722            insertPs.executeUpdate();
1723
1724            flushStatus();
1725         }
1726         catch(SQLException JavaDoc e)
1727         {
1728            log.error("Failed to insert new rows: " + e.getMessage(), e);
1729            throw e;
1730         }
1731         finally
1732         {
1733            JDBCUtil.safeClose(rs);
1734            JDBCUtil.safeClose(duplicatePkPs);
1735            JDBCUtil.safeClose(insertPs);
1736            JDBCUtil.safeClose(con);
1737         }
1738      }
1739
1740      private Object JavaDoc loadField(int i)
1741      {
1742         JDBCCMPFieldBridge2 field = (JDBCCMPFieldBridge2)entity.getFields().get(i);
1743
1744         StringBuffer JavaDoc query = new StringBuffer JavaDoc();
1745         query.append("select ")
1746            .append(field.getColumnName())
1747            .append(" from ")
1748            .append(tableName)
1749            .append(" where ");
1750
1751         JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[])entity.getPrimaryKeyFields();
1752         for(int pkI = 0; pkI < pkFields.length; ++pkI)
1753         {
1754            if(pkI > 0)
1755            {
1756               query.append(" and ");
1757            }
1758            query.append(pkFields[pkI].getColumnName()).append("=?");
1759         }
1760
1761         if(log.isDebugEnabled())
1762         {
1763            log.debug("executing: " + query.toString());
1764         }
1765
1766         Object JavaDoc value = null;
1767         Connection JavaDoc con = null;
1768         PreparedStatement JavaDoc ps = null;
1769         ResultSet JavaDoc rs = null;
1770
1771         try
1772         {
1773            con = dataSource.getConnection();
1774            ps = con.prepareStatement(query.toString());
1775
1776            for(int pkI = 0; pkI < pkFields.length; ++pkI)
1777            {
1778               JDBCCMPFieldBridge2 pkField = pkFields[pkI];
1779               Object JavaDoc fieldValue = fields[pkField.getRowIndex()];
1780               pkField.setArgumentParameters(ps, pkI + 1, fieldValue);
1781            }
1782
1783            rs = ps.executeQuery();
1784
1785            if(!rs.next())
1786            {
1787               throw new NoSuchEntityException JavaDoc("Row not found: " + pk);
1788            }
1789
1790            value = field.loadArgumentResults(rs, 1);
1791         }
1792         catch(SQLException JavaDoc e)
1793         {
1794            throw new EJBException JavaDoc("Failed to load field " +
1795               entity.getEntityName() + "." + field.getFieldName() +
1796               ": " + e.getMessage(), e);
1797         }
1798         finally
1799         {
1800            JDBCUtil.safeClose(rs);
1801            JDBCUtil.safeClose(ps);
1802            JDBCUtil.safeClose(con);
1803         }
1804
1805         fields[field.getRowIndex()] = value;
1806         return value;
1807      }
1808   }
1809
1810   public static interface CommitStrategy
1811   {
1812      void executeUpdate(PreparedStatement JavaDoc ps) throws SQLException JavaDoc;
1813
1814      void executeBatch(PreparedStatement JavaDoc ps) throws SQLException JavaDoc;
1815   }
1816
1817   private static final CommitStrategy BATCH_UPDATE = new CommitStrategy()
1818   {
1819      public void executeUpdate(PreparedStatement JavaDoc ps) throws SQLException JavaDoc
1820      {
1821         ps.addBatch();
1822      }
1823
1824      public void executeBatch(PreparedStatement JavaDoc ps) throws SQLException JavaDoc
1825      {
1826         int[] updates = ps.executeBatch();
1827         for(int i = 0; i < updates.length; ++i)
1828         {
1829            int status = updates[i];
1830            if(status != 1 && status != -2 /* java.sql.Statement.SUCCESS_NO_INFO since jdk1.4*/)
1831            {
1832               String JavaDoc msg = (status == -3 /* java.sql.Statement.EXECUTE_FAILED since jdk1.4 */ ?
1833                  "One of the commands in the batch failed to execute" :
1834                  "Each command in the batch should update exactly 1 row but " +
1835                  "one of the commands updated " + updates[i] + " rows.");
1836               throw new EJBException JavaDoc(msg);
1837            }
1838         }
1839      }
1840   };
1841
1842   private static final CommitStrategy NON_BATCH_UPDATE = new CommitStrategy()
1843   {
1844      public void executeUpdate(PreparedStatement JavaDoc ps) throws SQLException JavaDoc
1845      {
1846         int rows = ps.executeUpdate();
1847         if(rows != 1)
1848         {
1849            throw new EJBException JavaDoc("Expected one updated row but got: " + rows);
1850         }
1851      }
1852
1853      public void executeBatch(PreparedStatement JavaDoc ps)
1854      {
1855      }
1856   };
1857
1858   public class ForeignKeyConstraint
1859   {
1860      public final int index;
1861      private final String JavaDoc nullFkSql;
1862
1863      public ForeignKeyConstraint(int index, JDBCCMPFieldBridge2[] fkFields)
1864      {
1865         this.index = index;
1866
1867         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1868         buf.append("update ").append(tableName).append(" set ")
1869            .append(fkFields[0].getColumnName()).append("=null");
1870         for(int i = 1; i < fkFields.length; ++i)
1871         {
1872            buf.append(", ").append(fkFields[i].getColumnName()).append("=null");
1873         }
1874
1875         buf.append(" where ");
1876         JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
1877         buf.append(pkFields[0].getColumnName()).append("=?");
1878         for(int i = 1; i < pkFields.length; ++i)
1879         {
1880            buf.append(" and ").append(pkFields[i].getColumnName()).append("=?");
1881         }
1882
1883         nullFkSql = buf.toString();
1884         if(log.isDebugEnabled())
1885         {
1886            log.debug("update foreign key sql: " + nullFkSql);
1887         }
1888      }
1889   }
1890}
1891
Popular Tags