com.datastax.driver.mapping.MappingSession Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-driver-mapping Show documentation
Show all versions of cassandra-driver-mapping Show documentation
Entity Mapping Addon JPA 2.1 compatible for DataStax Java Driver 3.0+ for Cassandra.
/*
* Copyright (C) 2014 Eugene Valchkou.
*
* 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 com.datastax.driver.mapping;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static com.datastax.driver.core.querybuilder.QueryBuilder.ttl;
import static com.datastax.driver.core.querybuilder.QueryBuilder.timestamp;
import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Batch;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.driver.mapping.option.BatchOptions;
import com.datastax.driver.mapping.option.ReadOptions;
import com.datastax.driver.mapping.option.WriteOptions;
import com.datastax.driver.mapping.schemasync.SchemaSync;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* API to work with entities to be persisted in Cassandra. This is lightweight
* wrapper for the datastax Session Usage: create one instance per datastax
* Session or have a new one for each request.
* MappingSession msession = new MappingSession(keyspace, session);
* msession.save(entity);
* msession.get(Entity.class, id);
* msession.delete(entity);
*/
public class MappingSession {
protected static final Logger log = Logger.getLogger(EntityFieldMetaData.class.getName());
protected Session session;
protected String keyspace;
protected boolean doNotSync;
protected static Cache statementCache = CacheBuilder
.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.maximumSize(1000)
.concurrencyLevel(4)
.build();
/**
* Get statement from the cache or
* Prepare statement and place it in the cache.
* @return PreparedStatement.
*/
protected static PreparedStatement getOrPrepareStatement(final Session session, final BuiltStatement stmt, String key) {
PreparedStatement ps = null;
try {
ps = statementCache.get(key, new Callable() {
@Override
public PreparedStatement call() throws Exception {
return session.prepare(stmt);
}
});
} catch (ExecutionException e) {
// if the error caused by prepare the client will get it as is,
// otherwise process will not blow and statement will not be cached.
return session.prepare(stmt);
}
return ps;
}
public MappingSession(String keyspace, Session session) {
this(keyspace, session, false);
}
public MappingSession(String keyspace, Session session, boolean doNotSync) {
this.session = session;
this.keyspace = keyspace;
this.doNotSync = doNotSync;
}
/**
* Return the persistent instance of the given entity class with the given
* identifier, or null if there is no such persistent instance
*
* @param clazz
* - a persistent class
* @param id
* - an identifier
* @return a persistent instance or null
*/
public T get(Class clazz, Object id) {
return get(clazz, id, null);
}
/**
* Return the persistent instance of the given entity class with the given
* identifier, or null if there is no such persistent instance
*
* @param clazz
* - a persistent class
* @param id
* - an identifier
* @param options
* - read options supported by Cassandra. such as read
* consistency
* @return a persistent instance or null
*/
public T get(Class clazz, Object id, ReadOptions options) {
maybeSync(clazz);
BoundStatement bs = prepareSelect(clazz, id, options);
ResultSet rs = session.execute(bs);
List all = getFromResultSet(clazz, rs);
if (all.size() > 0) {
return all.get(0);
}
return null;
}
/**
* Delete the given instance
*
* @param entity - an instance of a persistent class
*/
public void delete(E entity) {
maybeSync(entity.getClass());
BuiltStatement bs = buildDelete(entity);
session.execute(bs);
}
/**
* Delete the given instance by ID(Primary key)
*
* @param clazz - type of the object
* @param id - primary key of the object
*/
public void delete(Class clazz, Object id) {
maybeSync(clazz);
BuiltStatement bs = buildDelete(clazz, id);
session.execute(bs);
}
/**
* Persist the given instance Entity must have a property id or a property
* annotated with @Id
*
* @param entity
* - an instance of a persistent class
* @return saved instance
*/
public E save(E entity) {
return save(entity, null);
}
/**
* Persist the given instance Entity must have a property id or a property
* annotated with @Id
*
* @param entity
* - an instance of a persistent class
* @return saved instance
*/
public E save(E entity, WriteOptions options) {
Statement stmt = prepareSave(entity, options);
log.fine(stmt.toString());
ResultSet rs = session.execute(stmt);
EntityTypeMetadata entityMetadata = EntityTypeParser.getEntityMetadata(entity.getClass());
if (entityMetadata.hasVersion()) {
Row row = rs.one();
if (!(row != null && row.getBool("[applied]"))) {
return null;
}
}
return entity;
}
protected BuiltStatement prepareSave(E entity, WriteOptions options) {
Class> clazz = entity.getClass();
maybeSync(clazz);
EntityTypeMetadata entityMetadata = EntityTypeParser.getEntityMetadata(clazz);
long version = Long.MIN_VALUE;
if (entityMetadata.hasVersion()) {
EntityFieldMetaData verField = entityMetadata.getVersionField();
version = ((Long) verField.getValue(entity)).longValue();
}
BuiltStatement stmt = null;
if (version > 0) {
stmt = buildUpdate(entity, options);
} else {
stmt = buildInsert(entity, options);
}
return stmt;
}
/**
* Execute the query and populate the list with items of given class.
*
* @param clazz
* @param query
* Statement
* @return List of items
*/
public List getByQuery(Class clazz, Statement query) {
maybeSync(clazz);
return getFromResultSet(clazz, session.execute(query));
}
/**
* Execute the query and populate the list with items of given class.
*
* @param clazz
* @param query
* String
* @return List of items
*/
public List getByQuery(Class clazz, String query) {
maybeSync(clazz);
return getFromResultSet(clazz, session.execute(query));
}
/**
* remove an item or items from the Set or List.
* @param id - entity PK
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - a single item, List or Set
*/
public void remove(Object id, Class> clazz, String propertyName, Object item) {
maybeSync(clazz);
EntityTypeMetadata emeta = EntityTypeParser.getEntityMetadata(clazz);
EntityFieldMetaData fmeta = emeta.getFieldMetadata(propertyName);
Update update = update(keyspace, emeta.getTableName());
if (item instanceof Set> && fmeta.getType() == Set.class) {
Set> set = (Set>)item;
if (set.size() == 0) return;
update.with(QueryBuilder.removeAll(fmeta.getColumnName(), set));
} else if (item instanceof List> && fmeta.getType() == List.class) {
List> list = (List>) item;
if (list.size() == 0) return;
update.with(QueryBuilder.discardAll(fmeta.getColumnName(), list));
} else if (fmeta.getType() == Set.class) {
update.with(QueryBuilder.remove(fmeta.getColumnName(), item));
} else if (fmeta.getType() == List.class) {
update.with(QueryBuilder.discard(fmeta.getColumnName(), item));
}
prepareAndExecuteUpdate(id, emeta, update);
}
/**
* delete value for the column
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
*/
public void deleteValue(Object id, Class> clazz, String propertyName) {
maybeSync(clazz);
EntityTypeMetadata emeta = EntityTypeParser.getEntityMetadata(clazz);
EntityFieldMetaData fmeta = emeta.getFieldMetadata(propertyName);
Delete delete = QueryBuilder.delete(fmeta.getColumnName()).from(keyspace, emeta.getTableName());
prepareAndExecuteDelete(id, emeta, delete);
}
/**
* append value to the Set, List or Map value can be .
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - a single value, a List, Set or a Map
*/
public void append(Object id, Class> clazz, String propertyName, Object item) {
append(id, clazz, propertyName, item, null);
}
/**
* append value to the Set, List or Map value can be .
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - a single value, a List, Set or a Map
* @param options - WriteOptions
*/
public void append(Object id, Class> clazz, String propertyName, Object item, WriteOptions options) {
maybeSync(clazz);
EntityTypeMetadata emeta = EntityTypeParser.getEntityMetadata(clazz);
EntityFieldMetaData fmeta = emeta.getFieldMetadata(propertyName);
Update update = update(keyspace, emeta.getTableName());
if (item instanceof Set> && fmeta.getType() == Set.class) {
Set> set = (Set>)item;
if (set.size() == 0) return;
update.with(QueryBuilder.addAll(fmeta.getColumnName(), set));
} else if (item instanceof List> && fmeta.getType() == List.class) {
List> list = (List>) item;
if (list.size() == 0) return;
update.with(QueryBuilder.appendAll(fmeta.getColumnName(), list));
} else if (item instanceof Map, ?>) {
Map,?> map = (Map,?>) item;
if (map.size() == 0) return;
update.with(QueryBuilder.putAll(fmeta.getColumnName(), map));
} else if (fmeta.getType() == Set.class) {
update.with(QueryBuilder.add(fmeta.getColumnName(), item));
} else if (fmeta.getType() == List.class) {
update.with(QueryBuilder.append(fmeta.getColumnName(), item));
}
applyOptions(options, update);
prepareAndExecuteUpdate(id, emeta, update);
}
/**
* add items at the beginning of the List
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - a single value or a List,
*/
public void prepend(Object id, Class> clazz, String propertyName, Object item) {
prepend(id, clazz, propertyName, item, null);
}
/**
* add items at the beginning of the List
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - a single value or a List
* @param options - WriteOptions
*/
public void prepend(Object id, Class> clazz, String propertyName, Object item, WriteOptions options) {
maybeSync(clazz);
EntityTypeMetadata emeta = EntityTypeParser.getEntityMetadata(clazz);
EntityFieldMetaData fmeta = emeta.getFieldMetadata(propertyName);
Update update = update(keyspace, emeta.getTableName());
if (item instanceof List> && fmeta.getType() == List.class) {
List> list = (List>) item;
if (list.size() == 0) return;
update.with(QueryBuilder.prependAll(fmeta.getColumnName(), list));
} else if (fmeta.getType() == List.class) {
update.with(QueryBuilder.prepend(fmeta.getColumnName(), item));
}
applyOptions(options, update);
prepareAndExecuteUpdate(id, emeta, update);
}
/**
* place item at the specified position in the List.
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - new value,
* @param idx - index where new value will be placed at
*/
public void replaceAt(Object id, Class> clazz, String propertyName, Object item, int idx) {
replaceAt(id, clazz, propertyName, item, idx, null);
}
/**
* place item at the specified position in the List.
* @param id - entity Primary Key
* @param clazz - entity class
* @param propertyName - property of entity to be modified
* @param item - new value,
* @param idx - index where new value will be placed at
* @param options - WriteOptions
*/
public void replaceAt(Object id, Class> clazz, String propertyName, Object item, int idx, WriteOptions options) {
maybeSync(clazz);
EntityTypeMetadata emeta = EntityTypeParser.getEntityMetadata(clazz);
EntityFieldMetaData fmeta = emeta.getFieldMetadata(propertyName);
Update update = update(keyspace, emeta.getTableName());
if (fmeta.getType() == List.class) {
update.with(QueryBuilder.setIdx(fmeta.getColumnName(), idx, item));
}
applyOptions(options, update);
prepareAndExecuteUpdate(id, emeta, update);
}
protected void prepareAndExecuteUpdate(Object id, EntityTypeMetadata emeta, Update update) {
List pkCols = emeta.getPkColumns();
for (String col : pkCols) {
update.where(eq(col, QueryBuilder.bindMarker()));
}
prepareAndExecute(id, emeta, update, pkCols);
}
protected void prepareAndExecuteDelete(Object id, EntityTypeMetadata emeta, Delete delete) {
List pkCols = emeta.getPkColumns();
for (String col : pkCols) {
delete.where(eq(col, QueryBuilder.bindMarker()));
}
prepareAndExecute(id, emeta, delete, pkCols);
}
protected void prepareAndExecute(Object id, EntityTypeMetadata emeta, BuiltStatement stmt, List pkCols) {
// bind parameters
Object[] values = emeta.getIdValues(id).toArray(new Object[pkCols.size()]);
String q = stmt.getQueryString();
PreparedStatement ps = getOrPrepareStatement(session, stmt, q);
BoundStatement bs = ps.bind(values);
session.execute(bs);
}
/**
* Statement to persist an entity in Cassandra
*
* @param entity to be inserted
* @return com.datastax.driver.core.BoundStatement
*/
protected BuiltStatement buildInsert(E entity, WriteOptions options) {
Class> clazz = entity.getClass();
EntityTypeMetadata entityMetadata = EntityTypeParser.getEntityMetadata(clazz);
String table = entityMetadata.getTableName();
List fields = entityMetadata.getFields();
List pkCols = entityMetadata.getPkColumns();
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy