mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fix for #108 - ENH (443) : Postgres HStore support
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ColumnHstore {
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,11 @@ import java.sql.Types;
|
||||
*/
|
||||
public class PostgresPlatform extends DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Unique jdbc type id defined for hstore type.
|
||||
*/
|
||||
public static final int TYPE_HSTORE = 4001;
|
||||
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
@@ -40,6 +46,8 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
|
||||
@@ -122,6 +122,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
this.bean = bean;
|
||||
this.parentBean = parentBean;
|
||||
this.controller = beanDescriptor.getPersistController();
|
||||
if (Type.UPDATE == type) {
|
||||
// Mark Mutable scalar properties (like Hstore) as dirty where necessary
|
||||
beanDescriptor.checkMutableProperties(intercept);
|
||||
}
|
||||
this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept);
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
@@ -220,6 +220,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
*/
|
||||
private final BeanProperty[] propertiesLocal;
|
||||
|
||||
/**
|
||||
* Scalar mutable properties (need to dirty check on update).
|
||||
*/
|
||||
private final BeanProperty[] propertiesMutable;
|
||||
|
||||
|
||||
private final BeanPropertyAssocOne<?> unidirectional;
|
||||
|
||||
/**
|
||||
@@ -391,6 +397,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
this.propertiesBaseCompound = listHelper.getBaseCompound();
|
||||
this.propertiesEmbedded = listHelper.getEmbedded();
|
||||
this.propertiesLocal = listHelper.getLocal();
|
||||
this.propertiesMutable = listHelper.getMutable();
|
||||
this.unidirectional = listHelper.getUnidirectional();
|
||||
this.propertiesOne = listHelper.getOnes();
|
||||
this.propertiesOneExported = listHelper.getOneExported();
|
||||
@@ -1918,7 +1925,27 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for mutable scalar types and mark as dirty if necessary.
|
||||
*/
|
||||
public void checkMutableProperties(EntityBeanIntercept ebi) {
|
||||
for (int i = 0; i < propertiesMutable.length; i++) {
|
||||
BeanProperty beanProperty = propertiesMutable[i];
|
||||
if (ebi.isDirtyProperty(beanProperty.getPropertyIndex())) {
|
||||
// already marked as dirty
|
||||
} else if (ebi.isLoadedProperty(beanProperty.getPropertyIndex())) {
|
||||
Object value = beanProperty.getValue(ebi.getOwner());
|
||||
if (value == null || beanProperty.isDirtyValue(value)) {
|
||||
// mutable scalar value which is considered dirty so mark
|
||||
// it as such so that it is included in an update
|
||||
ebi.markPropertyAsChanged(beanProperty.getPropertyIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
|
||||
|
||||
if (!hasVersionProperty(ebi)) {
|
||||
return ConcurrencyMode.NONE;
|
||||
} else {
|
||||
|
||||
@@ -477,6 +477,13 @@ public class BeanProperty implements ElPropertyValue {
|
||||
public boolean isDiscriminator() {
|
||||
return discriminator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the underlying type is mutable.
|
||||
*/
|
||||
public boolean isMutableScalarType() {
|
||||
return scalarType.isMutable();
|
||||
}
|
||||
|
||||
public void copyProperty(EntityBean sourceBean, EntityBean destBean) {
|
||||
Object value = getValue(sourceBean);
|
||||
@@ -868,6 +875,14 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return descriptor.getFullName() + "." + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the mutable value is considered dirty.
|
||||
* This is only used for 'mutable' scalar types like hstore etc.
|
||||
*/
|
||||
public boolean isDirtyValue(Object value) {
|
||||
return scalarType.isDirty(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scalarType.
|
||||
*/
|
||||
|
||||
+11
-1
@@ -37,6 +37,8 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
private final ArrayList<BeanProperty> local = new ArrayList<BeanProperty>();
|
||||
|
||||
private final ArrayList<BeanProperty> mutable = new ArrayList<BeanProperty>();
|
||||
|
||||
private final ArrayList<BeanProperty> manys = new ArrayList<BeanProperty>();
|
||||
|
||||
private final ArrayList<BeanProperty> nonManys = new ArrayList<BeanProperty>();
|
||||
@@ -135,6 +137,10 @@ public class DeployBeanPropertyLists {
|
||||
nonTransients.add(prop);
|
||||
}
|
||||
|
||||
if (prop.isMutableScalarType()) {
|
||||
mutable.add(prop);
|
||||
}
|
||||
|
||||
if (desc.getInheritInfo() != null && prop.isLocal()) {
|
||||
local.add(prop);
|
||||
}
|
||||
@@ -197,7 +203,7 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
public BeanProperty getId() {
|
||||
if (ids.size() > 1) {
|
||||
String msg = "Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
|
||||
String msg = "Issue with bean "+desc+". Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
|
||||
+" Please email the ebean google group if you need further clarification.";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
@@ -223,6 +229,10 @@ public class DeployBeanPropertyLists {
|
||||
return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]);
|
||||
}
|
||||
|
||||
public BeanProperty[] getMutable() {
|
||||
return (BeanProperty[]) mutable.toArray(new BeanProperty[mutable.size()]);
|
||||
}
|
||||
|
||||
public BeanPropertyAssocOne<?>[] getEmbedded() {
|
||||
return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Iterator;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import com.avaje.ebean.annotation.ColumnHstore;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.deploy.DetermineManyType;
|
||||
import com.avaje.ebeaninternal.server.deploy.ManyType;
|
||||
@@ -21,8 +22,10 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypePostgresHstore;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -276,6 +279,16 @@ public class DeployCreateProperties {
|
||||
Class<?> propertyType = field.getType();
|
||||
Class<?> innerType = propertyType;
|
||||
|
||||
String specialTypeKey = getSpecialScalarType(field);
|
||||
if (specialTypeKey != null) {
|
||||
ScalarType<?> scalarType = typeManager.getScalarTypeFromKey(specialTypeKey);
|
||||
if (scalarType == null) {
|
||||
logger.error("Could not find ScalarType to match key ["+specialTypeKey+"]");
|
||||
} else {
|
||||
return new DeployBeanProperty(desc, propertyType, scalarType, null);
|
||||
}
|
||||
}
|
||||
|
||||
// check for Collection type (list, set or map)
|
||||
ManyType manyType = determineManyType.getManyType(propertyType);
|
||||
|
||||
@@ -336,6 +349,15 @@ public class DeployCreateProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private String getSpecialScalarType(Field field) {
|
||||
|
||||
if (field.getAnnotation(ColumnHstore.class) != null) {
|
||||
return ScalarTypePostgresHstore.KEY;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isTransientField(Field field) {
|
||||
|
||||
Transient t = field.getAnnotation(Transient.class);
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.avaje.ebeaninternal.server.type.reflect.KnownImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundTypeProperty;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedTypeBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -64,6 +65,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
private final ConcurrentHashMap<Integer, ScalarType<?>> nativeMap;
|
||||
|
||||
private final ConcurrentHashMap<String, ScalarType<?>> customTypeMap;
|
||||
|
||||
private final DefaultTypeFactory extraTypeFactory;
|
||||
|
||||
private final ScalarType<?> charType = new ScalarTypeChar();
|
||||
@@ -136,6 +139,9 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
this.compoundTypeMap = new ConcurrentHashMap<Class<?>, CtCompoundType<?>>();
|
||||
this.typeMap = new ConcurrentHashMap<Class<?>, ScalarType<?>>();
|
||||
this.nativeMap = new ConcurrentHashMap<Integer, ScalarType<?>>();
|
||||
this.customTypeMap = new ConcurrentHashMap<String, ScalarType<?>>();
|
||||
|
||||
this.customTypeMap.put(ScalarTypePostgresHstore.KEY, new ScalarTypePostgresHstore());
|
||||
|
||||
this.extraTypeFactory = new DefaultTypeFactory(config);
|
||||
|
||||
@@ -149,6 +155,14 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a special or custom scalar type by key.
|
||||
*/
|
||||
@Override
|
||||
public ScalarType<?> getScalarTypeFromKey(String specialTypeKey) {
|
||||
return customTypeMap.get(specialTypeKey);
|
||||
}
|
||||
|
||||
public boolean isKnownImmutable(Class<?> cls) {
|
||||
|
||||
if (cls == null) {
|
||||
@@ -613,7 +627,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
* plus some other common types such as java.util.Date and java.util.Calendar.
|
||||
*/
|
||||
protected void initialiseStandard(int platformClobType, int platformBlobType, boolean binaryUUID) {
|
||||
|
||||
|
||||
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate();
|
||||
typeMap.put(java.util.Date.class, utilDateType);
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Wraps a collection for the purposes of detecting modifications.
|
||||
*/
|
||||
public class ModifyAwareCollection<E> implements Collection<E> {
|
||||
|
||||
protected final ModifyAwareOwner owner;
|
||||
|
||||
protected final Collection<E> c;
|
||||
|
||||
/**
|
||||
* Create with an Owner and the underlying collection this wraps.
|
||||
* <p>
|
||||
* The owner is notified of the additions and removals.
|
||||
* </p>
|
||||
*/
|
||||
public ModifyAwareCollection(ModifyAwareOwner owner, Collection<E> c) {
|
||||
this.owner = owner;
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return c.toString();
|
||||
}
|
||||
|
||||
public boolean add(E o) {
|
||||
if (c.add(o)) {
|
||||
owner.markAsModified();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> collection) {
|
||||
boolean changed = false;
|
||||
Iterator<? extends E> it = collection.iterator();
|
||||
while (it.hasNext()) {
|
||||
E o = it.next();
|
||||
if (c.add(o)) {
|
||||
owner.markAsModified();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (!c.isEmpty()) {
|
||||
owner.markAsModified();
|
||||
}
|
||||
c.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return c.contains(o);
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
return c.containsAll(collection);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return c.isEmpty();
|
||||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
return new ModifyAwareIterator<E>(owner, c.iterator());
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
if (c.remove(o)) {
|
||||
owner.markAsModified();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
boolean changed = false;
|
||||
Iterator<?> it = collection.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object o = (Object) it.next();
|
||||
if (c.remove(o)) {
|
||||
owner.markAsModified();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
boolean changed = false;
|
||||
Iterator<?> it = c.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object o = (Object) it.next();
|
||||
if (!collection.contains(o)) {
|
||||
it.remove();
|
||||
owner.markAsModified();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return c.size();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return c.toArray();
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return c.toArray(a);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Wraps an iterator for the purposes of detecting modifications.
|
||||
*/
|
||||
public class ModifyAwareIterator<E> implements Iterator<E> {
|
||||
|
||||
private final ModifyAwareOwner owner;
|
||||
|
||||
private final Iterator<E> it;
|
||||
|
||||
/**
|
||||
* Create with an Owner and the underlying Iterator this wraps.
|
||||
* <p>
|
||||
* The owner is notified of the removals.
|
||||
* </p>
|
||||
*/
|
||||
public ModifyAwareIterator(ModifyAwareOwner owner, Iterator<E> it) {
|
||||
this.owner = owner;
|
||||
this.it = it;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
return it.next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
owner.markAsModified();
|
||||
it.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Map that is wraps an underlying map for the purpose of detecting changes.
|
||||
*/
|
||||
public class ModifyAwareMap<K,V> implements Map<K,V>, ModifyAwareOwner {
|
||||
|
||||
/**
|
||||
* Dirty flag set when the map has been modified.
|
||||
*/
|
||||
private boolean dirty;
|
||||
|
||||
/**
|
||||
* The underlying map.
|
||||
*/
|
||||
private Map<K,V> map;
|
||||
|
||||
public ModifyAwareMap(Map<K,V> underyling) {
|
||||
this.map = underyling;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsModified() {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
markAsModified();
|
||||
return map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
V value = map.remove(key);
|
||||
if (value != null) {
|
||||
markAsModified();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
markAsModified();
|
||||
map.putAll(m);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (!map.isEmpty()) {
|
||||
markAsModified();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return new ModifyAwareSet<K>(this, map.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return new ModifyAwareCollection<V>(this, map.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
return new ModifyAwareSet<Map.Entry<K, V>>(this, map.entrySet());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
/**
|
||||
* Owner object notified when a modification is detected.
|
||||
*/
|
||||
public interface ModifyAwareOwner {
|
||||
|
||||
/**
|
||||
* Return true if the value is considered dirty.
|
||||
*/
|
||||
public boolean isMarkedDirty();
|
||||
|
||||
/**
|
||||
* Marks the object as modified.
|
||||
*/
|
||||
public void markAsModified();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Wraps a Set for the purposes of detecting modifications.
|
||||
*/
|
||||
public class ModifyAwareSet<E> extends ModifyAwareCollection<E> implements Set<E> {
|
||||
|
||||
/**
|
||||
* Create with an Owner that is notified of modifications.
|
||||
*/
|
||||
public ModifyAwareSet(ModifyAwareOwner owner, Set<E> s) {
|
||||
super(owner, s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
public interface ModifyAwareType {
|
||||
|
||||
public boolean isDirty();
|
||||
}
|
||||
@@ -34,6 +34,17 @@ import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer;
|
||||
*/
|
||||
public interface ScalarType<T> extends StringParser, StringFormatter, ScalarDataReader<T> {
|
||||
|
||||
/**
|
||||
* Return true if this is a mutable scalar type (like hstore).
|
||||
*/
|
||||
public boolean isMutable();
|
||||
|
||||
/**
|
||||
* For mutable scalarType's return true if the value is dirty.
|
||||
* Non-dirty properties may be excluded from updates.
|
||||
*/
|
||||
public boolean isDirty(Object value);
|
||||
|
||||
/**
|
||||
* Return the default DB column length for this type.
|
||||
* <p>
|
||||
|
||||
@@ -21,6 +21,22 @@ public abstract class ScalarTypeBase<T> implements ScalarType<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of mutable false.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default to true.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just return 0.
|
||||
*/
|
||||
public int getLength() {
|
||||
|
||||
@@ -26,6 +26,16 @@ public class ScalarTypeBytesEncrypted implements ScalarType<byte[]> {
|
||||
this.dataEncryptSupport = dataEncryptSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void bind(DataBind b, byte[] value) throws SQLException {
|
||||
value = dataEncryptSupport.encrypt(value);
|
||||
baseType.bind(b, value);
|
||||
|
||||
@@ -22,6 +22,16 @@ public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
this.dataEncryptSupport = dataEncryptSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return wrapped.isMutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return wrapped.isDirty(value);
|
||||
}
|
||||
|
||||
public Object readData(DataInput dataInput) throws IOException {
|
||||
return wrapped.readData(dataInput);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
|
||||
|
||||
/**
|
||||
* Postgres Hstore type which maps Map<String,String> to a single 'HStore column' in the DB.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
|
||||
|
||||
public static final String KEY = "hstore";
|
||||
|
||||
public static final int HSTORE_TYPE = PostgresPlatform.TYPE_HSTORE;
|
||||
|
||||
public ScalarTypePostgresHstore() {
|
||||
super(Map.class, false, HSTORE_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
if (value instanceof ModifyAwareOwner) {
|
||||
return ((ModifyAwareOwner)value).isMarkedDirty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Map read(DataReader dataReader) throws SQLException {
|
||||
|
||||
Object value = dataReader.getObject();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Map == false) {
|
||||
throw new RuntimeException("Expecting Hstore to return as Map but got type "+value.getClass());
|
||||
}
|
||||
return new ModifyAwareMap((Map)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Map value) throws SQLException {
|
||||
b.setObject(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map toBeanType(Object value) {
|
||||
return (Map)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Map v) {
|
||||
// TODO format as json
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map parse(String value) {
|
||||
// TODO parse json into map
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map parseDateTime(long dateTime) {
|
||||
throw new RuntimeException("Should never be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readData(DataInput dataInput) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, Object v) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -37,11 +37,21 @@ public class ScalarTypeWrapper<B, S> implements ScalarType<B> {
|
||||
this.nullValue = converter.getNullValue();
|
||||
this.wrapperType = wrapperType;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return "ScalarTypeWrapper " + wrapperType + " to " + scalarType.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return scalarType.isMutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return scalarType.isDirty(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object readData(DataInput dataInput) throws IOException {
|
||||
Object v = scalarType.readData(dataInput);
|
||||
|
||||
@@ -57,4 +57,9 @@ public interface TypeManager {
|
||||
* or String which has limitations).
|
||||
*/
|
||||
public ScalarType<?> createEnumScalarType(Class<?> enumType);
|
||||
|
||||
/**
|
||||
* Find a scalarType using a custom type key. Used for Hstore and similar special types.
|
||||
*/
|
||||
public ScalarType<?> getScalarTypeFromKey(String specialTypeKey);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user