Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package net.kaleidos.hibernate;

import net.kaleidos.hibernate.usertype.HstoreType;
import net.kaleidos.hibernate.usertype.ArrayType;

import net.kaleidos.hibernate.usertype.HstoreType;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.id.PersistentIdentifierGenerator;
Expand Down Expand Up @@ -31,6 +30,7 @@ public PostgresqlExtensionsDialect() {

/**
* Get the native identifier generator class.
*
* @return TableNameSequenceGenerator.
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package net.kaleidos.hibernate.criterion.array;

import net.kaleidos.hibernate.usertype.ArrayType;

import net.kaleidos.hibernate.utils.PgArrayUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Criterion;
import org.hibernate.engine.TypedValue;
import org.hibernate.type.Type;
import org.hibernate.type.CustomType;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;

/**
Expand All @@ -18,8 +18,6 @@ public class PgArrayExpression implements Criterion {

private static final long serialVersionUID = 2872183637309166619L;

private final PgCriteriaUtils pgCriteriaUtils = new PgCriteriaUtils();

private final String propertyName;
private final Object value;
private final String op;
Expand All @@ -32,44 +30,52 @@ protected PgArrayExpression(String propertyName, Object value, String op) {

@Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
ArrayType arrayType = checkAndGetArrayType(criteria, criteriaQuery);

String postgresArrayType = PgArrayUtils.getNativeSqlType(arrayType.getTypeClass()) + "[]";

return StringHelper.join(
" and ",
StringHelper.suffix(criteriaQuery.findColumns(propertyName, criteria), " " + op + " ARRAY[?]")
" and ",
StringHelper.suffix(criteriaQuery.findColumns(propertyName, criteria), " " + op + " CAST(? as " + postgresArrayType + ")")
);
}

@Override
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
Type propertyType = criteriaQuery.getType(criteria, propertyName);

if (!(propertyType instanceof CustomType) || !(((CustomType)propertyType).getUserType() instanceof ArrayType)) {
throw new HibernateException("Property is not an instance of the postgres type ArrayType. Type is: " + propertyType.getClass());
}

ArrayType arrayType = (ArrayType)((CustomType)propertyType).getUserType();
ArrayType arrayType = checkAndGetArrayType(criteria, criteriaQuery);

Object[] arrValue;
if (arrayType.getTypeClass().isEnum()) {
arrValue = pgCriteriaUtils.getValueAsArrayOfType(
value,
Integer.class,
new PgCriteriaUtils.MapFunction() {
@SuppressWarnings("rawtypes")
public Object map(Object o) {
try {
return ((Enum)o).ordinal();
} catch (ClassCastException e) {
throw new HibernateException("Unable to cast object " + o + " to Enum.");
arrValue = PgArrayUtils.getValueAsArrayOfType(
value,
Integer.class,
new PgArrayUtils.MapFunction() {
@SuppressWarnings("rawtypes")
public Object map(Object o) {
try {
return ((Enum) o).ordinal();
} catch (ClassCastException e) {
throw new HibernateException("Unable to cast object " + o + " to Enum.");
}
}
}
}
);
} else {
arrValue = pgCriteriaUtils.getValueAsArrayOfType(value, arrayType.getTypeClass());
arrValue = PgArrayUtils.getValueAsArrayOfType(value, arrayType.getTypeClass());
}

return new TypedValue[] {
criteriaQuery.getTypedValue(criteria, propertyName, arrValue)
return new TypedValue[]{
criteriaQuery.getTypedValue(criteria, propertyName, arrValue)
};
}

private ArrayType checkAndGetArrayType(Criteria criteria, CriteriaQuery criteriaQuery) {
Type propertyType = criteriaQuery.getType(criteria, propertyName);

if (!(propertyType instanceof CustomType) || !(((CustomType) propertyType).getUserType() instanceof ArrayType)) {
throw new HibernateException("Property is not an instance of the postgres type ArrayType. Type is: " + propertyType.getClass());
}

return (ArrayType) ((CustomType) propertyType).getUserType();
}
}
72 changes: 23 additions & 49 deletions src/java/net/kaleidos/hibernate/usertype/ArrayType.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package net.kaleidos.hibernate.usertype;

import java.io.Serializable;
import java.util.Properties;

import net.kaleidos.hibernate.utils.PgArrayUtils;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

import java.io.Serializable;
import java.sql.Array;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;


public class ArrayType implements UserType, ParameterizedType {
Expand Down Expand Up @@ -62,47 +62,44 @@ public Object replace(Object original, Object target, Object owner) throws Hiber

@Override
public void setParameterValues(Properties parameters) {
this.typeClass = (Class<?>)parameters.get("type");
this.typeClass = (Class<?>) parameters.get("type");
if (typeClass == null) {
throw new RuntimeException("The user type needs to be configured with the type. None provided");
}
}




@Override
public Class<?> returnedClass() {
return java.lang.reflect.Array.newInstance(this.typeClass, 0).getClass();
}

@Override
public int[] sqlTypes() {
if (Integer.class.equals(this.typeClass)){
return new int[] { INTEGER_ARRAY };
if (Integer.class.equals(this.typeClass)) {
return new int[]{INTEGER_ARRAY};
}

if (Long.class.equals(this.typeClass)){
return new int[] { LONG_ARRAY };
if (Long.class.equals(this.typeClass)) {
return new int[]{LONG_ARRAY};
}

if (String.class.equals(this.typeClass)){
return new int[] { STRING_ARRAY };
if (String.class.equals(this.typeClass)) {
return new int[]{STRING_ARRAY};
}

if (Float.class.equals(this.typeClass)){
return new int[] { FLOAT_ARRAY };
if (Float.class.equals(this.typeClass)) {
return new int[]{FLOAT_ARRAY};
}

if (Double.class.equals(this.typeClass)){
return new int[] { DOUBLE_ARRAY };
if (Double.class.equals(this.typeClass)) {
return new int[]{DOUBLE_ARRAY};
}

if (this.typeClass.isEnum()) {
return new int[] { ENUM_INTEGER_ARRAY };
return new int[]{ENUM_INTEGER_ARRAY};
}

throw new RuntimeException("The type " + this.typeClass + " is not a valid type");
throw new RuntimeException("The type " + this.typeClass + " is not a valid type");
}

@Override
Expand All @@ -115,10 +112,10 @@ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws Hib
int length = java.lang.reflect.Array.getLength(array);
Object converted = java.lang.reflect.Array.newInstance(typeClass, length);
for (int i = 0; i < length; i++) {
java.lang.reflect.Array.set(converted, i, idToEnum(java.lang.reflect.Array.get(array,i)));
java.lang.reflect.Array.set(converted, i, idToEnum(java.lang.reflect.Array.get(array, i)));
}
} else {
result = (Object[])typeArrayClass.cast(array.getArray());
result = (Object[]) typeArrayClass.cast(array.getArray());
}
}
return result;
Expand All @@ -131,7 +128,7 @@ public void nullSafeSet(PreparedStatement st, Object value, int index) throws Hi
return;
}

Object[] valueToSet = (Object[])value;
Object[] valueToSet = (Object[]) value;
Class typeArrayClass = java.lang.reflect.Array.newInstance(typeClass, 0).getClass();

if (typeClass.isEnum()) {
Expand All @@ -140,41 +137,18 @@ public void nullSafeSet(PreparedStatement st, Object value, int index) throws Hi

for (int i = 0; i < valueToSet.length; i++) {
if (valueToSet[i] instanceof Integer) {
converted[i] = (Integer)valueToSet[i];
converted[i] = (Integer) valueToSet[i];
} else {
converted[i] = ((Enum)valueToSet[i]).ordinal();
converted[i] = ((Enum) valueToSet[i]).ordinal();
}
}
valueToSet = converted;
}

Array array = st.getConnection().createArrayOf(getNativeSqlType(typeClass), (Object[])typeArrayClass.cast(valueToSet));
Array array = st.getConnection().createArrayOf(PgArrayUtils.getNativeSqlType(typeClass), (Object[]) typeArrayClass.cast(valueToSet));
st.setArray(index, array);
}

private String getNativeSqlType(Class clazz) {
if (Integer.class.equals(this.typeClass) || this.typeClass.isEnum()){
return "int";
}

if (Long.class.equals(this.typeClass)){
return "int8";
}

if (String.class.equals(this.typeClass)){
return "varchar";
}

if (Float.class.equals(this.typeClass)){
return "float";
}

if (Double.class.equals(this.typeClass)){
return "float8";
}
throw new RuntimeException("Type class not valid: " + this.typeClass);
}

public Class<?> getTypeClass() {
return this.typeClass;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
package net.kaleidos.hibernate.criterion.array;
package net.kaleidos.hibernate.utils;

import org.hibernate.HibernateException;

import java.lang.reflect.Array;
import java.util.List;

import org.hibernate.HibernateException;

/**
* Protected class with utils for the different criteria queries
*/
class PgCriteriaUtils {
public class PgArrayUtils {
/**
* Returns a new array wrapping the parameter value. The type of the array
* will be the type passed as parameter
* @param targetValue The value we want to wrap as an array
*
* @param targetValue The value we want to wrap as an array
* @param expectedType The expected type of the returned array
* @param mapFunction If non-null, it will transform each object in the array to a given object.
* @param mapFunction If non-null, it will transform each object in the array to a given object.
* @return
*/
@SuppressWarnings("unchecked")
public Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType, MapFunction mapFunction) {
public static Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType, MapFunction mapFunction) {
Object[] arrValue;

if (targetValue instanceof List) {
List<Object> valueAsList = (List<Object>)targetValue;
List<Object> valueAsList = (List<Object>) targetValue;
arrValue = (Object[]) Array.newInstance(expectedType, valueAsList.size());

// We will iterate the collection and if the value it's not a valid value we throw the exception
Expand All @@ -33,7 +34,7 @@ public Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType,
arrValue[i] = expectedType.cast(mapFunction.map(valueAsList.get(i)));
} else {
throw new HibernateException("criteria doesn't support values of type: " +
targetValue.getClass().getName() + ". Try: " + expectedType + " or List<" + expectedType + "> instead");
targetValue.getClass().getName() + ". Try: " + expectedType + " or List<" + expectedType + "> instead");
}
}
} else if (expectedType.isInstance(targetValue) || mapFunction != null) {
Expand All @@ -45,18 +46,18 @@ public Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType,
arrValue[0] = expectedType.cast(targetValue);
}
} else if (targetValue instanceof Object[]) {
arrValue = (Object[])targetValue;
arrValue = (Object[]) targetValue;
} else {
throw new HibernateException("criteria doesn't support values of type: " +
targetValue.getClass().getName() + ". Try: " + expectedType + " or List<" + expectedType + "> instead");
targetValue.getClass().getName() + ". Try: " + expectedType + " or List<" + expectedType + "> instead");
}
return arrValue;
}

/**
* Overloaded version of getValueAsArrayOfType that doesn't use a mapFunction
*/
public Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType) {
public static Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType) {
return getValueAsArrayOfType(targetValue, expectedType, null);
}

Expand All @@ -66,9 +67,33 @@ public Object[] getValueAsArrayOfType(Object targetValue, Class<?> expectedType)
public static abstract class MapFunction {
/**
* Transforms an object into some new value.
*
* @param o the object we want to transform
* @return some transformed version of the object
*/
public abstract Object map(Object o);
}

public static String getNativeSqlType(Class clazz) {
if (Integer.class.equals(clazz) || clazz.isEnum()) {
return "int";
}

if (Long.class.equals(clazz)) {
return "int8";
}

if (String.class.equals(clazz)) {
return "varchar";
}

if (Float.class.equals(clazz)) {
return "float";
}

if (Double.class.equals(clazz)) {
return "float8";
}
throw new RuntimeException("Type class not valid: " + clazz);
}
}