Package org.springframework.data.cassandra.convert

Source Code of org.springframework.data.cassandra.convert.MappingCassandraConverter

/*
* Copyright 2013-2014 the original author or authors
*
* 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.
*/
package org.springframework.data.cassandra.convert;

import java.io.Serializable;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.data.cassandra.repository.MapIdentifiable;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import com.datastax.driver.core.Row;
import com.datastax.driver.core.querybuilder.Delete.Where;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;

import static org.springframework.data.cassandra.repository.support.BasicMapId.id;

/**
* {@link CassandraConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to
* {@link Row}.
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Oliver Gierke
*/
public class MappingCassandraConverter extends AbstractCassandraConverter implements CassandraConverter,
    ApplicationContextAware, BeanClassLoaderAware {

  protected final Logger log = LoggerFactory.getLogger(getClass());

  protected final CassandraMappingContext mappingContext;
  protected ApplicationContext applicationContext;
  protected SpELContext spELContext;

  protected ClassLoader beanClassLoader;

  /**
   * Creates a new {@link MappingCassandraConverter} with a {@link BasicCassandraMappingContext}.
   */
  public MappingCassandraConverter() {
    this(new BasicCassandraMappingContext());
  }

  /**
   * Creates a new {@link MappingCassandraConverter} with the given {@link CassandraMappingContext}.
   *
   * @param mappingContext must not be {@literal null}.
   */
  public MappingCassandraConverter(CassandraMappingContext mappingContext) {

    super(new DefaultConversionService());

    Assert.notNull(mappingContext);

    this.mappingContext = mappingContext;
    this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
  }

  @SuppressWarnings("unchecked")
  public <R> R readRow(Class<R> clazz, Row row) {

    Class<R> beanClassLoaderClass = transformClassToBeanClassLoaderClass(clazz);

    TypeInformation<? extends R> type = ClassTypeInformation.from(beanClassLoaderClass);
    TypeInformation<? extends R> typeToUse = type;
    Class<? extends R> rawType = typeToUse.getType();

    if (Row.class.isAssignableFrom(rawType)) {
      return (R) row;
    }

    CassandraPersistentEntity<R> persistentEntity = (CassandraPersistentEntity<R>) mappingContext
        .getPersistentEntity(typeToUse);
    if (persistentEntity == null) {
      throw new MappingException("No mapping metadata found for " + rawType.getName());
    }

    return readEntityFromRow(persistentEntity, row);
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
    this.spELContext = new SpELContext(this.spELContext, applicationContext);
  }

  protected <S> S readEntityFromRow(final CassandraPersistentEntity<S> entity, final Row row) {

    DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(row, spELContext);

    BasicCassandraRowValueProvider rowValueProvider = new BasicCassandraRowValueProvider(row, evaluator);

    CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider(
        entity, rowValueProvider, null);

    EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
    S instance = instantiator.createInstance(entity, parameterProvider);

    BeanWrapper<S> wrapper = BeanWrapper.create(instance, conversionService);

    readPropertiesFromRow(entity, rowValueProvider, wrapper);

    return wrapper.getBean();
  }

  protected void readPropertiesFromRow(final CassandraPersistentEntity<?> entity,
      final BasicCassandraRowValueProvider row, final BeanWrapper<?> wrapper) {

    entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {

      @Override
      public void doWithPersistentProperty(CassandraPersistentProperty prop) {

        MappingCassandraConverter.this.readPropertyFromRow(entity, prop, row, wrapper);
      }
    });
  }

  protected void readPropertyFromRow(final CassandraPersistentEntity<?> entity, final CassandraPersistentProperty prop,
      final BasicCassandraRowValueProvider row, final BeanWrapper<?> wrapper) {

    if (entity.isConstructorArgument(prop)) { // skip 'cause prop was set in ctor
      return;
    }

    if (prop.isCompositePrimaryKey()) {

      // get the key
      CassandraPersistentProperty keyProperty = entity.getIdProperty();
      Object key = wrapper.getProperty(keyProperty);
      if (key == null) {
        key = instantiatePrimaryKey(keyProperty.getCompositePrimaryKeyEntity(), keyProperty, row);
      }

      // wrap the key
      BeanWrapper<Object> keyWrapper = BeanWrapper.create(key, conversionService);

      // now recurse on using the key this time
      readPropertiesFromRow(prop.getCompositePrimaryKeyEntity(), row, keyWrapper);

      // now that the key's properties have been populated, set the key property on the entity
      wrapper.setProperty(keyProperty, keyWrapper.getBean());
      return;
    }

    if (!row.getRow().getColumnDefinitions().contains(prop.getColumnName().toCql())) {
      return;
    }

    Object obj = row.getPropertyValue(prop);
    wrapper.setProperty(prop, obj);
  }

  protected Object instantiatePrimaryKey(CassandraPersistentEntity<?> entity, CassandraPersistentProperty keyProperty,
      BasicCassandraRowValueProvider propertyProvider) {

    EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);

    return instantiator.createInstance(entity, new CassandraPersistentEntityParameterValueProvider(entity,
        propertyProvider, null));
  }

  @Override
  public <R> R read(Class<R> type, Object row) {
    if (row instanceof Row) {
      return readRow(type, (Row) row);
    }
    throw new MappingException("Unknown row object " + row.getClass().getName());
  }

  @Override
  public void write(Object source, Object sink) {

    if (source == null) {
      return;
    }

    Class<?> beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass());
    CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(beanClassLoaderClass);

    if (entity == null) {
      throw new MappingException("No mapping metadata found for " + source.getClass());
    }

    if (sink instanceof Insert) {
      writeInsertFromObject(source, (Insert) sink, entity);
    } else if (sink instanceof Update) {
      writeUpdateFromObject(source, (Update) sink, entity);
    } else if (sink instanceof Where) {
      writeDeleteWhereFromObject(source, (Where) sink, entity);
    } else {
      throw new MappingException("Unknown buildStatement " + sink.getClass().getName());
    }
  }

  protected void writeInsertFromObject(final Object object, final Insert insert, CassandraPersistentEntity<?> entity) {
    writeInsertFromWrapper(BeanWrapper.create(object, conversionService), insert, entity);
  }

  protected void writeInsertFromWrapper(final BeanWrapper<Object> wrapper, final Insert insert,
      CassandraPersistentEntity<?> entity) {

    entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {

      @Override
      public void doWithPersistentProperty(CassandraPersistentProperty prop) {

        Object value = wrapper.getProperty(prop, prop.getType());

        log.debug("prop.type -> " + prop.getType().getName());
        log.debug("prop.value -> " + value);

        if (prop.isCompositePrimaryKey()) {
          log.debug("prop is a compositeKey");
          writeInsertFromWrapper(BeanWrapper.create(value, conversionService), insert,
              prop.getCompositePrimaryKeyEntity());
          return;
        }

        if (value != null) {
          log.debug(String.format("Adding insert.value [%s] - [%s]", prop.getColumnName().toCql(), value));
          insert.value(prop.getColumnName().toCql(), value);
        }
      }
    });
  }

  protected void writeUpdateFromObject(final Object object, final Update update, CassandraPersistentEntity<?> entity) {
    writeUpdateFromWrapper(BeanWrapper.create(object, conversionService), update, entity);
  }

  protected void writeUpdateFromWrapper(final BeanWrapper<Object> wrapper, final Update update,
      final CassandraPersistentEntity<?> entity) {

    entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {

      @Override
      public void doWithPersistentProperty(CassandraPersistentProperty prop) {

        Object value = wrapper.getProperty(prop, prop.getType());

        if (prop.isCompositePrimaryKey()) {
          writeUpdateFromWrapper(BeanWrapper.create(value, conversionService), update,
              prop.getCompositePrimaryKeyEntity());
          return;
        }

        if (value != null) {
          if (prop.isIdProperty() || entity.isCompositePrimaryKey() || prop.isPrimaryKeyColumn()) {
            update.where(QueryBuilder.eq(prop.getColumnName().toCql(), value));
          } else {
            update.with(QueryBuilder.set(prop.getColumnName().toCql(), value));
          }
        }
      }
    });
  }

  protected void writeDeleteWhereFromObject(final Object object, final Where where, CassandraPersistentEntity<?> entity) {
    writeDeleteWhereFromWrapper(BeanWrapper.create(object, conversionService), where, entity);
  }

  protected void writeDeleteWhereFromWrapper(final BeanWrapper<Object> wrapper, final Where where,
      CassandraPersistentEntity<?> entity) {

    // if the entity itself if a composite primary key, then we've recursed, so just add columns & return
    if (entity.isCompositePrimaryKey()) {
      entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
        @Override
        public void doWithPersistentProperty(CassandraPersistentProperty p) {
          where.and(QueryBuilder.eq(p.getColumnName().toCql(), wrapper.getProperty(p)));
        }
      });
      return;
    }

    // else, wrapper is an entity with an id
    Object id = getId(wrapper, entity);
    if (id == null) {
      String msg = String.format("no id value found in object {}", wrapper.getBean());
      log.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (id instanceof MapId) {

      for (Map.Entry<String, Serializable> entry : ((MapId) id).entrySet()) {
        where.and(QueryBuilder.eq(entry.getKey(), entry.getValue()));
      }
      return;
    }

    CassandraPersistentProperty idProperty = entity.getIdProperty();
    if (idProperty != null) {

      if (idProperty.isCompositePrimaryKey()) {
        writeDeleteWhereFromWrapper(BeanWrapper.create(id, conversionService), where,
            idProperty.getCompositePrimaryKeyEntity());
        return;
      }

      where.and(QueryBuilder.eq(idProperty.getColumnName().toCql(), id));
      return;
    }
  }

  @Override
  public Object getId(Object object, CassandraPersistentEntity<?> entity) {

    Assert.notNull(object);

    final BeanWrapper<?> wrapper = object instanceof BeanWrapper ? (BeanWrapper<?>) object : BeanWrapper.create(object,
        conversionService);
    object = wrapper.getBean();

    if (!entity.getType().isAssignableFrom(object.getClass())) {
      throw new IllegalArgumentException(String.format(
          "given instance of type [%s] is not of compatible expected type [%s]", object.getClass().getName(), entity
              .getType().getName()));
    }

    if (object instanceof MapIdentifiable) {
      return ((MapIdentifiable) object).getMapId();
    }

    CassandraPersistentProperty idProperty = entity.getIdProperty();
    if (idProperty != null) {
      return wrapper.getProperty(entity.getIdProperty(), idProperty.getType());
    }

    // if the class doesn't have an id property, then it's using MapId
    final MapId id = id();
    entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {

      @Override
      public void doWithPersistentProperty(CassandraPersistentProperty p) {
        if (p.isPrimaryKeyColumn()) {
          id.with(p.getName(), (Serializable) wrapper.getProperty(p, p.getType()));
        }
      }
    });

    return id;
  }

  @SuppressWarnings("unchecked")
  protected <T> Class<T> transformClassToBeanClassLoaderClass(Class<T> entity) {
    try {
      return (Class<T>) ClassUtils.forName(entity.getName(), beanClassLoader);
    } catch (ClassNotFoundException e) {
      return entity;
    } catch (LinkageError e) {
      return entity;
    }
  }

  @Override
  public void setBeanClassLoader(ClassLoader classLoader) {
    this.beanClassLoader = classLoader;

  }

  @Override
  public CassandraMappingContext getMappingContext() {
    return mappingContext;
  }
}
TOP

Related Classes of org.springframework.data.cassandra.convert.MappingCassandraConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.