Source code: com/clra/rowing/remote/RowingSessionBean.java
1 /*
2 * Copyright (c) Carnegie Lake Rowing Association 2002. All rights reserved.
3 * Distributed under the GPL license. See doc/COPYING.
4 * $RCSfile: RowingSessionBean.java,v $
5 * $Date: 2003/02/26 03:38:45 $
6 * $Revision: 1.8 $
7 */
8
9 package com.clra.rowing.remote;
10
11 import com.clra.rowing.Configuration;
12 import com.clra.rowing.DefaultRowingSessionComparator;
13 import com.clra.rowing.RowingDBRead;
14 import com.clra.rowing.RowingException;
15 import com.clra.rowing.RowingSessionStateException;
16 import com.clra.rowing.RowingSessionLevel;
17 import com.clra.rowing.RowingSessionState;
18 import com.clra.rowing.RowingSessionType;
19 import com.clra.rowing.RowingSessionSnapshot;
20 import com.clra.util.DBConfiguration;
21 import com.clra.util.ISerializableComparator;
22 import java.rmi.RemoteException;
23 import java.sql.Connection;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Date;
30 import javax.ejb.CreateException;
31 import javax.ejb.EJBException;
32 import javax.ejb.EntityBean;
33 import javax.ejb.EntityContext;
34 import javax.ejb.FinderException;
35 import javax.ejb.NoSuchEntityException;
36 import javax.ejb.ObjectNotFoundException;
37 import javax.ejb.RemoveException;
38 import org.apache.log4j.Category;
39
40 /**
41 * @version $Revision: 1.8 $ $Date: 2003/02/26 03:38:45 $
42 * @author <a href="mailto:rphall@pluto.njcc.com">Rick Hall</a>
43 */
44 public class RowingSessionBean implements EntityBean {
45
46 private final static String base = RowingSessionBean.class.getName();
47 private final static Category theLog = Category.getInstance( base );
48
49 private transient boolean isDirty = true;
50 private Integer id = null;
51 private RowingSessionState state = null;
52 private Date date = null;
53 private RowingSessionLevel level = null;
54 private RowingSessionType type = null;
55
56 private EntityContext context;
57
58 /**
59 * Returns the natural Comparator for rowing sessions, in which rowing
60 * sessions are compared by date, state, type, level, and id, with that
61 * respective weighting.
62 */
63 public ISerializableComparator getNaturalComparator() {
64 return new DefaultRowingSessionComparator();
65 }
66
67 /** Returns a snapshot of a rowing session */
68 public RowingSessionSnapshot getData() throws RemoteException {
69 RowingSessionSnapshot retVal = new RowingSessionSnapshot( this.id,
70 this.state, this.date, this.level, this.type );
71 return retVal;
72 }
73
74 /**
75 * Sets the date, level and type of a rowing session if the rowing
76 * session is TENATIVE. The id and state properties are not set.
77 */
78 public void setData(RowingSessionSnapshot data)
79 throws RowingSessionStateException {
80 if ( this.state != RowingSessionState.TENATIVE ) {
81 String msg = "Can not edit data when state == '"
82 + this.state.getName() + "'";
83 throw new RowingSessionStateException( msg );
84 }
85 if ( !this.date.equals( data.getDate() ) ) {
86 this.isDirty = true;
87 this.date = data.getDate();
88 }
89 if ( !this.level.equals( data.getLevel() ) ) {
90 this.isDirty = true;
91 this.level = data.getLevel();
92 }
93 if ( !this.type.equals( data.getType() ) ) {
94 this.isDirty = true;
95 this.type = data.getType();
96 }
97 }
98
99 /**
100 * Returns the primary key of a rowing session. The id is immutable
101 * after a rowing session is created.
102 */
103 public Integer getId() {
104 return this.id;
105 }
106
107 /**
108 * Returns the state of a rowing session. The state of a rowing
109 * session can not be set directly. It is changed as a side-effect
110 * of other operations on a rowing session.
111 */
112 public RowingSessionState getState() {
113 return this.state;
114 }
115
116 /**
117 * Publishes a rowing session. Only TENATIVE sessions may be published.
118 * The state of a published rowing session becomes OPEN.
119 * @exception RowingSessionStateException if a non-tenative rowing session is
120 * published.
121 */
122 public void publish() throws RowingSessionStateException {
123 if ( getState() != RowingSessionState.TENATIVE ) {
124 throw new RowingSessionStateException( "state is " + getState() );
125 }
126 if ( !this.state.equals( RowingSessionState.OPEN ) ) {
127 // This block is here in case preconditions change
128 this.isDirty = true;
129 this.state = RowingSessionState.OPEN;
130 }
131 } // publish()
132
133 /**
134 * Locks a rowing session. Only OPEN sessions may be locked.
135 * The state of a locked rowing session becomes LOCKED.
136 * @exception RowingSessionStateException if a non-open rowing session is
137 * locked.
138 */
139 public void lock() throws RowingSessionStateException {
140 if ( getState() != RowingSessionState.OPEN ) {
141 throw new RowingSessionStateException( "state is " + getState() );
142 }
143 if ( !this.state.equals( RowingSessionState.LOCKED ) ) {
144 // This block is here in case preconditions change
145 this.isDirty = true;
146 this.state = RowingSessionState.LOCKED;
147 }
148 } // lock()
149
150 /**
151 * Cancels a rowing session. The state of a cancelled state becomes
152 * CANCELLED.<p>
153 *
154 * A TENTATIVE session may not be cancelled (but it may be deleted).
155 * A COMPLETE, INVOICING or CLOSED session may not be cancelled.
156 *
157 * @exception RowingSessionStateException if a tenative, complete, invoicing
158 * or closed session is cancelled.
159 */
160 public void cancel() throws RowingSessionStateException {
161 if ( getState() == RowingSessionState.TENATIVE
162 || getState() == RowingSessionState.COMPLETE
163 || getState() == RowingSessionState.INVOICING
164 || getState() == RowingSessionState.CLOSED ) {
165 throw new RowingSessionStateException( "state is " + getState() );
166 }
167 if ( !this.state.equals( RowingSessionState.CANCELLED ) ) {
168 this.isDirty = true;
169 this.state = RowingSessionState.CANCELLED;
170 }
171 } // cancel()
172
173 /**
174 * Deletes a rowing session. Only a TENATIVE session may be deleted.
175 * A deleted session is removed from the database.<p>
176 *
177 * This is a safe version of the standard EJBObject.remove()
178 * operation. It checks that the session is tenative before removing it.
179 * <strong>Application code should always delete, rather than remove, rowing
180 * sessions.</strong> The remove operation, however, is required for testing
181 * purposes.<p>
182 *
183 * @exception RowingSessionStateException if a non-tenative rowing session is
184 * deleted.
185 * @see com.clra.rowing.IRowingSession.remove()
186 */
187 public void delete()
188 throws RemoteException, RemoveException, RowingSessionStateException {
189 if ( getState() != RowingSessionState.TENATIVE ) {
190 throw new RowingSessionStateException( "state is " + getState() );
191 }
192 context.getEJBObject().remove();
193 } // delete();
194
195 /** Returns the date (and time) of a rowing session */
196 public Date getDate() {
197 return this.date;
198 }
199
200 /**
201 * Edits the date (and time) of a rowing session. Editing is allowed only
202 * for TENATIVE sessions.
203 * @exception RowingSessionStateException if the edited session is not
204 * in the TENATIVE state.
205 * @see RowingSessionState
206 */
207 public void setDate( Date date ) throws RowingSessionStateException {
208 // Precondition
209 if ( date == null ) {
210 throw new IllegalArgumentException( "null date" );
211 }
212 if ( this.state != RowingSessionState.TENATIVE ) {
213 String msg = "Can not edit date when state == '"
214 + this.state.getName() + "'";
215 throw new RowingSessionStateException( msg );
216 }
217 if ( !this.date.equals( date ) ) {
218 this.isDirty = true;
219 this.date = date;
220 }
221 }
222
223 /** Returns the level of a rowing session */
224 public RowingSessionLevel getLevel() throws RemoteException {
225 return this.level;
226 }
227
228 /**
229 * Edits the level of a rowing session. Editing is allowed only for
230 * TENATIVE sessions.
231 */
232 public void setLevel( RowingSessionLevel level )
233 throws RowingSessionStateException {
234 // Precondition
235 if ( level == null ) {
236 throw new IllegalArgumentException( "null level" );
237 }
238 if ( this.state != RowingSessionState.TENATIVE ) {
239 String msg = "Can not edit level when state == '"
240 + this.state.getName() + "'";
241 throw new RowingSessionStateException( msg );
242 }
243 if ( !this.level.equals( level ) ) {
244 this.isDirty = true;
245 this.level = level;
246 }
247 }
248
249 /** Returns the type of a rowing session */
250 public RowingSessionType getType() throws RemoteException {
251 return this.type;
252 }
253
254 /**
255 * Edits the type of a rowing session. Editing is allowed only for
256 * TENATIVE sessions.
257 */
258 public void setType( RowingSessionType type )
259 throws RowingSessionStateException {
260 // Precondition
261 if ( type == null ) {
262 throw new IllegalArgumentException( "null type" );
263 }
264 if ( this.state != RowingSessionState.TENATIVE ) {
265 String msg = "Can not edit type when state == '"
266 + this.state.getName() + "'";
267 throw new RowingSessionStateException( msg );
268 }
269 if ( !this.type.equals( type ) ) {
270 this.isDirty = true;
271 this.type = type;
272 }
273 }
274
275 public Integer ejbCreate( Date date, RowingSessionLevel level,
276 RowingSessionType type ) throws CreateException {
277
278 // Preconditions
279 if ( date == null ) {
280 throw new CreateException( "null date" );
281 }
282 if ( level == null ) {
283 throw new CreateException( "null level" );
284 }
285 if ( type == null ) {
286 throw new CreateException( "null type" );
287 }
288
289 try {
290
291 this.id = nextId();
292 this.state = RowingSessionState.TENATIVE;
293 this.date = date;
294 this.level = level;
295 this.type = type;
296
297 insertRow( this.id, this.state, this.date, this.level, this.type );
298
299 // FIXME shouldn't isDirty be set false here?
300 //this.isDirty = false;
301 // ENDFIXME
302
303 if ( theLog.isDebugEnabled() ) {
304 String msg = "ejbCreate: " + id + ", '"
305 + RowingDBRead.dateFormat.format( date ) + "', " + level.getName()
306 + ", " + type.getName() + ", " + state.getName();
307 theLog.debug( msg );
308 }
309
310 }
311 catch (Exception ex) {
312 throw new EJBException( "ejbCreate: " + ex.getMessage() );
313 }
314
315 return this.id;
316 } // ejbCreate(Date,RowingSessionLevel,RowingSessionType)
317
318 public Integer ejbFindByPrimaryKey(Integer primaryKey)
319 throws FinderException {
320
321 // Precondition
322 if ( primaryKey == null ) {
323 throw new FinderException( "null primaryKey" );
324 }
325
326 boolean hasRow = false;
327 try {
328 hasRow = selectByPrimaryKey(primaryKey);
329 if ( theLog.isDebugEnabled() ) {
330 String msg = "ejbFindByPrimaryKey: " + id + " " + hasRow;
331 theLog.debug( msg );
332 }
333
334 }
335 catch (Exception ex) {
336 throw new EJBException("ejbFindByPrimaryKey: " + ex.getMessage());
337 }
338
339 if (!hasRow) {
340 String msg = "Row for id " + primaryKey + " not found.";
341 throw new ObjectNotFoundException( msg );
342 }
343
344 return primaryKey;
345 } // ejbFindByPrimaryKey(Integer)
346
347 /**
348 * Returns a collection of rowing sessions that fall within the
349 * inclusive date range.
350 */
351 public Collection ejbFindInDateRange( Date start, Date finish )
352 throws FinderException {
353
354 // Preconditions
355 if ( start == null || finish == null ) {
356 throw new FinderException( "null date" );
357 }
358 if ( start.compareTo(finish) > 0 ) {
359 throw new FinderException( "start > finish" );
360 }
361
362 Collection result = null;
363 try {
364 result = selectInDateRange( start, finish );
365 if ( theLog.isDebugEnabled() ) {
366 String msg = "ejbFindInDateRange: size == " + result.size();
367 theLog.debug( msg );
368 }
369 }
370 catch (Exception ex) {
371 throw new EJBException("ejbFindInDateRange: " + ex.getMessage());
372 }
373
374 return result;
375 } // ejbFindInDateRange(Date,Date)
376
377 /**
378 * Returns a collection of rowing sessions that fall within the
379 * inclusive date range.
380 */
381 public Collection ejbFindAll()
382 throws FinderException {
383
384 Collection result = null;
385 try {
386 result = selectAll();
387 if ( theLog.isDebugEnabled() ) {
388 String msg = "ejbFindAll: size == " + result.size();
389 theLog.debug( msg );
390 }
391 }
392 catch (Exception ex) {
393 throw new EJBException("ejbFindAll: " + ex.getMessage());
394 }
395
396 return result;
397 } // ejbFindAll()
398
399 /** @see delete() */
400 public void ejbRemove() {
401
402 try {
403 Integer tmpId = this.getId();
404 deleteRow(id);
405 if ( !RowingSessionState.TENATIVE.equals(this.getState()) ) {
406 theLog.warn( "ejbRemove: row deleted, id == " + tmpId );
407 }
408 else if ( theLog.isDebugEnabled() ) {
409 theLog.debug( "ejbRemove: row deleted, id == " + tmpId );
410 }
411 }
412 catch (Exception ex) {
413 throw new EJBException("ejbRemove: " + ex.getMessage());
414 }
415
416 } // ejbRemove()
417
418 public void setEntityContext(EntityContext context) {
419
420 this.context = context;
421 if ( theLog.isDebugEnabled() ) {
422 theLog.debug( "setEntityContext: context set" );
423 theLog.debug( "setEntityContext: id == " + this.id );
424 }
425
426 return;
427 } // setEntityContext(EntityContext)
428
429 public void unsetEntityContext() {
430
431 this.context = null;
432 if ( theLog.isDebugEnabled() ) {
433 theLog.debug( "unsetEntityContext: id nulled" );
434 theLog.debug( "unsetEntityContext: context nulled" );
435 }
436
437 return;
438 } // unsetEntityContext()
439
440 public void ejbActivate() {
441 this.id = (Integer)context.getPrimaryKey();
442 if ( theLog.isDebugEnabled() ) {
443 theLog.debug( "ejbActivate: id == " + this.id );
444 }
445 }
446
447 public void ejbPassivate() {
448 this.id = null;
449 if ( theLog.isDebugEnabled() ) {
450 theLog.debug( "ejbPassivate: id == " + this.id );
451 }
452 }
453
454 public void ejbLoad() {
455
456 try {
457 loadRow();
458 if ( theLog.isDebugEnabled() ) {
459 theLog.debug( "ejbLoad: row loaded" );
460 }
461 isDirty = false;
462 }
463 catch (Exception ex) {
464 throw new EJBException("ejbLoad: " + ex.getMessage());
465 }
466
467 } // ejbLoad()
468
469 public void ejbStore() {
470
471 try {
472 if ( isDirty ) {
473 storeRow();
474 if ( theLog.isDebugEnabled() ) {
475 theLog.debug( "ejbStore: row stored" );
476 }
477 isDirty = false;
478 }
479 else {
480 if ( theLog.isDebugEnabled() ) {
481 theLog.debug( "ejbStore: row skipped" );
482 }
483 }
484 }
485 catch (Exception ex) {
486 throw new EJBException("ejbStore: " + ex.getMessage());
487 }
488
489 } // ejbStore()
490
491 public void ejbPostCreate( Date date, RowingSessionLevel level,
492 RowingSessionType type ) {}
493
494 private boolean selectByPrimaryKey(Integer primaryKey)
495 throws SQLException {
496
497 Connection conn = null;
498 PreparedStatement stmt = null;
499 ResultSet rs = null;
500 boolean retVal = false;
501 try {
502 conn = DBConfiguration.getConnection();
503 stmt = conn.prepareStatement(
504 Configuration.SQL_SESSION_02,
505 ResultSet.TYPE_FORWARD_ONLY,
506 ResultSet.CONCUR_READ_ONLY);
507
508 stmt.setInt( 1, primaryKey.intValue() );
509
510 rs = stmt.executeQuery();
511 retVal = rs.next();
512 if ( theLog.isDebugEnabled() ) {
513 theLog.debug( "selectByPrimaryKey: retVal == " + retVal );
514 }
515
516 }
517 finally {
518 DBConfiguration.closeSQLResultSet( rs );
519 DBConfiguration.closeSQLStatement( stmt );
520 DBConfiguration.closeSQLConnection( conn );
521 rs = null;
522 stmt = null;
523 conn = null;
524 }
525
526 return retVal;
527 } // selectByPrimaryKey(String)
528
529 private Collection selectAll()
530 throws SQLException {
531
532 Connection conn = null;
533 PreparedStatement stmt = null;
534 ResultSet rs = null;
535 Collection retVal = new ArrayList();
536 try {
537 conn = DBConfiguration.getConnection();
538 stmt = conn.prepareStatement(
539 Configuration.SQL_SESSION_01,
540 ResultSet.TYPE_FORWARD_ONLY,
541 ResultSet.CONCUR_READ_ONLY);
542
543 rs = stmt.executeQuery();
544
545 int rowCount = 0;
546 while ( rs.next() ) {
547 Integer id = new Integer( rs.getInt(1) );
548 retVal.add(id);
549 ++rowCount;
550 }
551
552 if ( theLog.isDebugEnabled() ) {
553 String msg = "selectAll: rowCount == " + rowCount;
554 theLog.debug( msg );
555 }
556
557 }
558 finally {
559 DBConfiguration.closeSQLResultSet( rs );
560 DBConfiguration.closeSQLStatement( stmt );
561 DBConfiguration.closeSQLConnection( conn );
562 rs = null;
563 stmt = null;
564 conn = null;
565 }
566
567 return retVal;
568 } // selectAll()
569
570 private Collection selectInDateRange(Date start, Date finish)
571 throws SQLException {
572
573 Connection conn = null;
574 PreparedStatement stmt = null;
575 ResultSet rs = null;
576 Collection retVal = new ArrayList();
577 try {
578 conn = DBConfiguration.getConnection();
579 stmt = conn.prepareStatement(
580 Configuration.SQL_SESSION_03,
581 ResultSet.TYPE_FORWARD_ONLY,
582 ResultSet.CONCUR_READ_ONLY);
583
584 String strStart = RowingDBRead.dateFormat.format( start );
585 String strFinish = RowingDBRead.dateFormat.format( finish );
586
587 stmt.setString( 1, strStart );
588 stmt.setString( 2, strFinish );
589
590 rs = stmt.executeQuery();
591
592 int rowCount = 0;
593 while ( rs.next() ) {
594 Integer id = new Integer( rs.getInt(1) );
595 retVal.add(id);
596 ++rowCount;
597 }
598
599 if ( theLog.isDebugEnabled() ) {
600 String msg = "selectInDateRange: rowCount == " + rowCount;
601 theLog.debug( msg );
602 }
603
604 }
605 finally {
606 DBConfiguration.closeSQLResultSet( rs );
607 DBConfiguration.closeSQLStatement( stmt );
608 DBConfiguration.closeSQLConnection( conn );
609 rs = null;
610 stmt = null;
611 conn = null;
612 }
613
614 return retVal;
615 } // selectInDateRange(Date,Date)
616
617 private void insertRow( Integer id, RowingSessionState state, Date date,
618 RowingSessionLevel level, RowingSessionType type ) throws SQLException {
619
620 Connection conn = null;
621 PreparedStatement stmt = null;
622 ResultSet rs = null;
623 try {
624 conn = DBConfiguration.getConnection();
625 stmt = conn.prepareStatement(
626 Configuration.SQL_SESSION_04,
627 ResultSet.TYPE_FORWARD_ONLY,
628 ResultSet.CONCUR_READ_ONLY);
629
630 String strDate = RowingDBRead.dateFormat.format( date );
631
632 stmt.setInt( 1, id.intValue() );
633 stmt.setString( 2, strDate );
634 stmt.setString( 3, level.getName() );
635 stmt.setString( 4, type.getName() );
636 stmt.setString( 5, state.getName() );
637
638 rs = stmt.executeQuery();
639
640 if ( theLog.isDebugEnabled() ) {
641 theLog.debug( "insertRow: row " + id );
642 }
643
644 }
645 finally {
646 DBConfiguration.closeSQLResultSet( rs );
647 DBConfiguration.closeSQLStatement( stmt );
648 DBConfiguration.closeSQLConnection( conn );
649 rs = null;
650 stmt = null;
651 conn = null;
652 }
653
654 return;
655 } // insertRow(..)
656
657 private void deleteRow(Integer id) throws SQLException {
658
659 Connection conn = null;
660 PreparedStatement stmt = null;
661 ResultSet rs = null;
662 try {
663 conn = DBConfiguration.getConnection();
664 stmt = conn.prepareStatement(
665 Configuration.SQL_SESSION_05,
666 ResultSet.TYPE_FORWARD_ONLY,
667 ResultSet.CONCUR_READ_ONLY);
668
669 stmt.setInt( 1, id.intValue() );
670
671 rs = stmt.executeQuery();
672
673 if ( theLog.isDebugEnabled() ) {
674 theLog.debug( "deleteRow: row " + id );
675 }
676
677 }
678 finally {
679 DBConfiguration.closeSQLResultSet( rs );
680 DBConfiguration.closeSQLStatement( stmt );
681 DBConfiguration.closeSQLConnection( conn );
682 rs = null;
683 stmt = null;
684 conn = null;
685 }
686
687 return;
688 } // deleteRow(Integer)
689
690 private void loadRow()
691 throws SQLException, RowingException, NoSuchEntityException {
692
693 /*
694 * Implementation note: the translation from DB row to snapshot
695 * to bean looks inefficient, but it is not the bottleneck in
696 * loading bean. The bottleneck is the EJB algorithm for finding
697 * an id, then loading a row, then storing the result back to
698 * the DB. That means at least two DB calls (assuming the storage
699 * step is skipped by checking an isDirty flag). The DB calls
700 * completely dominate performance.
701 *
702 * On the other hand, returning a row as a snapshot allows the
703 * RowingDBRead code to be reused directly elsewhere. Where
704 * speed is critical, and where it is unlikely that a RowingSessionBean
705 * is already in memory, it is best to use the RowingDBRead code to
706 * directly load a (read-only) snapshot from the database.
707 */
708 RowingSessionSnapshot snapshot = RowingDBRead.loadRowingSession( this.id );
709 this.id = snapshot.getId();
710 this.state = snapshot.getState();
711 this.date = snapshot.getDate();
712 this.level = snapshot.getLevel();
713 this.type = snapshot.getType();
714
715 } // loadRow()
716
717 private void storeRow() throws SQLException {
718
719 Connection conn = null;
720 PreparedStatement stmt = null;
721 int rowCount = 0;
722 try {
723 conn = DBConfiguration.getConnection();
724 stmt = conn.prepareStatement(
725 Configuration.SQL_SESSION_07,
726 ResultSet.TYPE_FORWARD_ONLY,
727 ResultSet.CONCUR_READ_ONLY);
728
729 String strDate = RowingDBRead.dateFormat.format( this.date );
730
731 stmt.setString( 1, strDate );
732 stmt.setString( 2, this.level.getName() );
733 stmt.setString( 3, this.type.getName() );
734 stmt.setString( 4, this.state.getName() );
735 stmt.setInt( 5, this.id.intValue() );
736
737 rowCount = stmt.executeUpdate();
738
739 if ( theLog.isDebugEnabled() ) {
740 theLog.debug( "storeRow: rowCount == " + rowCount );
741 String msg = "storeRow: " + id.intValue() + ", '" + strDate + "', "
742 + level.getName() + ", " + type.getName() + ", " + state.getName();
743 theLog.debug( msg );
744 }
745
746 }
747 finally {
748 DBConfiguration.closeSQLStatement( stmt );
749 DBConfiguration.closeSQLConnection( conn );
750 stmt = null;
751 conn = null;
752 }
753
754 if ( rowCount != 1 ) {
755 throw new EJBException( "failed to store row == " + this.id );
756 }
757
758 return;
759 } // storeRow()
760
761 /** A utility that gets (and reserves) the next id for a rowing session */
762 public static Integer nextId() throws SQLException, CreateException {
763
764 Connection conn = null;
765 PreparedStatement stmt = null;
766 ResultSet rs = null;
767 Integer retVal = null;
768 try {
769 conn = DBConfiguration.getConnection();
770 stmt = conn.prepareStatement(
771 Configuration.SQL_SESSION_08,
772 ResultSet.TYPE_FORWARD_ONLY,
773 ResultSet.CONCUR_READ_ONLY);
774
775 if ( theLog.isDebugEnabled() ) {
776 String msg =
777 "nextId update: SQL == " + Configuration.SQL_SESSION_08;
778 theLog.debug( msg );
779 }
780
781 if ( !DBConfiguration.isOracle() ) {
782
783 /*
784 * There is a problem in this block of code because it is not
785 * locked in a transaction. If two different threads execute
786 * this block simultaneously, the MySQL sequence value will
787 * have been incremented twice, and both threads will use the
788 * same value in the code section that follows this block.
789 *
790 * As a practical matter, during normal operation, rowing
791 * sessions are created infrequently, and this block of code
792 * is executed by one thread at a time. This code works because
793 * this is a low-load application.
794 */
795
796 boolean moreResults = stmt.execute();
797
798 stmt = conn.prepareStatement(
799 Configuration.SQL_SESSION_08A,
800 ResultSet.TYPE_FORWARD_ONLY,
801 ResultSet.CONCUR_READ_ONLY);
802
803 if ( theLog.isDebugEnabled() ) {
804 String msg =
805 "nextId select: SQL == " + Configuration.SQL_SESSION_08A;
806 theLog.debug( msg );
807 }
808
809 } // if !isOracle
810
811 rs = stmt.executeQuery();
812
813 int rowCount = 0;
814 while ( rs.next() ) {
815 retVal = new Integer( rs.getInt(1) );
816 ++rowCount;
817 }
818 if ( rowCount != 1 ) {
819 String msg = "unable to get next id: rowCount == " + rowCount;
820 throw new CreateException( msg );
821 }
822
823 if ( theLog.isDebugEnabled() ) {
824 String msg = "nextId: retVal == " + retVal.intValue();
825 theLog.debug( msg );
826 }
827
828 }
829 finally {
830 DBConfiguration.closeSQLResultSet( rs );
831 DBConfiguration.closeSQLStatement( stmt );
832 DBConfiguration.closeSQLConnection( conn );
833 rs = null;
834 stmt = null;
835 conn = null;
836 }
837
838 return retVal;
839 } // nextId()
840
841 } // RowingSessionBean
842
843 /*
844 * $Log: RowingSessionBean.java,v $
845 * Revision 1.8 2003/02/26 03:38:45 rphall
846 * Added copyright and GPL license
847 *
848 * Revision 1.7 2003/02/19 22:09:14 rphall
849 * Removed gratuitous use of CLRA acronym
850 *
851 * Revision 1.6 2003/02/16 00:48:53 rphall
852 * Rolled back 'fix' for where primary key is set
853 *
854 * Revision 1.5 2003/02/15 04:31:42 rphall
855 * Changes connected to major revision of MemberBean
856 *
857 * Revision 1.4 2003/02/11 01:33:03 rphall
858 * Added comment about code block that is not thread safe
859 *
860 */
861