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 <[email protected]>
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>