mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 14:32:04 +00:00
#827 - ENH: Add support for AttributeConverter from JPA 2.1
This commit is contained in:
@@ -22,6 +22,7 @@ import org.avaje.classpath.scanner.ClassFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
@@ -47,12 +48,14 @@ public class BootupClasses implements ClassFilter {
|
||||
|
||||
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends AttributeConverter<?, ?>>> attributeConverterList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends CompoundType<?>>> compoundTypeList = new ArrayList<>();
|
||||
|
||||
|
||||
// The following objects are instantiiated on first request
|
||||
// The following objects are instantiated on first request
|
||||
// there is always a candidate list, that holds the class and an
|
||||
// instance list, that holds the instance. Once a class is instantiiated
|
||||
// instance list, that holds the instance. Once a class is instantiated
|
||||
// (or added) it will get removed from the candidate list
|
||||
private final List<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<>();
|
||||
|
||||
@@ -337,6 +340,13 @@ public class BootupClasses implements ClassFilter {
|
||||
return scalarConverterList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of AttributeConverters found.
|
||||
*/
|
||||
public List<Class<? extends AttributeConverter<?, ?>>> getAttributeConverters() {
|
||||
return attributeConverterList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of ScalarConverters found.
|
||||
*/
|
||||
@@ -387,6 +397,11 @@ public class BootupClasses implements ClassFilter {
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (AttributeConverter.class.isAssignableFrom(cls)) {
|
||||
attributeConverterList.add((Class<? extends AttributeConverter<?, ?>>) cls);
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (CompoundType.class.isAssignableFrom(cls)) {
|
||||
compoundTypeList.add((Class<? extends CompoundType<?>>) cls);
|
||||
interesting = true;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Adapter from ScalarTypeConverter to AttributeConverter.
|
||||
*/
|
||||
class AttributeConverterAdapter<B,S> implements ScalarTypeConverter<B, S> {
|
||||
|
||||
private final AttributeConverter<B,S> converter;
|
||||
|
||||
AttributeConverterAdapter(AttributeConverter<B, S> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B getNullValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B wrapValue(S dbValue) {
|
||||
return converter.convertToEntityAttribute(dbValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public S unwrapValue(B beanType) {
|
||||
return converter.convertToDatabaseColumn(beanType);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.joda.time.LocalTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -223,6 +224,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
if (bootupClasses != null) {
|
||||
initialiseCustomScalarTypes(jsonDateTime, bootupClasses);
|
||||
initialiseScalarConverters(bootupClasses);
|
||||
initialiseAttributeConverters(bootupClasses);
|
||||
initialiseCompoundTypes(bootupClasses);
|
||||
}
|
||||
}
|
||||
@@ -813,17 +815,46 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
ScalarTypeConverter converter = (ScalarTypeConverter) cls.newInstance();
|
||||
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, converter);
|
||||
|
||||
logger.debug("Register ScalarTypeWrapper from " + logicalType + " -> " + persistType + " using:" + cls);
|
||||
|
||||
add(stw);
|
||||
|
||||
} catch (Exception e) {
|
||||
String msg = "Error loading ScalarType [" + cls.getName() + "]";
|
||||
logger.error(msg, e);
|
||||
logger.error("Error registering ScalarTypeConverter [" + cls.getName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void initialiseAttributeConverters(BootupClasses bootupClasses) {
|
||||
|
||||
List<Class<? extends AttributeConverter<?, ?>>> foundTypes = bootupClasses.getAttributeConverters();
|
||||
|
||||
for (Class<? extends AttributeConverter<?, ?>> foundType : foundTypes) {
|
||||
Class<?> cls = foundType;
|
||||
try {
|
||||
|
||||
Class<?>[] paramTypes = TypeReflectHelper.getParams(cls, AttributeConverter.class);
|
||||
if (paramTypes.length != 2) {
|
||||
throw new IllegalStateException("Expected 2 generics paramtypes but got: " + Arrays.toString(paramTypes));
|
||||
}
|
||||
|
||||
Class<?> logicalType = paramTypes[0];
|
||||
Class<?> persistType = paramTypes[1];
|
||||
|
||||
ScalarType<?> wrappedType = getScalarType(persistType);
|
||||
if (wrappedType == null) {
|
||||
throw new IllegalStateException("Could not find ScalarType for: " + paramTypes[1]);
|
||||
}
|
||||
|
||||
AttributeConverter converter = (AttributeConverter) cls.newInstance();
|
||||
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, new AttributeConverterAdapter(converter));
|
||||
logger.debug("Register ScalarTypeWrapper from " + logicalType + " -> " + persistType + " using:" + cls);
|
||||
add(stw);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error registering AttributeConverter [" + cls.getName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseCompoundTypes(BootupClasses bootupClasses) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class ESomeConvertType {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
Rate rate;
|
||||
|
||||
public ESomeConvertType(String name, Rate rate) {
|
||||
this.name = name;
|
||||
this.rate = rate;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Rate getRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
public void setRate(Rate rate) {
|
||||
this.rate = rate;
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,23 @@ package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.tests.model.ivo.Rate;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Converts between Rate and BigDecimal.
|
||||
*/
|
||||
public class RateTypeConverter {//implements ScalarTypeConverter<Rate,BigDecimal>{
|
||||
@Converter
|
||||
public class RateTypeConverter implements AttributeConverter<Rate,BigDecimal> {
|
||||
|
||||
public BigDecimal unwrapValue(Rate beanType) {
|
||||
return beanType.getValue();
|
||||
@Override
|
||||
public BigDecimal convertToDatabaseColumn(Rate attribute) {
|
||||
return attribute.getValue();
|
||||
}
|
||||
|
||||
public Rate wrapValue(BigDecimal scalarType) {
|
||||
return new Rate(scalarType);
|
||||
@Override
|
||||
public Rate convertToEntityAttribute(BigDecimal dbData) {
|
||||
return new Rate(dbData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.tests.model.ivo.test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.ivo.ESomeConvertType;
|
||||
import com.avaje.tests.model.ivo.Rate;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestAttributeConverter extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void insertUpdateDelete() {
|
||||
|
||||
ESomeConvertType bean = new ESomeConvertType("one", new Rate(20.1));
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
ESomeConvertType found = Ebean.find(ESomeConvertType.class, bean.getId());
|
||||
found.setRate(new Rate("40"));
|
||||
Ebean.save(found);
|
||||
|
||||
Ebean.delete(found);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user