This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git


The following commit(s) were added to refs/heads/master by this push:
     new 6f4cf5b11 OPENJPA-2930 Implements UUID and GenerationType.UUID 
strategy (#124)
6f4cf5b11 is described below

commit 6f4cf5b11b5bc5ffa0da1925260cdb7fbf7b8fe5
Author: Paulo Cristovão de Araújo Silva Filho <pcris...@gmail.com>
AuthorDate: Wed Feb 19 11:12:45 2025 -0300

    OPENJPA-2930 Implements UUID and GenerationType.UUID strategy (#124)
    
    * [OPENJPA-2930] Implemented JPA 3.1 UUID support
    
    * added basic support for UUID as string or uuid field, per db support
    * added GenerationStrategy.UUID support using UUID-4 random
    
    * [OPENJPA-2930] Implements UUID support
    
    * Allows usage of UUID as a basic type
    * Allows usage of UUID as identity type
    * Allows GenerationType.AUTO and GenerationType.UUID usage on String and 
UUID fields
    * Generates type 4 UUID for @GeneratedValue String and UUID fields
    * Storage UUID fields as binary data on supporting pair db/jdbc driver
    * Storage UUID fields as canonical string representation when database or 
jdbc driver does not support UUID binary usage.
    * Updates manual
    
    * [OPENJPA-2930] Fixing issues found on review
---
 .../openjpa/jdbc/kernel/JDBCStoreManager.java      |   1 +
 .../openjpa/jdbc/meta/MappingRepository.java       |   1 +
 .../jdbc/meta/strats/ImmutableValueHandler.java    |  14 +
 .../org/apache/openjpa/jdbc/sql/DBDictionary.java  |  19 ++
 .../org/apache/openjpa/jdbc/sql/H2Dictionary.java  |   2 +
 .../apache/openjpa/jdbc/sql/HSQLDictionary.java    |   3 +
 .../openjpa/jdbc/sql/PostgresDictionary.java       |   4 +-
 .../org/apache/openjpa/enhance/PCEnhancer.java     |   1 +
 .../apache/openjpa/kernel/StateManagerImpl.java    |   8 +
 .../org/apache/openjpa/meta/ClassMetaData.java     |   4 +
 .../java/org/apache/openjpa/meta/JavaTypes.java    |   9 +
 .../org/apache/openjpa/meta/ValueStrategies.java   |   9 +
 .../org/apache/openjpa/util/ApplicationIds.java    |   7 +
 .../java/org/apache/openjpa/util/ImplHelper.java   |   5 +
 .../main/java/org/apache/openjpa/util/UuidId.java  |  77 +++++
 .../generationtype/TestUuidGeneratedEntity.java    | 322 +++++++++++++++++++++
 .../generationtype/UuidGeneratedEntity.java        |  96 ++++++
 .../AnnotationPersistenceMetaDataParser.java       |  17 +-
 .../src/doc/manual/jpa_overview_meta.xml           |  13 +-
 openjpa-project/src/doc/manual/jpa_overview_pc.xml |  15 +-
 .../src/doc/manual/supported_databases.xml         |  22 +-
 21 files changed, 641 insertions(+), 8 deletions(-)

diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
index 801bd528e..af0a689f9 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
@@ -82,6 +82,7 @@ import org.apache.openjpa.util.InvalidStateException;
 import org.apache.openjpa.util.OpenJPAId;
 import org.apache.openjpa.util.StoreException;
 import org.apache.openjpa.util.UserException;
+import org.apache.openjpa.util.UuidId;
 
 /**
  * StoreManager plugin that uses JDBC to store persistent data in a
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
index c8db77abe..a007759de 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
@@ -1360,6 +1360,7 @@ public class MappingRepository extends MetaDataRepository 
{
             case JavaTypes.LOCAL_DATETIME:
             case JavaTypes.OFFSET_TIME:
             case JavaTypes.OFFSET_DATETIME:
+            case JavaTypes.UUID_OBJ:
                 return ImmutableValueHandler.getInstance();
             case JavaTypes.STRING:
                 if (isClob(val, true))
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
index 362d18de6..f28f703af 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
@@ -65,6 +65,8 @@ public class ImmutableValueHandler extends 
AbstractValueHandler {
         col.setIdentifier(name);
         if (vm.getTypeCode() == JavaTypes.DATE)
             col.setJavaType(JavaSQLTypes.getDateTypeCode(vm.getType()));
+        else if (vm.getTypeCode() == JavaTypes.UUID_OBJ) 
+            updateUUIDColumn(vm, col);
         else
             col.setJavaType(vm.getTypeCode());
         return new Column[]{ col };
@@ -94,6 +96,7 @@ public class ImmutableValueHandler extends 
AbstractValueHandler {
             case JavaTypes.OFFSET_DATETIME:
             case JavaTypes.BIGINTEGER:
             case JavaTypes.LOCALE:
+            case JavaTypes.UUID_OBJ:
                 return true;
             default:
                 return false;
@@ -117,4 +120,15 @@ public class ImmutableValueHandler extends 
AbstractValueHandler {
         // honor the user's null-value=default
         return JavaSQLTypes.getEmptyValue(vm.getTypeCode());
     }
+
+    private void updateUUIDColumn(ValueMapping vm, Column col) {
+        DBDictionary dict = vm.getMappingRepository().getDBDictionary();
+        if (dict.supportsUuidType) {
+            col.setJavaType(vm.getTypeCode());
+            col.setSize(-1);
+        } else {
+            col.setJavaType(JavaTypes.STRING);
+            col.setSize(36);
+        }
+    }
 }
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
index 537c6a286..fdfd15afa 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
@@ -67,6 +67,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.UUID;
 
 import javax.sql.DataSource;
 
@@ -397,6 +398,7 @@ public class DBDictionary
     public String varcharTypeName = "VARCHAR";
     public String xmlTypeName = "XML";
     public String xmlTypeEncoding = "UTF-8";
+    public String uuidTypeName = "UUID";
     public String getStringVal = "";
 
     // schema metadata
@@ -493,6 +495,8 @@ public class DBDictionary
     protected String defaultSchemaName = null;
     private String conversionKey = null;
 
+    public boolean supportsUuidType = false;
+
     // Naming utility and naming rules
     private DBIdentifierUtil namingUtil = null;
     private Map<String, IdentifierRule> namingRules = new HashMap<>();
@@ -1628,6 +1632,9 @@ public class DBDictionary
                 } else
                     setTimestamp(stmnt, idx, (Timestamp) val, null, col);
                 break;
+            case JavaTypes.UUID_OBJ:
+                setObject(stmnt, idx, val, Types.OTHER, col);
+                break;
             default:
                 if (col != null && (col.getType() == Types.BLOB
                     || col.getType() == Types.VARBINARY))
@@ -1733,6 +1740,10 @@ public class DBDictionary
         else if (val instanceof Reader)
             setCharacterStream(stmnt, idx, (Reader) val,
                 (sized == null) ? 0 : sized.size, col);
+        else if (val instanceof UUID && supportsUuidType)
+            setObject(stmnt, idx, (UUID) val, Types.OTHER, col);
+        else if (val instanceof UUID && !supportsUuidType) 
+            setString(stmnt, idx, ((UUID) val).toString(), col);
         else
             throw new UserException(_loc.get("bad-param", val.getClass()));
     }
@@ -1956,6 +1967,14 @@ public class DBDictionary
         if (col.isAutoAssigned() && autoAssignTypeName != null)
             return appendSize(col, autoAssignTypeName);
 
+        if (col.getJavaType() == JavaTypes.UUID_OBJ) {
+            if (supportsUuidType) 
+                return appendSize(col, uuidTypeName);
+            else {
+                return appendSize(col, getTypeName(Types.VARCHAR));
+            }
+        }
+
         return appendSize(col, getTypeName(col.getType()));
     }
 
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java
index 8e10535c0..a541b12b6 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java
@@ -144,6 +144,7 @@ public class H2Dictionary extends DBDictionary {
             "UNKNOWN",
             "USER",
             "USING",
+            "UUID",
             "VALUE",
             "VALUES",
             "WHEN",
@@ -238,6 +239,7 @@ public class H2Dictionary extends DBDictionary {
             "IS", "JOIN", "LIKE", "LIMIT", "MINUS", "NATURAL", "NOT", "NULL", 
"OFFSET", "ON", "ORDER", "PRIMARY", "ROWNUM",
             "SELECT", "SYSDATE", "TRUE", "UNION", "UNIQUE", "WHERE", "WITH",
         }));
+        supportsUuidType = true;
     }
 
     @Override
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java
index 34bbb7418..766a8ac77 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java
@@ -145,6 +145,9 @@ public class HSQLDictionary extends DBDictionary {
             packageName = "org.hsqldb.Trace";
             fieldName = "VIOLATION_OF_UNIQUE_INDEX";
         }
+        if (dbMajorVersion > 2 || (dbMajorVersion == 2 && dbMinorVersion >= 
4)) {
+            supportsUuidType = true;
+        }
         try {
             Class<?> cls = Class.forName(packageName);
             Field fld = cls.getField(fieldName);
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
index 9fbdbad3f..f03036523 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
@@ -216,6 +216,8 @@ public class PostgresDictionary extends DBDictionary {
 
         // PostgreSQL requires to escape search strings
         requiresSearchStringEscapeForLike = true;
+        supportsUuidType = true;
+        uuidTypeName = "UUID";
     }
 
 
@@ -305,7 +307,7 @@ public class PostgresDictionary extends DBDictionary {
     public void setNull(PreparedStatement stmnt, int idx, int colType,
         Column col)
         throws SQLException {
-        if (col != null && col.isXML()) {
+        if (col != null && (col.isXML() || col.getJavaType() == 
JavaTypes.UUID_OBJ)) {
             stmnt.setNull(idx, Types.OTHER);
             return;
         }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
index 6fd062dfb..f413b63b2 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
@@ -4927,6 +4927,7 @@ public class PCEnhancer {
                 case JavaTypes.MAP:
                 case JavaTypes.OBJECT:
                 case JavaTypes.CALENDAR:
+                case JavaTypes.UUID_OBJ:
                     // if (sm != null)
                     //   sm.proxyDetachedDeserialized (<index>);
                     instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // 
this
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
index a9e619b68..e83b8d259 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
@@ -36,6 +36,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.TimeZone;
+import java.util.UUID;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
@@ -2905,6 +2906,13 @@ public class StateManagerImpl implements 
OpenJPAStateManager, Serializable {
             case JavaTypes.STRING:
                 fm.storeStringField(field, (String) val);
                 break;
+            case JavaTypes.UUID_OBJ:
+                if (val instanceof String) {
+                    fm.storeObjectField(field, (UUID) UUID.fromString((String) 
val));
+                } else if (val instanceof UUID) {
+                    fm.storeObjectField(field, (UUID) val);
+                }
+                break;
             default:
                 fm.storeObjectField(field, val);
         }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java
index 655a3cf39..52e598bc0 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java
@@ -71,6 +71,7 @@ import org.apache.openjpa.util.OpenJPAId;
 import org.apache.openjpa.util.ShortId;
 import org.apache.openjpa.util.StringId;
 import org.apache.openjpa.util.UnsupportedException;
+import org.apache.openjpa.util.UuidId;
 
 
 /**
@@ -559,6 +560,9 @@ public class ClassMetaData
             case JavaTypes.BOOLEAN_OBJ:
                 _objectId = BooleanId.class;
                 break;
+            case JavaTypes.UUID_OBJ:
+                _objectId = UuidId.class;
+                break;
         }
         return _objectId;
     }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
index c0b9b8a3f..4c2907267 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
@@ -40,6 +40,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
+import java.util.UUID;
 
 import org.apache.openjpa.enhance.PersistenceCapable;
 import org.apache.openjpa.lib.meta.CFMetaDataParser;
@@ -96,6 +97,7 @@ public class JavaTypes {
     public static final int OFFSET_TIME = 36;
     public static final int OFFSET_DATETIME = 37;
 
+    public static final int UUID_OBJ = 38;
 
 
     private static final Localizer _loc = 
Localizer.forPackage(JavaTypes.class);
@@ -131,6 +133,8 @@ public class JavaTypes {
         _typeCodes.put(LocalDateTime.class, LOCAL_DATETIME);
         _typeCodes.put(OffsetTime.class, OFFSET_TIME);
         _typeCodes.put(OffsetDateTime.class, OFFSET_DATETIME);
+
+        _typeCodes.put(UUID.class, UUID_OBJ);
     }
 
     /**
@@ -417,6 +421,11 @@ public class JavaTypes {
                 if (val instanceof String)
                     return Short.valueOf(val.toString());
                 return val;
+            case UUID_OBJ:
+                if (val instanceof String) {
+                    return UUID.fromString((String) val);
+                }
+                return (UUID) val;
             case STRING:
                 return val.toString();
             default:
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueStrategies.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueStrategies.java
index 3aad2e89f..6f818c5b5 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueStrategies.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueStrategies.java
@@ -77,6 +77,13 @@ public class ValueStrategies {
      */
     public static final int UUID_TYPE4_HEX = 8;
 
+    /**
+     * JPA 3.1 native UUID strategy
+     */
+    public static final int UUID_JPA = 9;
+
+    public static final int UUID_TYPE4_CANON = 10;
+
     private static final Localizer _loc = Localizer.forPackage
         (ValueStrategies.class);
 
@@ -93,6 +100,8 @@ public class ValueStrategies {
         _map.put("uuid-hex", UUID_HEX);
         _map.put("uuid-type4-string", UUID_TYPE4_STRING);
         _map.put("uuid-type4-hex", UUID_TYPE4_HEX);
+        _map.put("uuid-jpa", UUID_JPA);
+        _map.put("uuid-type4-canon", UUID_TYPE4_CANON);
     }
 
     /**
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
index 7346d080f..78820d0e4 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
@@ -25,6 +25,7 @@ import java.math.BigInteger;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.util.Date;
+import java.util.UUID;
 
 import org.apache.openjpa.enhance.FieldManager;
 import org.apache.openjpa.enhance.PCRegistry;
@@ -208,6 +209,12 @@ public class ApplicationIds {
                         throw new ClassCastException("!(x instanceof 
Boolean)");
                     return new BooleanId(meta.getDescribedType(),
                         val == null ? false : (Boolean)val);
+                case JavaTypes.UUID_OBJ:
+                    if (convert && (val instanceof String))
+                        return new UuidId(meta.getDescribedType(), 
UUID.fromString((String) val));
+                    else if (val instanceof UUID)
+                        return new UuidId(meta.getDescribedType(), (UUID) val);
+                    throw new ClassCastException(String.format("Could not 
convert [%s] to UUID", val.getClass().getCanonicalName()));
                 default:
                     throw new InternalException();
             }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java
index 9d30db4ff..1c106ca4c 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java
@@ -23,6 +23,7 @@ import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
+import java.util.UUID;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.enhance.ManagedInstanceProvider;
@@ -169,6 +170,10 @@ public class ImplHelper {
                 return UUIDGenerator.nextString(UUIDGenerator.TYPE4);
             case ValueStrategies.UUID_TYPE4_HEX:
                 return UUIDGenerator.nextHex(UUIDGenerator.TYPE4);
+            case ValueStrategies.UUID_TYPE4_CANON:
+                return UUID.randomUUID().toString();
+            case ValueStrategies.UUID_JPA:
+                return UUID.randomUUID();
             default:
                 return null;
         }
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/UuidId.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/UuidId.java
new file mode 100644
index 000000000..fca7af49e
--- /dev/null
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/UuidId.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.util;
+
+import java.util.UUID;
+
+/**
+ * Identity type appropriate for UUID primary key fields and shared
+ * id classes.
+ *
+ * @author Abe White
+ * @author Paulo Cristovão Filho
+ * @author Max Solodovnik
+ */
+public final class UuidId
+    extends OpenJPAId {
+
+    
+    private static final long serialVersionUID = 1L;
+    private UUID _key;
+
+    public UuidId(Class<?> cls, UUID key) {
+        super(cls);
+        _key = key;
+    }
+
+    public UuidId(Class<?> cls, UUID key, boolean subs) {
+        super(cls, subs);
+        _key = key;
+    }
+
+    public UUID getId() {
+        return _key;
+    }
+
+    /**
+     * Allow utilities in this package to mutate id.
+     */
+    void setId(UUID id) {
+        _key = id;
+    }
+
+    @Override
+    public Object getIdObject() {
+        return _key;
+    }
+
+    @Override
+    protected int idHash() {
+        return (_key == null) ? 0 : _key.hashCode();
+    }
+
+    @Override
+    protected boolean idEquals(OpenJPAId o) {
+        if (!(o instanceof UuidId)) {
+            return false;
+        }
+        Object key = ((UuidId) o)._key;
+        return (_key == null) ? key == null : _key.equals(key);
+    }
+}
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestUuidGeneratedEntity.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestUuidGeneratedEntity.java
new file mode 100644
index 000000000..9796bea0d
--- /dev/null
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestUuidGeneratedEntity.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.generationtype;
+
+import jakarta.persistence.EntityManager;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+public class TestUuidGeneratedEntity extends SingleEMFTestCase {
+    
+    DBDictionary _dict;
+
+    @Override
+    public void setUp() {
+        setUp(UuidGeneratedEntity.class, CLEAR_TABLES);
+        _dict = 
((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance();
+    }
+
+    public void testMapping() {
+        ClassMapping cm = getMapping(UuidGeneratedEntity.class);
+        Column[] cols = cm.getPrimaryKeyColumns();
+        assertEquals(1, cols.length);
+
+        Column col = cols[0];
+        assertEquals(_dict.supportsUuidType ? JavaTypes.UUID_OBJ : 
JavaTypes.STRING, col.getJavaType());
+    }
+
+    public void testDefaultValues() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv1 = new UuidGeneratedEntity();
+        UuidGeneratedEntity gv2 = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv1);
+        em.persist(gv2);
+        em.getTransaction().commit();
+
+        em.refresh(gv1);
+        em.refresh(gv2);
+
+        assertNotNull(gv1.getId());
+        assertNotNull(gv2.getId());
+        assertFalse(gv1.getId().compareTo(gv2.getId()) == 0);
+        assertNotNull(gv1.getNativeUuid());
+        assertNotNull(gv2.getNativeUuid());
+        assertFalse(gv1.getNativeUuid().compareTo(gv2.getNativeUuid()) == 0);
+        assertTrue(isCanonicalHexUUID(gv1.getStringUUID(), 4));
+        assertTrue(isCanonicalHexUUID(gv2.getStringUUID(), 4));
+        closeEM(em);
+    }
+
+    public void testFindByUUIDProperty() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv);        
+        em.getTransaction().commit();
+
+        UUID nid = gv.getNativeUuid();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE 
u.nativeUuid = :nid";
+
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("nid", nid)
+            .getResultList();
+        
+        assertEquals(1, list.size());
+        assertEquals(nid, list.get(0).getNativeUuid());
+
+        closeEM(em);
+    }
+
+    public void testUpdateUUIDProperty() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv);        
+        em.getTransaction().commit();
+
+        UUID nid = gv.getNativeUuid();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE 
u.nativeUuid = :nid";
+
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("nid", nid)
+            .getResultList();
+        assertEquals(1, list.size());
+        UUID changed = UUID.randomUUID();
+
+        em.getTransaction().begin();
+        list.get(0).setNativeUuid(changed);
+        em.merge(list.get(0));
+        em.getTransaction().commit();
+
+        list = em.createQuery(query, UuidGeneratedEntity.class)
+                .setParameter("nid", nid)
+                .getResultList();
+        assertEquals(0, list.size());
+        list = em.createQuery(query, UuidGeneratedEntity.class)
+                .setParameter("nid", changed)
+                .getResultList();
+        assertEquals(1, list.size());
+
+        closeEM(em);
+
+    }
+
+    public void testFindByStringUUIDProperty() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv);        
+        em.getTransaction().commit();
+
+        String sid = gv.getStringUUID();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE 
u.stringUUID = :sid";
+
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("sid", sid)
+            .getResultList();
+        
+        assertEquals(1, list.size());
+        assertEquals(sid, list.get(0).getStringUUID());
+
+        closeEM(em);
+
+    }
+
+    public void testFindByUUID() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv);        
+        em.getTransaction().commit();
+
+        UUID id = gv.getId();
+
+        UuidGeneratedEntity fv = em.find(UuidGeneratedEntity.class, id);
+        
+        assertNotNull(fv);
+        assertEquals(gv.getId(), fv.getId());
+        assertEquals(gv.getStringUUID(), fv.getStringUUID());
+        assertEquals(gv.getNativeUuid(), fv.getNativeUuid());
+
+        closeEM(em);
+    }
+
+    public void testRemoveById() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity gv = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(gv);        
+        em.getTransaction().commit();
+
+        UUID id = gv.getId();
+
+        UuidGeneratedEntity fv = em.find(UuidGeneratedEntity.class, id);
+        
+        em.getTransaction().begin();
+        em.remove(fv);
+        em.getTransaction().commit();
+
+        fv = em.find(UuidGeneratedEntity.class, id);
+        assertNull(fv);
+
+        closeEM(em);
+    }
+
+    public void testParentRelationshipById() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity parent = new UuidGeneratedEntity();
+        UuidGeneratedEntity child = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(parent);
+        child.setParent(parent);
+        em.persist(child);        
+        em.getTransaction().commit();
+
+        assertEquals(parent, child.getParent());
+        assertEquals(parent.getId(), child.getParent().getId());
+
+        UUID parentId = parent.getId();
+        UUID childId = child.getId();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE 
u.parent.id = :pid";
+        
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("pid", parentId)
+            .getResultList();
+        assertEquals(1, list.size());
+        assertEquals(childId, list.get(0).getId());
+
+        closeEM(em);
+    }
+
+    public void testParentRelationshipByEntity() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity parent = new UuidGeneratedEntity();
+        UuidGeneratedEntity child = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(parent);
+        child.setParent(parent);
+        em.persist(child);        
+        em.getTransaction().commit();
+
+        UUID childId = child.getId();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE u.parent 
= :parent";
+        
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("parent", parent)
+            .getResultList();
+        assertEquals(1, list.size());
+        assertEquals(childId, list.get(0).getId());
+
+        closeEM(em);
+    }
+
+    public void testSetPreviouslyNullProperty() {
+        EntityManager em = emf.createEntityManager();
+
+        UuidGeneratedEntity parent = new UuidGeneratedEntity();
+        UuidGeneratedEntity child = new UuidGeneratedEntity();
+
+        em.getTransaction().begin();
+        em.persist(parent);
+        child.setParent(parent);
+        em.persist(child);        
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        child.setBasicUuid(new UUID(0, 0));
+        child = em.merge(child);
+        em.getTransaction().commit();
+
+        String query = "SELECT u FROM UuidGeneratedEntity AS u WHERE u.parent 
= :parent";
+        
+        List<UuidGeneratedEntity> list = em
+            .createQuery(query, UuidGeneratedEntity.class)
+            .setParameter("parent", parent)
+            .getResultList();
+        assertNotNull(child.getBasicUuid());
+
+
+        closeEM(em);
+    }
+
+    /*
+     * Verify a uuid hex string value is 32 characters long, consists entirely
+     * of hex digits and is the correct version.
+     */
+    private boolean isCanonicalHexUUID(String value, int type) {
+        if (value.length() != 36)
+            return false;
+        char[] chArr = value.toCharArray();
+        for (int i = 0; i < 36; i++)
+        {
+            char ch = chArr[i];
+            if ((i == 8 || i == 13 || i == 18|| i == 23) && ch == '-')
+                continue;
+            if (!(Character.isDigit(ch) ||
+                (ch >= 'a' && ch <= 'f') ||
+                (ch >= 'A' && ch <= 'F')))
+                return false;
+            if (i == 14) {
+                if (type == 1 && ch != '1')
+                    return false;
+                if (type == 4 && ch != '4')
+                    return false;
+                if (type == 7 && ch != '7')
+                    return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/UuidGeneratedEntity.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/UuidGeneratedEntity.java
new file mode 100644
index 000000000..6c1909932
--- /dev/null
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/UuidGeneratedEntity.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.generationtype;
+
+import static jakarta.persistence.GenerationType.UUID;
+
+import java.util.UUID;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.ManyToOne;
+
+@Entity
+public class UuidGeneratedEntity {
+    
+    @Id
+    @GeneratedValue
+    @Column(name = "id_")
+    private UUID id;
+
+    @GeneratedValue(strategy = UUID)
+    @Column(name = "nativeuuid_")
+    private UUID nativeUuid;
+
+    @GeneratedValue(strategy = UUID)
+    @Column(name = "stringuuid_")
+    private String stringUUID;
+
+    private UUID basicUuid;
+
+    @ManyToOne
+    private UuidGeneratedEntity parent;
+     
+    public UuidGeneratedEntity() {
+        super();
+    }
+
+    public UUID getId() {
+        return id;
+    }
+
+    public void setId(UUID id) {
+        this.id = id;
+    }
+
+    public UUID getNativeUuid() {
+        return nativeUuid;
+    }
+
+    public void setNativeUuid(UUID nativeUuid) {
+        this.nativeUuid = nativeUuid;
+    }
+
+    public String getStringUUID() {
+        return stringUUID;
+    }
+
+    public void setStringUUID(String stringUUID) {
+        this.stringUUID = stringUUID;
+    }
+
+    public UUID getBasicUuid() {
+        return basicUuid;
+    }
+
+    public void setBasicUuid(UUID basicUuid) {
+        this.basicUuid = basicUuid;
+    }
+
+    public UuidGeneratedEntity getParent() {
+        return parent;
+    }
+
+    public void setParent(UuidGeneratedEntity parent) {
+        this.parent = parent;
+    }
+    
+}
diff --git 
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
 
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
index 6d100993a..8da37ed5e 100644
--- 
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
+++ 
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
@@ -90,6 +90,7 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.Stack;
 import java.util.TreeSet;
+import java.util.UUID;
 
 import jakarta.persistence.Access;
 import jakarta.persistence.AccessType;
@@ -1410,12 +1411,22 @@ public class AnnotationPersistenceMetaDataParser
                     else
                         fmd.setValueSequenceName(generator);
                     break;
-                case AUTO:
-                    fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM);
-                    break;
                 case IDENTITY:
                     fmd.setValueStrategy(ValueStrategies.AUTOASSIGN);
                     break;
+                case AUTO:
+                    if (fmd.getType() != UUID.class) {
+                        fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM);
+                        return;
+                    }
+                case UUID:
+                    if (fmd.getType() == UUID.class) {
+                        fmd.setValueStrategy(ValueStrategies.UUID_JPA);
+                        return;
+                    } else if (fmd.getType() == String.class) {
+                        fmd.setValueStrategy(ValueStrategies.UUID_TYPE4_CANON);
+                        return;
+                    }
                 default:
                     throw new UnsupportedException(strategy.toString());
             }
diff --git a/openjpa-project/src/doc/manual/jpa_overview_meta.xml 
b/openjpa-project/src/doc/manual/jpa_overview_meta.xml
index 3054e9f8e..f6d6d0d9d 100644
--- a/openjpa-project/src/doc/manual/jpa_overview_meta.xml
+++ b/openjpa-project/src/doc/manual/jpa_overview_meta.xml
@@ -886,7 +886,8 @@ has the following values:
                         <listitem>
                             <para>
 <literal>GeneratorType.AUTO</literal>: The default. Assign the field a
-generated value, leaving the details to the JPA vendor.
+generated value, leaving the details to the JPA vendor. If the field type
+is UUID, it will be given an generated UUID.
                             </para>
                         </listitem>
                         <listitem>
@@ -907,6 +908,13 @@ generate a field value.
 field value.
                             </para>
                         </listitem>
+                        <listitem>
+                            <para>
+<literal>GenerationType.UUID</literal>: Assign the field a type 4 UUID
+generated value. In this case, the field must be either 
<classname>String</classname>
+ or <classname>UUID<classname>.
+                            </para>
+                        </listitem>
                     </itemizedlist>
                 </listitem>
                 <listitem>
@@ -1191,6 +1199,9 @@ Since JPA 2.2 the following 
<classname>java.time</classname> Types are also supp
 and <classname>java.time.OffsetDateTime</classname>.
             </para>
             <para>
