package me.prettyprint.hom;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.metamodel.Metamodel;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.beans.ColumnSlice;
import me.prettyprint.hector.api.mutation.Mutator;
import me.prettyprint.hom.annotations.AnnotationScanner;
import me.prettyprint.hom.annotations.DefaultAnnotationScanner;
import me.prettyprint.hom.cache.HectorObjectMapperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handles entity management by scanning classpath for classes annotated with
* {@link Entity} and providing basic load and save methods.
*
* @author
*/
public class EntityManagerImpl implements EntityManager {
private static Logger logger = LoggerFactory.getLogger(EntityManagerImpl.class);
private Keyspace keyspace;
private HectorObjectMapper objMapper;
private ClassCacheMgr cacheMgr;
private EntityManagerFactory emf;
public EntityManagerImpl(Keyspace keyspace, String classpathPrefix) {
this(keyspace, new DefaultAnnotationScanner(), classpathPrefix);
}
public EntityManagerImpl(Keyspace keyspace, AnnotationScanner scanner, String classpathPrefix) {
this(keyspace, new String[]{classpathPrefix}, null, null, scanner);
}
public EntityManagerImpl(Keyspace keyspace, String[] classpathPrefix) {
this(keyspace, new DefaultAnnotationScanner(), classpathPrefix);
}
public EntityManagerImpl(Keyspace keyspace, AnnotationScanner scanner, String[] classpathPrefix) {
this(keyspace, classpathPrefix, null, null, scanner);
}
public EntityManagerImpl(Keyspace keyspace, EntityManagerFactory emf, Class<?>... classes) {
this(keyspace, null, null, null);
for (Class<?> clazz : classes) {
cacheMgr.initializeCacheForClass(clazz);
}
this.emf = emf;
}
public EntityManagerImpl(Keyspace keyspace, String[] classpathPrefix, ClassCacheMgr cacheMgr,
HectorObjectMapper objMapper, AnnotationScanner scanner) {
this.keyspace = keyspace;
if (null != cacheMgr) {
this.cacheMgr = cacheMgr;
} else {
this.cacheMgr = new ClassCacheMgr();
}
if (null != objMapper) {
this.objMapper = objMapper;
} else {
this.objMapper = new HectorObjectMapper(this.cacheMgr);
}
initialize(scanner, classpathPrefix);
}
public EntityManagerImpl(Keyspace space, EntityManagerFactoryImpl entityManagerFactory, String[] packageToScan, AnnotationScanner scanner) {
this(space, packageToScan, null, null, scanner);
this.emf = entityManagerFactory;
}
/**
* Initialize the manager by scanning the classpath starting with the
* <code>classpathPrefix</code>, looking for classes annotated with
* {@link Entity}. If an Entity class is found, it looks for the
* {@link Table} annotation to determine the Cassandra column family
* name.
*
* @param classpathPrefixArr
* @see ClassCacheMgr
*/
public void initialize(AnnotationScanner scanner, String[] classpathPrefixArr) {
if (null != classpathPrefixArr && 0 < classpathPrefixArr.length) {
for (String classpathPrefix : classpathPrefixArr) {
initializeClasspath(scanner, classpathPrefix);
}
}
if (logger.isDebugEnabled()) {
logger.debug("classpath array has {} items : {}",
(null != classpathPrefixArr ? classpathPrefixArr.length : "0"), classpathPrefixArr);
}
}
private void initializeClasspath(AnnotationScanner scanner, String classpathPrefix) {
Set<Class<?>> classSet = scanner.scan(classpathPrefix, Entity.class);
for (Class<?> clazz : classSet) {
cacheMgr.initializeCacheForClass(clazz);
}
}
@Deprecated
public <T, I> T load(Class<T> clazz, I id) {
return find(clazz, id);
}
@Deprecated
public <T> T load(Class<T> clazz, Object id, ColumnSlice<String, byte[]> colSlice) {
return find(clazz, id, colSlice);
}
@Deprecated
public <T> T save(T obj) {
persist(obj);
return obj;
}
/**
* Load an entity instance. If the ID does not map to a persisted entity, then
* null is returned.
*
* @param <T> The type of entity to load for compile time type checking
* @param clazz The type of entity to load for runtime instance creation
* @param id ID of the instance to load
* @return instance of Entity or null if can't be found
*/
@Override
public <T> T find(Class<T> clazz, Object id) {
if (null == clazz) {
throw new IllegalArgumentException("clazz cannot be null");
}
if (null == id) {
throw new IllegalArgumentException("id cannot be null");
}
CFMappingDef<T> cfMapDef = cacheMgr.getCfMapDef(clazz, false);
if (null == cfMapDef) {
throw new HectorObjectMapperException("No class annotated with @"
+ Entity.class.getSimpleName() + " for type, " + clazz.getName());
}
return objMapper.getObject(keyspace, cfMapDef.getEffectiveColFamName(), id);
}
/**
* Load an entity instance given the raw column slice. This is a stop gap
* solution for instanting objects using entity manager while iterating over
* rows.
*
* @param <T> The type of entity to load for compile time type checking
* @param clazz The type of entity to load for runtime instance creation
* @param id ID of the instance to load
* @param colSlice Raw row slice as returned from Hector API, of the type
* <code>ColumnSlice<String, byte[]></code>
* @return Completely instantiated persisted object
*/
public <T> T find(Class<T> clazz, Object id, ColumnSlice<String, byte[]> colSlice) {
if (null == clazz) {
throw new IllegalArgumentException("clazz cannot be null");
}
if (null == id) {
throw new IllegalArgumentException("id cannot be null");
}
CFMappingDef<T> cfMapDef = cacheMgr.getCfMapDef(clazz, false);
if (null == cfMapDef) {
throw new HectorObjectMapperException("No class annotated with @"
+ Entity.class.getSimpleName() + " for type, " + clazz.getName());
}
T obj = objMapper.createObject(cfMapDef, id, colSlice);
return obj;
}
/**
* Save the list of entity intances.
*
* @param objColl
* @return
*/
public Collection<Object> persist(Collection<Object> objColl) {
if (null == objColl) {
throw new IllegalArgumentException("object to save cannot be null");
}
objMapper.saveObjCollection(keyspace, objColl);
return objColl;
}
/**
* Save the list of entity intances.
*
* @param objColl
* @return
*/
public Collection<?> persist(Collection<?> objColl, Mutator<byte[]> m) {
if (null == objColl) {
throw new IllegalArgumentException("object to save cannot be null");
}
objMapper.saveObjCollection(keyspace, objColl, m);
return objColl;
}
/**
* Save the entity instance.
*
* @param obj
* @return
*/
@Override
public void persist(Object obj) {
if (null == obj) {
throw new IllegalArgumentException("object to save cannot be null");
}
objMapper.saveObj(keyspace, obj);
}
@Override
public <T> T merge(T t) {
return objMapper.saveObj(keyspace, t);
}
@Override
public void remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
return find(entityClass, primaryKey);
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
return find(entityClass, primaryKey);
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
return find(entityClass, primaryKey);
}
@Override
public <T> T getReference(Class<T> entityClass, Object primaryKey) {
return find(entityClass, primaryKey);
}
@Override
public void flush() {
// no-op
}
@Override
public void setFlushMode(FlushModeType flushMode) {
// no-op
}
@Override
public FlushModeType getFlushMode() {
return FlushModeType.AUTO;
}
@Override
public void lock(Object entity, LockModeType lockMode) {
// no-op
}
@Override
public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
// no-op
}
@Override
public void refresh(Object entity) {
// no-op
}
@Override
public void refresh(Object entity, Map<String, Object> properties) {
refresh(entity);
}
@Override
public void refresh(Object entity, LockModeType lockMode) {
refresh(entity);
}
@Override
public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
refresh(entity);
}
@Override
public void clear() {
// no-op
}
@Override
public void detach(Object entity) {
// no-op
}
@Override
public boolean contains(Object entity) {
return false;
}
@Override
public LockModeType getLockMode(Object entity) {
return LockModeType.NONE;
}
@Override
public void setProperty(String propertyName, Object value) {
// no-op
}
@Override
public Map<String, Object> getProperties() {
return Collections.emptyMap();
}
@Override
public Query createQuery(String qlString) {
return null;
}
@Override
public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
return null;
}
@Override
public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
return null;
}
@Override
public Query createNamedQuery(String name) {
return null;
}
@Override
public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
return null;
}
@Override
public Query createNativeQuery(String sqlString) {
return null;
}
@Override
public Query createNativeQuery(String sqlString, Class resultClass) {
return null;
}
@Override
public Query createNativeQuery(String sqlString, String resultSetMapping) {
return null;
}
@Override
public void joinTransaction() {
// no-op
}
@Override
public <T> T unwrap(Class<T> cls) {
return null;
}
@Override
public Object getDelegate() {
return this;
}
@Override
public void close() {
// no-op
}
@Override
public boolean isOpen() {
return true;
}
@Override
public EntityTransaction getTransaction() {
return null;
}
@Override
public EntityManagerFactory getEntityManagerFactory() {
return emf;
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
return null;
}
@Override
public Metamodel getMetamodel() {
return null;
}
}