mirror of
https://github.com/apache/openjpa.git
synced 2025-03-06 16:39:11 +00:00
Merge pull request #81 from tandraschko/master
OPENJPA-2877 Implement JPA2.1 @Convert / AttributeConverter
This commit is contained in:
commit
cb258aee42
@ -188,6 +188,7 @@ public class FieldMetaData
|
|||||||
private Boolean _serializableField = null;
|
private Boolean _serializableField = null;
|
||||||
private boolean _generated = false;
|
private boolean _generated = false;
|
||||||
private boolean _useSchemaElement = true;
|
private boolean _useSchemaElement = true;
|
||||||
|
private Class _converter;
|
||||||
|
|
||||||
// Members aren't serializable. Use a proxy that can provide a Member
|
// Members aren't serializable. Use a proxy that can provide a Member
|
||||||
// to avoid writing the full Externalizable implementation.
|
// to avoid writing the full Externalizable implementation.
|
||||||
@ -198,6 +199,10 @@ public class FieldMetaData
|
|||||||
private transient Method _extMethod = DEFAULT_METHOD;
|
private transient Method _extMethod = DEFAULT_METHOD;
|
||||||
private transient Member _factMethod = DEFAULT_METHOD;
|
private transient Member _factMethod = DEFAULT_METHOD;
|
||||||
|
|
||||||
|
private transient Constructor _converterConstructor;
|
||||||
|
private transient Method _converterExtMethod;
|
||||||
|
private transient Method _converterFactMethod;
|
||||||
|
|
||||||
// intermediate and impl data
|
// intermediate and impl data
|
||||||
private boolean _intermediate = true;
|
private boolean _intermediate = true;
|
||||||
private Boolean _implData = Boolean.TRUE;
|
private Boolean _implData = Boolean.TRUE;
|
||||||
@ -1317,35 +1322,58 @@ public class FieldMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
Method externalizer = getExternalizerMethod();
|
Method externalizer = getExternalizerMethod();
|
||||||
if (externalizer == null)
|
if (externalizer != null) {
|
||||||
return val;
|
// special case for queries: allow the given value to pass through
|
||||||
|
// as-is if it is already in externalized form
|
||||||
|
if (val != null && getType().isInstance(val)
|
||||||
|
&& (!getDeclaredType().isInstance(val)
|
||||||
|
|| getDeclaredType() == Object.class))
|
||||||
|
return val;
|
||||||
|
|
||||||
// special case for queries: allow the given value to pass through
|
try {
|
||||||
// as-is if it is already in externalized form
|
// either invoke the static toExternal(val[, ctx]) method, or the
|
||||||
if (val != null && getType().isInstance(val)
|
// non-static val.toExternal([ctx]) method
|
||||||
&& (!getDeclaredType().isInstance(val)
|
if (Modifier.isStatic(externalizer.getModifiers())) {
|
||||||
|| getDeclaredType() == Object.class))
|
if (externalizer.getParameterTypes().length == 1)
|
||||||
return val;
|
return externalizer.invoke(null, new Object[]{ val });
|
||||||
|
return externalizer.invoke(null, new Object[]{ val, ctx });
|
||||||
try {
|
}
|
||||||
// either invoke the static toExternal(val[, ctx]) method, or the
|
if (val == null)
|
||||||
// non-static val.toExternal([ctx]) method
|
return null;
|
||||||
if (Modifier.isStatic(externalizer.getModifiers())) {
|
if (externalizer.getParameterTypes().length == 0)
|
||||||
if (externalizer.getParameterTypes().length == 1)
|
return externalizer.invoke(val, (Object[]) null);
|
||||||
return externalizer.invoke(null, new Object[]{ val });
|
return externalizer.invoke(val, new Object[]{ ctx });
|
||||||
return externalizer.invoke(null, new Object[]{ val, ctx });
|
} catch (OpenJPAException ke) {
|
||||||
|
throw ke;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MetaDataException(_loc.get("externalizer-err", this,
|
||||||
|
Exceptions.toString(val), e.toString())).setCause(e);
|
||||||
}
|
}
|
||||||
if (val == null)
|
|
||||||
return null;
|
|
||||||
if (externalizer.getParameterTypes().length == 0)
|
|
||||||
return externalizer.invoke(val, (Object[]) null);
|
|
||||||
return externalizer.invoke(val, new Object[]{ ctx });
|
|
||||||
} catch (OpenJPAException ke) {
|
|
||||||
throw ke;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new MetaDataException(_loc.get("externalizer-err", this,
|
|
||||||
Exceptions.toString(val), e.toString())).setCause(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Class converter = getConverter();
|
||||||
|
if (converter != null && val != null) {
|
||||||
|
try {
|
||||||
|
// TODO support CDI (OPENJPA-2714)
|
||||||
|
if (_converterConstructor == null) {
|
||||||
|
_converterConstructor = converter.getDeclaredConstructor();
|
||||||
|
}
|
||||||
|
Object instance = _converterConstructor.newInstance();
|
||||||
|
|
||||||
|
// see AttributeConverter.java from the JPA specs
|
||||||
|
if (_converterExtMethod == null) {
|
||||||
|
_converterExtMethod = converter.getDeclaredMethod("convertToDatabaseColumn", Object.class);
|
||||||
|
}
|
||||||
|
return _converterExtMethod.invoke(instance, val);
|
||||||
|
} catch (OpenJPAException ke) {
|
||||||
|
throw ke;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MetaDataException(_loc.get("converter-err", this,
|
||||||
|
Exceptions.toString(val), e.toString())).setCause(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1359,50 +1387,73 @@ public class FieldMetaData
|
|||||||
return fieldValues.get(val);
|
return fieldValues.get(val);
|
||||||
|
|
||||||
Member factory = getFactoryMethod();
|
Member factory = getFactoryMethod();
|
||||||
if (factory == null)
|
if (factory != null) {
|
||||||
return val;
|
try {
|
||||||
|
if (val == null && getNullValue() == NULL_DEFAULT)
|
||||||
|
return AccessController.doPrivileged(
|
||||||
|
J2DoPrivHelper.newInstanceAction(getDeclaredType()));
|
||||||
|
|
||||||
try {
|
// invoke either the constructor for the field type,
|
||||||
if (val == null && getNullValue() == NULL_DEFAULT)
|
// or the static type.toField(val[, ctx]) method
|
||||||
return AccessController.doPrivileged(
|
if (factory instanceof Constructor) {
|
||||||
J2DoPrivHelper.newInstanceAction(getDeclaredType()));
|
if (val == null)
|
||||||
|
return null;
|
||||||
|
return ((Constructor) factory).newInstance
|
||||||
|
(new Object[]{ val });
|
||||||
|
}
|
||||||
|
|
||||||
// invoke either the constructor for the field type,
|
Method meth = (Method) factory;
|
||||||
// or the static type.toField(val[, ctx]) method
|
if (meth.getParameterTypes().length == 1)
|
||||||
if (factory instanceof Constructor) {
|
return meth.invoke(null, new Object[]{ val });
|
||||||
if (val == null)
|
return meth.invoke(null, new Object[]{ val, ctx });
|
||||||
return null;
|
} catch (Exception e) {
|
||||||
return ((Constructor) factory).newInstance
|
// unwrap cause
|
||||||
(new Object[]{ val });
|
if (e instanceof InvocationTargetException) {
|
||||||
|
Throwable t = ((InvocationTargetException) e).
|
||||||
|
getTargetException();
|
||||||
|
if (t instanceof Error)
|
||||||
|
throw (Error) t;
|
||||||
|
e = (Exception) t;
|
||||||
|
|
||||||
|
// allow null values to cause NPEs and illegal arg exceptions
|
||||||
|
// without error
|
||||||
|
if (val == null && (e instanceof NullPointerException
|
||||||
|
|| e instanceof IllegalArgumentException))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof OpenJPAException)
|
||||||
|
throw (OpenJPAException) e;
|
||||||
|
if (e instanceof PrivilegedActionException)
|
||||||
|
e = ((PrivilegedActionException) e).getException();
|
||||||
|
throw new MetaDataException(_loc.get("factory-err", this,
|
||||||
|
Exceptions.toString(val), e.toString())).setCause(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Method meth = (Method) factory;
|
|
||||||
if (meth.getParameterTypes().length == 1)
|
|
||||||
return meth.invoke(null, new Object[]{ val });
|
|
||||||
return meth.invoke(null, new Object[]{ val, ctx });
|
|
||||||
} catch (Exception e) {
|
|
||||||
// unwrap cause
|
|
||||||
if (e instanceof InvocationTargetException) {
|
|
||||||
Throwable t = ((InvocationTargetException) e).
|
|
||||||
getTargetException();
|
|
||||||
if (t instanceof Error)
|
|
||||||
throw (Error) t;
|
|
||||||
e = (Exception) t;
|
|
||||||
|
|
||||||
// allow null values to cause NPEs and illegal arg exceptions
|
|
||||||
// without error
|
|
||||||
if (val == null && (e instanceof NullPointerException
|
|
||||||
|| e instanceof IllegalArgumentException))
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e instanceof OpenJPAException)
|
|
||||||
throw (OpenJPAException) e;
|
|
||||||
if (e instanceof PrivilegedActionException)
|
|
||||||
e = ((PrivilegedActionException) e).getException();
|
|
||||||
throw new MetaDataException(_loc.get("factory-err", this,
|
|
||||||
Exceptions.toString(val), e.toString())).setCause(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Class converter = getConverter();
|
||||||
|
if (converter != null && val != null) {
|
||||||
|
try {
|
||||||
|
// TODO support CDI (OPENJPA-2714)
|
||||||
|
if (_converterConstructor == null) {
|
||||||
|
_converterConstructor = converter.getDeclaredConstructor();
|
||||||
|
}
|
||||||
|
Object instance = _converterConstructor.newInstance();
|
||||||
|
|
||||||
|
// see AttributeConverter.java from the JPA specs
|
||||||
|
if (_converterFactMethod == null) {
|
||||||
|
_converterFactMethod = converter.getDeclaredMethod("convertToEntityAttribute", Object.class);
|
||||||
|
}
|
||||||
|
return _converterFactMethod.invoke(instance, val);
|
||||||
|
} catch (OpenJPAException ke) {
|
||||||
|
throw ke;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MetaDataException(_loc.get("converter-err", this,
|
||||||
|
Exceptions.toString(val), e.toString())).setCause(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1420,6 +1471,12 @@ public class FieldMetaData
|
|||||||
_extMethod = DEFAULT_METHOD;
|
_extMethod = DEFAULT_METHOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setConverter(Class converter) {
|
||||||
|
_converter = converter;
|
||||||
|
_converterExtMethod = null;
|
||||||
|
_converterFactMethod = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this field's factory, or null if none.
|
* The name of this field's factory, or null if none.
|
||||||
*/
|
*/
|
||||||
@ -2044,6 +2101,7 @@ public class FieldMetaData
|
|||||||
_access = field._access;
|
_access = field._access;
|
||||||
_orderDec = field._orderDec;
|
_orderDec = field._orderDec;
|
||||||
_useSchemaElement = field._useSchemaElement;
|
_useSchemaElement = field._useSchemaElement;
|
||||||
|
_converter = field._converter;
|
||||||
|
|
||||||
// embedded fields can't be versions
|
// embedded fields can't be versions
|
||||||
if (_owner.getEmbeddingMetaData() == null && _version == null)
|
if (_owner.getEmbeddingMetaData() == null && _version == null)
|
||||||
@ -2523,4 +2581,8 @@ public class FieldMetaData
|
|||||||
}
|
}
|
||||||
return setterName;
|
return setterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class getConverter() {
|
||||||
|
return _converter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,8 @@ bad-externalizer: The externalizer method "{1}" on field "{0}" is not valid. \
|
|||||||
"<owning-class>.<method-name>", and that the method is static.
|
"<owning-class>.<method-name>", and that the method is static.
|
||||||
externalizer-err: There was an error invoking the externalizer for field \
|
externalizer-err: There was an error invoking the externalizer for field \
|
||||||
"{0}" on Java value "{1}": {2}
|
"{0}" on Java value "{1}": {2}
|
||||||
|
converter-err: There was an error invoking the converter for field \
|
||||||
|
"{0}" on Java value "{1}": {2}
|
||||||
factory-err: There was an error invoking the factory for field \
|
factory-err: There was an error invoking the factory for field \
|
||||||
"{0}" on datastore value "{1}": {2}
|
"{0}" on datastore value "{1}": {2}
|
||||||
bad-factory: The factory method supplied for field "{0}" does not exist or \
|
bad-factory: The factory method supplied for field "{0}" does not exist or \
|
||||||
|
@ -133,6 +133,7 @@ public class TestExternalValues
|
|||||||
assertFalse(result.isEmpty());
|
assertFalse(result.isEmpty());
|
||||||
for (ExternalValues x:result) {
|
for (ExternalValues x:result) {
|
||||||
assertEquals(uuid, x.getUuid());
|
assertEquals(uuid, x.getUuid());
|
||||||
|
assertEquals(uuid, x.getUuid2());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +149,7 @@ public class TestExternalValues
|
|||||||
assertFalse(result.isEmpty());
|
assertFalse(result.isEmpty());
|
||||||
for (ExternalValues pc:result) {
|
for (ExternalValues pc:result) {
|
||||||
assertEquals(uuid, pc.getUuid());
|
assertEquals(uuid, pc.getUuid());
|
||||||
|
assertEquals(uuid, pc.getUuid2());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +191,7 @@ public class TestExternalValues
|
|||||||
em.getTransaction().begin();
|
em.getTransaction().begin();
|
||||||
ExternalValues pc = new ExternalValues();
|
ExternalValues pc = new ExternalValues();
|
||||||
pc.setUuid(uuid);
|
pc.setUuid(uuid);
|
||||||
|
pc.setUuid2(uuid);
|
||||||
em.persist(pc);
|
em.persist(pc);
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
em.clear();
|
em.clear();
|
||||||
|
@ -20,6 +20,7 @@ package org.apache.openjpa.persistence.meta.common.apps;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.persistence.Convert;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
|
||||||
@ -44,6 +45,9 @@ public class ExternalValues {
|
|||||||
@Factory("UUID.fromString")
|
@Factory("UUID.fromString")
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
|
|
||||||
|
@Convert(converter = UuidAttributeConverter.class)
|
||||||
|
private UUID uuid2;
|
||||||
|
|
||||||
public boolean getBooleanToShort() {
|
public boolean getBooleanToShort() {
|
||||||
return booleanToShort;
|
return booleanToShort;
|
||||||
}
|
}
|
||||||
@ -123,4 +127,13 @@ public class ExternalValues {
|
|||||||
public void setUuid(UUID uuid) {
|
public void setUuid(UUID uuid) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UUID getUuid2() {
|
||||||
|
return uuid2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid2(UUID uuid2) {
|
||||||
|
this.uuid2 = uuid2;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.meta.common.apps;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
@Converter
|
||||||
|
public class UuidAttributeConverter implements AttributeConverter<UUID, String>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(UUID attribute)
|
||||||
|
{
|
||||||
|
return attribute.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID convertToEntityAttribute(String dbData)
|
||||||
|
{
|
||||||
|
return UUID.fromString(dbData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -96,6 +96,7 @@ import javax.persistence.AccessType;
|
|||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
import javax.persistence.Cacheable;
|
import javax.persistence.Cacheable;
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Convert;
|
||||||
import javax.persistence.ElementCollection;
|
import javax.persistence.ElementCollection;
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
import javax.persistence.Embedded;
|
import javax.persistence.Embedded;
|
||||||
@ -174,6 +175,7 @@ import org.apache.openjpa.util.InternalException;
|
|||||||
import org.apache.openjpa.util.MetaDataException;
|
import org.apache.openjpa.util.MetaDataException;
|
||||||
import org.apache.openjpa.util.UnsupportedException;
|
import org.apache.openjpa.util.UnsupportedException;
|
||||||
import org.apache.openjpa.util.UserException;
|
import org.apache.openjpa.util.UserException;
|
||||||
|
import static org.apache.openjpa.persistence.MetaDataTag.CONVERT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,6 +233,7 @@ public class AnnotationPersistenceMetaDataParser
|
|||||||
_tags.put(ElementType.class, ELEM_TYPE);
|
_tags.put(ElementType.class, ELEM_TYPE);
|
||||||
_tags.put(ExternalValues.class, EXTERNAL_VALS);
|
_tags.put(ExternalValues.class, EXTERNAL_VALS);
|
||||||
_tags.put(Externalizer.class, EXTERNALIZER);
|
_tags.put(Externalizer.class, EXTERNALIZER);
|
||||||
|
_tags.put(Convert.class, CONVERT);
|
||||||
_tags.put(Factory.class, FACTORY);
|
_tags.put(Factory.class, FACTORY);
|
||||||
_tags.put(FetchGroup.class, FETCH_GROUP);
|
_tags.put(FetchGroup.class, FETCH_GROUP);
|
||||||
_tags.put(FetchGroups.class, FETCH_GROUPS);
|
_tags.put(FetchGroups.class, FETCH_GROUPS);
|
||||||
@ -1326,6 +1329,10 @@ public class AnnotationPersistenceMetaDataParser
|
|||||||
fmd.setTypeOverride(toOverrideType(((Type) anno).
|
fmd.setTypeOverride(toOverrideType(((Type) anno).
|
||||||
value()));
|
value()));
|
||||||
break;
|
break;
|
||||||
|
case CONVERT:
|
||||||
|
if (isMetaDataMode() && !((Convert) anno).disableConversion())
|
||||||
|
fmd.setConverter(((Convert) anno).converter());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedException(_loc.get("unsupported", fmd,
|
throw new UnsupportedException(_loc.get("unsupported", fmd,
|
||||||
anno.toString()));
|
anno.toString()));
|
||||||
|
@ -91,5 +91,6 @@ public enum MetaDataTag {
|
|||||||
OPENJPA_VERSION,
|
OPENJPA_VERSION,
|
||||||
// JPA 2.1
|
// JPA 2.1
|
||||||
STOREDPROCEDURE_QUERIES,
|
STOREDPROCEDURE_QUERIES,
|
||||||
STOREDPROCEDURE_QUERY
|
STOREDPROCEDURE_QUERY,
|
||||||
|
CONVERT
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user