persistence@glassfish.java.net

Code changes relevant to Issue 283 - PostgreSQL database and SERIAL field

From: Pramod Gopinath <Pramod.Gopinath_at_Sun.COM>
Date: Sat, 26 Aug 2006 12:57:36 -0700

Hi Andrei
   The changes that are attached to this email are related to Issue 283.
https://glassfish.dev.java.net/issues/show_bug.cgi?id=283

 We had discussed this back in March timeframe. Since the issue was
marked as an Enhancement, I had stopped working on this issue. But
lately there was an issue 805 filed that was related to the same area.
After I had sent the code changes to Tom and updated the Issue 805 in
glassfish, thought that it would be great to finally solve this issue
once and for all.
Hence using the code changes that we had worked on, I have finally got
this issue resolved.

All the changes are in the entity-persistence module. The files that
were changed are:

src/java/oracle/toplink/essentials/internal/databaseaccess/DatasourcePlatform.java
src/java/oracle/toplink/essentials/internal/databaseaccess/Platform.java
src/java/oracle/toplink/essentials/internal/sequencing/SequencingManager.java
src/java/oracle/toplink/essentials/platform/database/PostgreSQLPlatform
 
With these changes a user wanting to use the SERIAL field of PostgreSQL
database would just have to define the following in their entity class
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)


The field that would be created would be of type "SERIAL" and the
implicit native sequence associated with this field would be of the form
fullyQualifiedName_IdFieldColumeName_SEQ. This would ensure that if the
user has defined the table in a different schema we would use the
"schemaName".Table_name correctly. Thus we would also solve scenarios
like those defined in issue 805.

Thanks
Pramod


Index: src/java/oracle/toplink/essentials/internal/databaseaccess/Platform.java
===================================================================
RCS file: /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/databaseaccess/Platform.java,v
retrieving revision 1.6
diff -r1.6 Platform.java
30a31
> import oracle.toplink.essentials.sessions.DatabaseSession;
223a225,231
>
> /**
> * INTERNAL:
> * Platform specific sequencing initialization.
> * This internal method should only be called by SequencingManager.
> */
> public void platformSpecificSequencingInitialization(DatabaseSession session);
Index: src/java/oracle/toplink/essentials/internal/databaseaccess/DatasourcePlatform.java
===================================================================
RCS file: /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/databaseaccess/DatasourcePlatform.java,v
retrieving revision 1.9
diff -r1.9 DatasourcePlatform.java
31a32
> import oracle.toplink.essentials.sessions.DatabaseSession;
595a597,605
>
> /**
> * INTERNAL:
> * Platform specific sequencing initialization.
> * This internal method should only be called by SequencingManager.
> * By default does nothing.
> */
> public void platformSpecificSequencingInitialization(DatabaseSession session) {
> }
Index: src/java/oracle/toplink/essentials/internal/sequencing/SequencingManager.java
===================================================================
RCS file: /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/sequencing/SequencingManager.java,v
retrieving revision 1.5
diff -r1.5 SequencingManager.java
567a568,569
> getOwnerSession().getDatasourcePlatform().platformSpecificSequencingInitialization(getOwnerSession());
>
Index: src/java/oracle/toplink/essentials/platform/database/PostgreSQLPlatform.java
===================================================================
RCS file: /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/platform/database/PostgreSQLPlatform.java,v
retrieving revision 1.7
diff -r1.7 PostgreSQLPlatform.java
39a40,43
> import oracle.toplink.essentials.sequencing.Sequence;
> import oracle.toplink.essentials.sequencing.DefaultSequence;
> import oracle.toplink.essentials.sequencing.NativeSequence;
> import oracle.toplink.essentials.sessions.DatabaseSession;
161c165
< selectQuery.setSQLString("select currval(current_schema()" + "|| \'." + seqName + "\')");
---
>         selectQuery.setSQLString("select currval(\'"  + seqName + "\')"); 
375c379,445
<     }   
---
>     }     
>       
>     protected String getDBSequenceName(String tableName, String pkFieldName) {
>         StringBuffer seqName = new StringBuffer(tableName);
>         seqName.append("_").append(pkFieldName).append("_seq");
>         return seqName.toString();
>     }  
>     
>      /**
>      * INTERNAL:
>      * Platform specific sequencing initialization.
>      * This internal method should only be called by SequencingManager.
>      * 
>      * For PostgreSQL database for id fields that are defined as "SERIAL" the database creates
>      * an implicit sequence. The name of the sequence provided by the database is of the form
>      * <table_name>_<id_field_name>_seq.
>      * As part of the insert statement we need to get the correct sequence to be able to get
>      * the next value. This code ensures that we have our internal structures pointing to the
>      * correct sequence name for the runtime code to work correctly. 
>      * The current version of postgresql allows only 1 SERIAL field to be defined per table.
>      */
>     public void platformSpecificSequencingInitialization(DatabaseSession session) {
>         Boolean isDefaultSequenceNative = null;
>         
>         // We need to iterate over all the entities  
>         Iterator iterator = session.getProject().getDescriptors().values().iterator();
>         Sequence sequence;
>         
>         while (iterator.hasNext()) {
>             ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
> 
>             // Ensure that the entity uses sequencing
>             if (descriptor.usesSequenceNumbers()) {
>                 String currSequenceName = descriptor.getSequenceNumberName();
>                 sequence = (Sequence)getSequences().get(currSequenceName);
> 
>                 boolean shouldVerifySequenceName;
> 
>                 boolean usesDefaultSequence = sequence == null || sequence instanceof DefaultSequence;
>                 if(usesDefaultSequence) {
>                     // is defaultSequence is a NativeSequence?
>                     if(isDefaultSequenceNative == null) {
>                         isDefaultSequenceNative = new Boolean(getDefaultSequence() instanceof NativeSequence);
>                     }
>                     shouldVerifySequenceName = isDefaultSequenceNative.booleanValue();
>                 } else {
>                     shouldVerifySequenceName = sequence instanceof NativeSequence;
>                 }
> 
>                 if(shouldVerifySequenceName) {                                        
>                     DatabaseTable tbl = (DatabaseTable)descriptor.getTables().firstElement();
>                     String tableName = tbl.getQualifiedName();                                       
>                     String pkFieldName = descriptor.getSequenceNumberField().getName();
>                     String seqName = getDBSequenceName(tableName, pkFieldName);
>                     if(!currSequenceName.equals(seqName)) {
>                         descriptor.setSequenceNumberName(seqName);
>                         if(sequence != null) {
>                             removeSequence(currSequenceName);
>                             sequence.setName(seqName);
>                             addSequence(sequence);
>                         }
>                     }
>                 }
>             }
>         }
>     }
>