#650 - Refactor: Optimise L2 cache entry for serialisation

This commit is contained in:
Robin Bygrave
2016-04-14 11:56:31 +12:00
parent 444b848c22
commit 1b8b05790e
16 changed files with 251 additions and 17 deletions
@@ -131,4 +131,10 @@ public class CachedBeanData implements Externalizable {
return data.get(propertyName);
}
/**
* Return all the property data.
*/
public Map<String,Object> getData() {
return data;
}
}
@@ -634,11 +634,13 @@ final class BeanDescriptorCacheHelp<T> {
for (int i = 0; i < dirtyProperties.length; i++) {
if (dirtyProperties[i]) {
BeanProperty property = desc.propertiesIndex[i];
Object val = property.getCacheDataValue(bean);
changes.put(property.getName(), val);
if (property.isNaturalKey()) {
updateNaturalKey = true;
changeSet.addNaturalKeyPut(desc, id, val);
if (property.isCacheDataInclude()) {
Object val = property.getCacheDataValue(bean);
changes.put(property.getName(), val);
if (property.isNaturalKey()) {
updateNaturalKey = true;
changeSet.addNaturalKeyPut(desc, id, val);
}
}
}
}
@@ -733,11 +733,42 @@ public class BeanProperty implements ElPropertyValue, Property {
return prefix + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + "] threw error";
}
public Object getCacheDataValue(EntityBean bean) {
return getValue(bean);
/**
* Return true if this property should be included in the cache bean data.
*/
public boolean isCacheDataInclude() {
return true;
}
/**
* Return the value for this property which we hold in the L2 cache entry.
* <p>
* This uses format() where possible to store the value as a string and this
* is done to make any resulting Java object serialisation content smaller as
* strings get special treatment.
* </p>
*/
public Object getCacheDataValue(EntityBean bean) {
Object value = getValue(bean);
if (value == null || scalarType.isBinaryType()) {
return value;
} else {
// convert to string as an optimisation for java object serialisation
return scalarType.format(value);
}
}
/**
* Read the value for this property from L2 cache entry and set it to the bean.
* <p>
* This uses parse() as per the comment in getCacheDataValue().
* </p>
*/
public void setCacheDataValue(EntityBean bean, Object cacheData) {
if (cacheData instanceof String) {
// parse back from string to support optimisation of java object serialisation
cacheData = scalarType.parse((String)cacheData);
}
setValue(bean, cacheData);
}
@@ -326,6 +326,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return server.findIds(q, t);
}
/**
* Exclude many properties from bean cache data.
*/
@Override
public boolean isCacheDataInclude() {
// this would change for DB Array type support
return false;
}
/**
* Add the loaded current bean to its associated parent.
*/
@@ -385,6 +385,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return getPropertyType();
}
@Override
public Object getCacheDataValue(EntityBean bean) {
Object ap = getValue(bean);
if (ap == null) {
@@ -392,22 +393,23 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
if (embedded) {
return targetDescriptor.cacheEmbeddedBeanExtract((EntityBean) ap);
} else {
return targetDescriptor.getId((EntityBean) ap);
return targetDescriptor.getIdProperty().getCacheDataValue((EntityBean) ap);
}
}
@Override
public void setCacheDataValue(EntityBean bean, Object cacheData) {
if (cacheData != null) {
if (cacheData == null) {
setValue(bean, null);
} else {
if (embedded) {
EntityBean embeddedBean = targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData);
setValue(bean, embeddedBean);
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData));
} else {
T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData);
setValue(bean, ref);
if (cacheData instanceof String) {
cacheData = targetDescriptor.getIdProperty().scalarType.parse((String)cacheData);
}
setValue(bean, targetDescriptor.createReference(Boolean.FALSE, cacheData));
}
}
}
@@ -35,6 +35,12 @@ import java.sql.SQLException;
*/
public interface ScalarType<T> extends StringParser, StringFormatter, ScalarDataReader<T> {
/**
* Return true if this is a binary type and can not support parse() and format() from/to string.
* This allows Ebean to optimise marshalling types to string.
*/
boolean isBinaryType();
/**
* Return true if this is a mutable scalar type (like hstore).
*/
@@ -22,6 +22,12 @@ public abstract class ScalarTypeBase<T> implements ScalarType<T> {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
// override for binary/byte based types
return false;
}
/**
* Default implementation of mutable false.
*/
@@ -23,6 +23,11 @@ public class ScalarTypeByte extends ScalarTypeBase<Byte> {
super(Byte.class, true, Types.TINYINT);
}
@Override
public boolean isBinaryType() {
return true;
}
public void bind(DataBind b, Byte value) throws SQLException {
if (value == null) {
b.setNull(Types.TINYINT);
@@ -20,6 +20,11 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase<byte[]> {
super(byte[].class, jdbcNative, jdbcType);
}
@Override
public boolean isBinaryType() {
return true;
}
public byte[] convertToBytes(Object value) {
return (byte[]) value;
}
@@ -29,6 +29,11 @@ public class ScalarTypeBytesEncrypted implements ScalarType<byte[]> {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return true;
}
@Override
public boolean isMutable() {
return false;
@@ -28,6 +28,11 @@ public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return wrapped.isBinaryType();
}
@Override
public boolean isMutable() {
return wrapped.isMutable();
@@ -29,6 +29,11 @@ public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase i
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return false;
}
/**
* Return the IN values for DB constraint construction.
*/
@@ -54,6 +54,11 @@ public class ScalarTypeFile extends ScalarTypeBase<File> {
this.bufferSize = bufferSize;
}
@Override
public boolean isBinaryType() {
return true;
}
private InputStream getInputStream(File value) throws IOException {
FileInputStream fi = new FileInputStream(value);
return new BufferedInputStream(fi, bufferSize);
@@ -47,6 +47,11 @@ public class ScalarTypeWrapper<B, S> implements ScalarType<B> {
return scalarType.asVersion(unwrapValue);
}
@Override
public boolean isBinaryType() {
return scalarType.isBinaryType();
}
@Override
public boolean isMutable() {
return scalarType.isMutable();