+Since JPA 3.1, <classname>java.util.UUID</classname> is also supported.
+            </para>
+            <para>
 <classname>Basic</classname> declares these properties:
             </para>
             <itemizedlist>
diff --git a/openjpa-project/src/doc/manual/jpa_overview_pc.xml 
b/openjpa-project/src/doc/manual/jpa_overview_pc.xml
index ba0b3de8b..cd3d7f52a 100644
--- a/openjpa-project/src/doc/manual/jpa_overview_pc.xml
+++ b/openjpa-project/src/doc/manual/jpa_overview_pc.xml
@@ -460,6 +460,11 @@ java.lang.Byte</classname>, etc)
 <classname>java.time.OffsetTime</classname>
                     </para>
                 </listitem>
+                <listitem>
+                    <para>
+<classname>java.util.UUID</classname>
+                    </para>
+                </listitem>
             </itemizedlist>
             <para>
 JPA also supports <classname>byte[]</classname>, <classname>Byte[]</classname>,
@@ -739,7 +744,7 @@ other entities of the same type.
         <para>
 Identity fields must be primitives, primitive wrappers, <classname>
 String</classname>s, <classname>Date</classname>s, <classname>
-Timestamp</classname>s, or embeddable types.
+Timestamp</classname>s, <classname>UUID</classname>s or embeddable types.
         </para>
         <note>
             <para>
