From 25ac9113921e5bd0ed5c373fcd0ac586392d049c Mon Sep 17 00:00:00 2001
From: Vlad Mihalcea
jsonb
and json
column typesjson
column typeNVARCHAR
column type storing JSONVARCHAR
column type storing JSONjson
column typejsonb
and json
column typesjson
column typeNVARCHAR
column type storing JSONJSON
column type if you're using Oracle 21c or the VARCHAR
column type storing JSON if you're using an older Oracle versionjson
column type+ * If you switch to Oracle 21c from an older version, then you should also migrate your {@code JSON} columns to the native JSON type since this binary type performs better than + * {@code VARCHAR2} or {@code BLOB} column types. + *
+ *+ * However, if you don't want to migrate to the new {@code JSON} data type, + * then you just have to provide the column type via the JPA {@link Column#columnDefinition()} attribute, + * like in the following example: + *
+ *+ * {@code @Type(}type = "com.vladmihalcea.hibernate.type.json.JsonType") + * {@code @Column(}columnDefinition = "VARCHAR2") + **
* For more details about how to use the {@link JsonType}, check out this article on vladmihalcea.com. *
*
- * If you are using Oracle and want to store JSON objects in a BLOB
column types, then you should use the {@link JsonBlobType} instead. For more details, check out this article on vladmihalcea.com.
+ * If you are using Oracle and want to store JSON objects in a BLOB
column type, then you can use the {@link JsonBlobType} instead. For more details, check out this article on vladmihalcea.com.
*
+ * Or, you can use the {@link JsonType}, but you'll have to specify the underlying column type + * using the JPA {@link Column#columnDefinition()} attribute, like this: + *
+ *+ * {@code @Type(}type = "com.vladmihalcea.hibernate.type.json.JsonType") + * {@code @Column(}columnDefinition = "BLOB") + * private String properties; + ** @author Vlad Mihalcea */ public class JsonType @@ -53,7 +76,7 @@ public JsonType(Type javaType) { public JsonType(Configuration configuration) { super( - new JsonSqlTypeDescriptor(), + new JsonSqlTypeDescriptor(configuration.getProperties()), new JsonTypeDescriptor(configuration.getObjectMapperWrapper()), configuration ); @@ -94,6 +117,10 @@ public String getName() { @Override public void setParameterValues(Properties parameters) { ((JsonTypeDescriptor) getJavaTypeDescriptor()).setParameterValues(parameters); + SqlTypeDescriptor sqlTypeDescriptor = getSqlTypeDescriptor(); + if(sqlTypeDescriptor instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) sqlTypeDescriptor; + parameterizedType.setParameterValues(parameters); + } } - } \ No newline at end of file diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonBlobSqlTypeDescriptor.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonBlobSqlTypeDescriptor.java new file mode 100644 index 000000000..6e4fb2220 --- /dev/null +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonBlobSqlTypeDescriptor.java @@ -0,0 +1,31 @@ +package com.vladmihalcea.hibernate.type.json.internal; + +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.descriptor.sql.BlobTypeDescriptor; + +/** + * @author Vlad Mihalcea + */ +public class JsonBlobSqlTypeDescriptor extends AbstractJsonSqlTypeDescriptor { + + public static final JsonBlobSqlTypeDescriptor INSTANCE = new JsonBlobSqlTypeDescriptor(); + + private BlobTypeDescriptor blobTypeDescriptor = BlobTypeDescriptor.DEFAULT; + + @Override + public
ParameterizedTypeUtils
- {@link DynamicParameterizedType.ParameterType} utilities holder.
+ *
+ * @author Vlad Mihalcea
+ * @since 2.16.0
+ */
+public class ParameterTypeUtils {
+
+ private static final Pattern COLUMN_TYPE_PATTERN = Pattern.compile("([a-zA-Z0-9]+).*?");
+
+ private ParameterTypeUtils() {
+ throw new UnsupportedOperationException("StringUtils is not instantiable!");
+ }
+
+ /**
+ * Resolve the {@link DynamicParameterizedType.ParameterType} instance
+ * from the provided {@link Properties} object.
+ *
+ * @param properties configuration properties
+ * @return {@link DynamicParameterizedType.ParameterType} instance
+ */
+ public static DynamicParameterizedType.ParameterType resolve(Properties properties) {
+ Object parameterTypeObject = properties.get(DynamicParameterizedType.PARAMETER_TYPE);
+ if (parameterTypeObject instanceof DynamicParameterizedType.ParameterType) {
+ return (DynamicParameterizedType.ParameterType) parameterTypeObject;
+ }
+ return null;
+ }
+
+ /**
+ * Get the required annotation from the {@link DynamicParameterizedType.ParameterType} instance.
+ *
+ * @param parameterType {@link DynamicParameterizedType.ParameterType} instance
+ * @param annotationClass annotation class
+ * @return annotation
+ */
+ @SuppressWarnings("unchecked")
+ public static A getAnnotationOrNull(DynamicParameterizedType.ParameterType parameterType, Class annotationClass) {
+ List annotations = getAnnotations(parameterType, annotationClass);
+ if(annotations.size() > 1) {
+ throw new IllegalArgumentException(
+ String.format(
+ "The provided ParameterType associated with the [%s] property contains more than one annotation of the [%s] type!",
+ parameterType.getReturnedClass(),
+ annotationClass.getName()
+ )
+ );
+ }
+ return (A) Arrays.stream(parameterType.getAnnotationsMethod())
+ .filter(a -> annotationClass.isAssignableFrom(a.annotationType()))
+ .findAny()
+ .orElse(null);
+ }
+
+ /**
+ * Get the required annotations from the {@link DynamicParameterizedType.ParameterType} instance.
+ *
+ * @param parameterType {@link DynamicParameterizedType.ParameterType} instance
+ * @param annotationClass annotation class
+ * @return annotations
+ */
+ @SuppressWarnings("unchecked")
+ public static List getAnnotations(DynamicParameterizedType.ParameterType parameterType, Class annotationClass) {
+ return Arrays.stream(parameterType.getAnnotationsMethod())
+ .filter(a -> annotationClass.isAssignableFrom(a.annotationType()))
+ .map(a -> (A) a)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get the column type association from the {@link DynamicParameterizedType.ParameterType} instance.
+ *
+ * @param parameterType {@link DynamicParameterizedType.ParameterType} instance
+ * @return column type
+ */
+ public static String getColumnType(DynamicParameterizedType.ParameterType parameterType) {
+ if (parameterType != null) {
+ Column columnAnnotation = ParameterTypeUtils.getAnnotationOrNull(parameterType, Column.class);
+ if(columnAnnotation != null) {
+ String columnDefinition = columnAnnotation.columnDefinition();
+ if(!StringUtils.isBlank(columnDefinition)) {
+ Matcher matcher = COLUMN_TYPE_PATTERN.matcher(columnDefinition);
+ if (matcher.matches()) {
+ return StringUtils.toLowercase(matcher.group(1));
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/util/StringUtils.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/util/StringUtils.java
index 473aff942..6a5a95a47 100644
--- a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/util/StringUtils.java
+++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/util/StringUtils.java
@@ -1,5 +1,7 @@
package com.vladmihalcea.hibernate.util;
+import java.util.Locale;
+
/**
* StringUtils
- String utilities holder.
*
@@ -38,4 +40,25 @@ public static String join(CharSequence delimiter, CharSequence... elements) {
return builder.toString();
}
+ /**
+ * Check if the String value is null, empty or contains only whitespace characters.
+ * @param value String value
+ * @return if the string is blank
+ */
+ public static boolean isBlank(String value) {
+ return value == null || value.isEmpty() || value.trim().isEmpty();
+ }
+
+ /**
+ * Transform string to lowercase.
+ *
+ * @param value String value
+ * @return String value in lowercase
+ */
+ public static String toLowercase(String value) {
+ if(isBlank(value)) {
+ return value;
+ }
+ return value.toLowerCase(Locale.ROOT);
+ }
}
diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/MySQLGenericJsonTypeTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericMySQLJsonTypeTest.java
similarity index 96%
rename from hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/MySQLGenericJsonTypeTest.java
rename to hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericMySQLJsonTypeTest.java
index b94e8890d..74cf0b213 100644
--- a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/MySQLGenericJsonTypeTest.java
+++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericMySQLJsonTypeTest.java
@@ -1,4 +1,4 @@
-package com.vladmihalcea.hibernate.type.json;
+package com.vladmihalcea.hibernate.type.json.generic;
import com.vladmihalcea.hibernate.type.model.BaseEntity;
import com.vladmihalcea.hibernate.type.model.Location;
@@ -21,7 +21,7 @@
/**
* @author Vlad Mihalcea
*/
-public class MySQLGenericJsonTypeTest extends AbstractMySQLIntegrationTest {
+public class GenericMySQLJsonTypeTest extends AbstractMySQLIntegrationTest {
@Override
protected Class>[] entities() {
diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/OffsetDateTimeJsonTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericOffsetDateTimeJsonTest.java
similarity index 93%
rename from hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/OffsetDateTimeJsonTest.java
rename to hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericOffsetDateTimeJsonTest.java
index ae7403949..4f548d5ea 100644
--- a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/OffsetDateTimeJsonTest.java
+++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/generic/GenericOffsetDateTimeJsonTest.java
@@ -1,5 +1,6 @@
-package com.vladmihalcea.hibernate.type.json;
+package com.vladmihalcea.hibernate.type.json.generic;
+import com.vladmihalcea.hibernate.type.json.JsonType;
import com.vladmihalcea.hibernate.util.AbstractPostgreSQLIntegrationTest;
import org.hibernate.annotations.TypeDef;
import org.junit.Test;
@@ -18,7 +19,7 @@
/**
* @author Vlad Mihalcea
*/
-public class OffsetDateTimeJsonTest extends AbstractPostgreSQLIntegrationTest {
+public class GenericOffsetDateTimeJsonTest extends AbstractPostgreSQLIntegrationTest {
@Override
protected Class>[] entities() {
diff --git a/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/json/JsonBlobType.java b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/json/JsonBlobType.java
index 25d37824c..3d4575b5d 100644
--- a/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/json/JsonBlobType.java
+++ b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/json/JsonBlobType.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vladmihalcea.hibernate.type.AbstractHibernateType;
+import com.vladmihalcea.hibernate.type.json.internal.JsonBlobSqlTypeDescriptor;
import com.vladmihalcea.hibernate.type.json.internal.JsonTypeDescriptor;
import com.vladmihalcea.hibernate.type.util.Configuration;
import com.vladmihalcea.hibernate.type.util.ObjectMapperWrapper;
@@ -33,21 +34,21 @@ public class JsonBlobType extends AbstractHibernateType