org.datanucleus.store.cassandra.fieldmanager.FetchFieldManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-cassandra Show documentation
Show all versions of datanucleus-cassandra Show documentation
Plugin providing persistence to Cassandra datastores.
/**********************************************************************
Copyright (c) 2014 Andy Jefferson and others. All rights reserved.
Licensed 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.
Contributors:
...
**********************************************************************/
package org.datanucleus.store.cassandra.fieldmanager;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusObjectNotFoundException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.identity.IdentityUtils;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.JdbcType;
import org.datanucleus.metadata.MetaDataUtils;
import org.datanucleus.metadata.RelationType;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.cassandra.CassandraUtils;
import org.datanucleus.store.fieldmanager.AbstractFetchFieldManager;
import org.datanucleus.store.fieldmanager.FieldManager;
import org.datanucleus.store.query.QueryUtils;
import org.datanucleus.store.schema.table.Column;
import org.datanucleus.store.schema.table.MemberColumnMapping;
import org.datanucleus.store.schema.table.Table;
import org.datanucleus.store.types.SCOUtils;
import org.datanucleus.store.types.converters.MultiColumnConverter;
import org.datanucleus.store.types.converters.TypeConverter;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.TypeConversionHelper;
import com.datastax.driver.core.Row;
/**
* FieldManager to use for retrieving values from Cassandra to put into a persistable object.
*/
public class FetchFieldManager extends AbstractFetchFieldManager
{
protected Table table;
protected Row row;
public FetchFieldManager(ObjectProvider op, Row row, Table table)
{
super(op);
this.table = table;
this.row = row;
}
public FetchFieldManager(ExecutionContext ec, Row row, AbstractClassMetaData cmd, Table table)
{
super(ec, cmd);
this.table = table;
this.row = row;
}
protected MemberColumnMapping getColumnMapping(int fieldNumber)
{
return table.getMemberColumnMappingForMember(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber));
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchBooleanField(int)
*/
@Override
public boolean fetchBooleanField(int fieldNumber)
{
return row.getBool(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchCharField(int)
*/
@Override
public char fetchCharField(int fieldNumber)
{
return row.getString(getColumnMapping(fieldNumber).getColumn(0).getName()).charAt(0);
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchByteField(int)
*/
@Override
public byte fetchByteField(int fieldNumber)
{
return (byte) row.getInt(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchShortField(int)
*/
@Override
public short fetchShortField(int fieldNumber)
{
return (short) row.getInt(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchIntField(int)
*/
@Override
public int fetchIntField(int fieldNumber)
{
return row.getInt(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchLongField(int)
*/
@Override
public long fetchLongField(int fieldNumber)
{
return row.getLong(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchFloatField(int)
*/
@Override
public float fetchFloatField(int fieldNumber)
{
Column col = getColumnMapping(fieldNumber).getColumn(0);
if (col.getJdbcType() == JdbcType.DECIMAL)
{
return row.getDecimal(col.getName()).floatValue();
}
else if (col.getJdbcType() == JdbcType.DOUBLE)
{
return (float) row.getDouble(col.getName());
}
return row.getFloat(col.getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchDoubleField(int)
*/
@Override
public double fetchDoubleField(int fieldNumber)
{
Column col = getColumnMapping(fieldNumber).getColumn(0);
if (col.getJdbcType() == JdbcType.DECIMAL)
{
return row.getDecimal(col.getName()).doubleValue();
}
return row.getDouble(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchStringField(int)
*/
@Override
public String fetchStringField(int fieldNumber)
{
return row.getString(getColumnMapping(fieldNumber).getColumn(0).getName());
}
/*
* (non-Javadoc)
* @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchObjectField(int)
*/
@Override
public Object fetchObjectField(int fieldNumber)
{
ClassLoaderResolver clr = ec.getClassLoaderResolver();
AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
RelationType relationType = mmd.getRelationType(clr);
if (relationType != RelationType.NONE && MetaDataUtils.getInstance().isMemberEmbedded(ec.getMetaDataManager(), clr, mmd, relationType, null))
{
// Embedded field
if (RelationType.isRelationSingleValued(relationType))
{
// TODO Null detection
List embMmds = new ArrayList<>();
embMmds.add(mmd);
AbstractClassMetaData embCmd = ec.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
ObjectProvider embOP = ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, embCmd, op, fieldNumber);
FieldManager fetchEmbFM = new FetchEmbeddedFieldManager(embOP, row, embMmds, table);
embOP.replaceFields(embCmd.getAllMemberPositions(), fetchEmbFM);
return embOP.getObject();
}
else if (RelationType.isRelationMultiValued(relationType))
{
// TODO Embedded Collection
NucleusLogger.PERSISTENCE.debug("Field=" + mmd.getFullFieldName() + " not currently supported (embedded)");
return null; // Remove this when we support embedded
}
}
return fetchNonEmbeddedObjectField(mmd, relationType, clr);
}
protected Object fetchNonEmbeddedObjectField(AbstractMemberMetaData mmd, RelationType relationType, ClassLoaderResolver clr)
{
int fieldNumber = mmd.getAbsoluteFieldNumber();
MemberColumnMapping mapping = getColumnMapping(fieldNumber);
boolean optional = false;
if (Optional.class.isAssignableFrom(mmd.getType()))
{
if (relationType != RelationType.NONE)
{
relationType = RelationType.ONE_TO_ONE_UNI;
}
optional = true;
}
if (RelationType.isRelationSingleValued(relationType))
{
if (row.isNull(mapping.getColumn(0).getName()))
{
return optional ? Optional.empty() : null;
}
if (mmd.isSerialized())
{
// Convert back from ByteBuffer
TypeConverter serialConv = ec.getTypeManager().getTypeConverterForType(Serializable.class, ByteBuffer.class);
ByteBuffer datastoreBuffer = row.getBytes(mapping.getColumn(0).getName());
Object value = serialConv.toMemberType(datastoreBuffer);
// Make sure it has an ObjectProvider
ObjectProvider pcOP = ec.findObjectProvider(value);
if (pcOP == null || ec.getApiAdapter().getExecutionContext(value) == null)
{
ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, value, false, op, fieldNumber);
}
return value;
}
// TODO Support 1-1 storage using "FK" style column(s) for related object
Object value = row.getString(mapping.getColumn(0).getName());
Object memberValue = getValueForSingleRelationField(mmd, value, clr);
return optional ? Optional.of(memberValue) : memberValue;
}
else if (RelationType.isRelationMultiValued(relationType))
{
Object value = null;
if (mmd.hasCollection())
{
Class elementCls = mmd.getCollection().isSerializedElement() ? ByteBuffer.class : String.class;
if (Set.class.isAssignableFrom(mmd.getType()))
{
value = row.getSet(mapping.getColumn(0).getName(), elementCls);
}
else if (List.class.isAssignableFrom(mmd.getType()) || mmd.getOrderMetaData() != null)
{
value = row.getList(mapping.getColumn(0).getName(), elementCls);
}
else
{
value = row.getSet(mapping.getColumn(0).getName(), elementCls);
}
}
else if (mmd.hasMap())
{
Class keyCls = clr.classForName(mmd.getMap().getKeyType());
if (mmd.getMap().keyIsPersistent())
{
keyCls = mmd.getMap().isSerializedKey() ? ByteBuffer.class : String.class;
}
Class valCls = clr.classForName(mmd.getMap().getValueType());
if (mmd.getMap().valueIsPersistent())
{
valCls = mmd.getMap().isSerializedValue() ? ByteBuffer.class : String.class;
}
value = row.getMap(mapping.getColumn(0).getName(), keyCls, valCls);
}
else if (mmd.hasArray())
{
value = row.getList(mapping.getColumn(0).getName(), String.class);
}
return getValueForContainerRelationField(mmd, value, clr);
}
else
{
if (mapping.getTypeConverter() != null && !mmd.isSerialized())
{
// Convert any columns that have a converter defined back to the member type with the converter
if (mapping.getNumberOfColumns() > 1)
{
// Check for null member
boolean allNull = true;
for (int i = 0; i < mapping.getNumberOfColumns(); i++)
{
if (!row.isNull(mapping.getColumn(i).getName()))
{
allNull = false;
}
}
if (allNull)
{
return optional ? Optional.empty() : null;
}
Object valuesArr = null;
Class[] colTypes = ((MultiColumnConverter) mapping.getTypeConverter()).getDatastoreColumnTypes();
if (colTypes[0] == int.class)
{
valuesArr = new int[mapping.getNumberOfColumns()];
}
else if (colTypes[0] == long.class)
{
valuesArr = new long[mapping.getNumberOfColumns()];
}
else if (colTypes[0] == double.class)
{
valuesArr = new double[mapping.getNumberOfColumns()];
}
else if (colTypes[0] == float.class)
{
valuesArr = new double[mapping.getNumberOfColumns()];
}
else if (colTypes[0] == String.class)
{
valuesArr = new String[mapping.getNumberOfColumns()];
}
else
{
valuesArr = new Object[mapping.getNumberOfColumns()];
}
boolean isNull = true;
for (int i = 0; i < mapping.getNumberOfColumns(); i++)
{
Column col = mapping.getColumn(i);
if (col.getTypeName().equals("int"))
{
Array.set(valuesArr, i, row.getInt(col.getName()));
}
else if (col.getTypeName().equals("bool"))
{
Array.set(valuesArr, i, row.getBool(col.getName()));
}
else if (col.getTypeName().equals("timestamp"))
{
Array.set(valuesArr, i, row.getTimestamp(col.getName()));
}
else if (col.getTypeName().equals("decimal"))
{
Array.set(valuesArr, i, row.getDecimal(col.getName()));
}
else if (col.getTypeName().equals("double"))
{
Array.set(valuesArr, i, row.getDouble(col.getName()));
}
else if (col.getTypeName().equals("float"))
{
Array.set(valuesArr, i, row.getFloat(col.getName()));
}
else if (col.getTypeName().equals("bigint"))
{
Array.set(valuesArr, i, row.getLong(col.getName()));
}
else
{
Array.set(valuesArr, i, row.getString(col.getName()));
}
// TODO Support other types
if (isNull && Array.get(valuesArr, i) != null)
{
isNull = false;
}
}
if (isNull)
{
return null;
}
return mapping.getTypeConverter().toMemberType(valuesArr);
}
if (row.isNull(mapping.getColumn(0).getName()))
{
return optional ? Optional.empty() : null;
}
// Obtain value using converter
Object returnValue = CassandraUtils.getMemberValueForColumnWithConverter(row, mapping.getColumn(0), mapping.getTypeConverter());
if (op != null)
{
returnValue = SCOUtils.wrapSCOField(op, mmd.getAbsoluteFieldNumber(), returnValue, true);
}
return returnValue;
}
if (!optional && mmd.hasCollection())
{
if (mmd.isSerialized())
{
// Collection field was serialised, so convert back from ByteBuffer
TypeConverter serialConv = ec.getTypeManager().getTypeConverterForType(Serializable.class, ByteBuffer.class);
ByteBuffer datastoreBuffer = row.getBytes(mapping.getColumn(0).getName());
if (datastoreBuffer == null)
{
return null;
}
Object returnValue = serialConv.toMemberType(datastoreBuffer);
returnValue = (op!=null) ? (Collection) SCOUtils.wrapSCOField(op, mmd.getAbsoluteFieldNumber(), returnValue, true) : returnValue;
}
Collection