@@ -751,6 +756,14 @@ identity field, you must create an identity class.  
Identity classes are
 covered below.
             </para>
         </note>
+        <note>
+            <para>
+OpenJPA supports <classname>UUID</classname>s identity fields using the
+binary representation of UUID on the database if the database and its 
+JDBC driver supports it. When this conditions are not met, it uses the
+canonical string representation of the UUID.
+            </para>
+        </note>
         <warning>
             <para>
 Changing the fields of an embeddable instance while it is assigned to an
diff --git a/openjpa-project/src/doc/manual/supported_databases.xml 
b/openjpa-project/src/doc/manual/supported_databases.xml
index b6332e596..bc279eaf8 100644
--- a/openjpa-project/src/doc/manual/supported_databases.xml
+++ b/openjpa-project/src/doc/manual/supported_databases.xml
@@ -827,9 +827,13 @@ openjpa.ConnectionURL: jdbc:h2:DB_NAME
             <itemizedlist>
                 <listitem>
                     <para>
-None
+<classname>UUID</classname> support allows usage of binary data on entities. 
+If the user wants to exchange <classname>String</classname> for 
+<classname>UUID</classname>, it is necessary to previously convert the 
+<classname>String</classname> to UUID type in database.
                     </para>
                 </listitem>
+
             </itemizedlist>
         </section>
     </section>
@@ -1258,7 +1262,13 @@ The number of fractions can be explicitly set via scale:
                         A value of <code>@Column(scale=-1)</code> will 
explicitly turn off all fractions.
                     </para>
                 </listitem>
-
+                <listitem>
+                    <para>
+                        As of MariaDB 10.7 the <code>UUID</code> data type is 
supported,
+                        but the JDBC driver does not writes the object 
directly, so 
+                        UUID field support is made through UUID to String 
conversion.
+                    </para>
+                </listitem>
             </itemizedlist>
         </section>
     </section>
@@ -1467,6 +1477,14 @@ supported when using
 <link linkend="ref_guide_streamsupport">LOB streaming</link>.
                     </para>
                 </listitem>
+                <listitem>
+                    <para>
+<classname>UUID</classname> support allows usage of binary data on entities. 
+If the user wants to exchange <classname>String</classname> for 
+<classname>UUID</classname>, it is necessary to previously convert the 
+<classname>String</classname> to UUID type in database.
+                    </para>
+                </listitem>
             </itemizedlist>
         </section>
     </section>


Reply via email to