All Downloads are FREE. Search and download functionalities are using the official Maven repository.

panda.dao.gae.GaeDao Maven / Gradle / Ivy

Go to download

Panda Core is the core module of Panda Framework, it contains commonly used utility classes similar to apache-commons.

There is a newer version: 1.8.0
Show newest version
package panda.dao.gae;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query.CompositeFilter;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.datastore.Text;

import panda.dao.AbstractDao;
import panda.dao.DaoException;
import panda.dao.DaoIterator;
import panda.dao.entity.Entity;
import panda.dao.entity.EntityField;
import panda.dao.query.DataQuery;
import panda.dao.query.Filter.ComboFilter;
import panda.dao.query.Filter.ReferFilter;
import panda.dao.query.Filter.SimpleFilter;
import panda.dao.query.Filter.ValueFilter;
import panda.dao.query.Operator;
import panda.dao.query.Query;
import panda.lang.Exceptions;
import panda.lang.Iterators.SingleIterator;
import panda.lang.Logical;
import panda.lang.Order;
import panda.lang.Randoms;
import panda.lang.Strings;
import panda.lang.reflect.Types;
import panda.log.Log;
import panda.log.Logs;

public class GaeDao extends AbstractDao {
	private static final Log log = Logs.getLog(GaeDao.class);
	
	private static final int MAX_SL = 500;
	//private static final int MAX_BL = 1048576; //1M
	//private static final int MAX_TL = 1048576; //1M

	private static final String ROOT = "_root_";

	private static Map roots;

	private com.google.appengine.api.datastore.Entity rootEntity;

	/**
	 * auto start count
	 */
	private int autoCount;
	
	private DatastoreService service;
	private com.google.appengine.api.datastore.Transaction transaction;
	
	public GaeDao(GaeDaoClient daoClient, DatastoreService service) {
		super(daoClient);
		this.service = service;
	}

	//---------------------------------------------------------------------
	protected GaeDaoClient getGaeDaoClient() {
		return (GaeDaoClient)getDaoClient();
	}

	//---------------------------------------------------------------------
	@Override
	protected void autoStart() {
		if (transaction == null) {
			try {
				transaction = service.beginTransaction();
			}
			catch (Exception e) {
				throw new DaoException("Failed to open transaction", e);
			}
		}
		autoCount++;
	}

	@Override
	protected void autoCommit() {
		if (autoCount == 1) {
			commit();
		}
	}

	@Override
	protected void autoClose() {
		autoCount--;
		if (autoCount == 0) {
			transaction = null;
		}
	}

	//--------------------------------------------------------------------
	/**
	 * commit a transaction
	 */
	@Override
	public void commit() {
		if (transaction != null) {
			try {
				transaction.commit();
			}
			catch (Exception e) {
				throw new DaoException("Failed to commit transaction", e);
			}
			finally {
				transaction = null;
			}
		}
	}
	
	/**
	 * rollback a transaction
	 */
	@Override
	public void rollback() {
		if (transaction != null) {
			try {
				transaction.rollback();
			}
			catch (Exception e) {
				throw new DaoException("Failed to rollback transaction", e);
			}
			finally {
				transaction = null;
			}
		}
	}

	//--------------------------------------------------------------------
	/**
	 * execute a transaction
	 */
	@Override
	public void exec(Runnable transaction) {
		exec(transaction, 0);
	}
	
	/**
	 * execute a transaction
	 * @param level transaction level
	 */
	@Override
	public void exec(Runnable transaction, int level) {
		assertTransaction(transaction);

		try {
			autoStart();
			transaction.run();
			autoCommit();
		}
		catch (Throwable e) {
			rollback();
			throw Exceptions.wrapThrow(e);
		}
		finally {
			autoClose();
		}
	}

	//-----------------------------------------------------------------------------
	private com.google.appengine.api.datastore.Entity getRootEntity() {
		if (rootEntity == null) {
			if (roots == null) {
				synchronized (GaeDao.class) {
					roots = new HashMap();
					com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query(ROOT);
					PreparedQuery pq = service.prepare(q);
					if (log.isDebugEnabled()) {
						log.debug("QUERY: " + q);
					}
					Iterator it = pq.asIterator();
					while (it.hasNext()) {
						com.google.appengine.api.datastore.Entity en = it.next();
						if (log.isDebugEnabled()) {
							log.debug("GET ROOT: " + en);
						}
						roots.put(en.getKey().getName(), en);
					}
				}
			}

			rootEntity = roots.get(getDaoClient().getName());
			if (rootEntity == null) {
				synchronized (GaeDao.class) {
					com.google.appengine.api.datastore.Transaction transaction = service.beginTransaction();
					Key key = KeyFactory.createKey(ROOT, getDaoClient().getName());
					rootEntity = new com.google.appengine.api.datastore.Entity(key);
					if (log.isDebugEnabled()) {
						log.debug("PUT ROOT: " + rootEntity);
					}
					service.put(transaction, rootEntity);
					transaction.commit();
					roots.put(key.getName(), rootEntity);
				}
			}
		}
		return rootEntity;
	}

	private Key getRootKey() {
		return getRootEntity().getKey();
	}

	//-----------------------------------------------------------------------------
	private Key createKey(Entity entity, Object id) {
		String table = getTableName(entity);
		if (id instanceof Number) {
			return KeyFactory.createKey(getRootKey(), table, ((Number)id).longValue());
		}

		return KeyFactory.createKey(getRootKey(), table, id.toString());
	}

	private Object getDataIdentity(Entity en, Object data) {
		EntityField eid = en.getIdentity();
		if (eid == null) {
			return null;
		}

		Object id = eid.getValue(data);
		return id;
	}

	private void setDataIdentity(Entity en, Object data, Key key) {
		EntityField eid = en.getIdentity();
		if (eid == null) {
			log.debug(en.getType() + " has no identity");
			return;
		}

		if (eid.isNumberIdentity()) {
			Object id = convertValueFromGae(eid, key.getId());
			eid.setValue(data, id);
		}
		else {
			Object id = convertValueFromGae(eid, Strings.isEmpty(key.getName()) ? key.getId() : key.getName());
			eid.setValue(data, id);
		}
	}

	private Object convertValueFromGae(EntityField ef, Object value) throws DaoException {
		if (value == null) {
			return value;
		}
		
		if (value instanceof Text) {
			value = ((Text)value).getValue();
		}
		else if (value instanceof Blob) {
			value = ((Blob)value).getBytes();
		}

		if (ef != null) {
			Type toType = ef.getType();
			if (!Types.isInstance(value, toType)) {
				value = getDaoClient().getCastors().cast(value, toType);
			}
		}

		return value;
	}

	private Object convertValueToGae(Object value) throws DaoException {
		if (value == null) {
			return value;
		}

		GaeConverter gc = getGaeDaoClient().findConverter(value.getClass());
		if (gc != null) {
			return gc.convert(value);
		}
		
		if (value instanceof Character) {
			value = String.valueOf(value);
		}
		else if (value instanceof CharSequence) {
			String s = value.toString();
			if (s.length() > MAX_SL) {
				value = new Text(s);
			}
			else {
				value = s;
			}
		}
		else if (value instanceof byte[]) {
			value = new Blob((byte[])value);
		}
		else if (value instanceof BigInteger) {
			value = ((BigInteger)value).longValue();
		}
		else if (value instanceof BigDecimal) {
			value = ((BigDecimal)value).doubleValue();
		}
		return value;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void convertEntityToData(com.google.appengine.api.datastore.Entity ge, Object data,
			Query query) throws DaoException {

		Entity entity = query.getEntity();
		if (entity == null) {
			Map map = (Map)data;
			Map eps = ge.getProperties();
			for (Entry e : eps.entrySet()) {
				String fn = e.getKey();
				if (query.shouldExclude(fn)) {
					continue;
				}

				Object v = e.getValue();
				v = convertValueFromGae(null, v);
				map.put(fn, v);
			}
		}
		else {
			Map eps = ge.getProperties();
			for (Entry e : eps.entrySet()) {
				String fn = e.getKey();
				EntityField ef = entity.getColumn(fn);
				if (ef == null || ef.isNotPersistent() || query.shouldExclude(ef.getName())) {
					continue;
				}
	
				Object v = e.getValue();
				v = convertValueFromGae(ef, v);
				ef.setValue(data, v);
			}
			setDataIdentity(entity, data, ge.getKey());
		}
	}
	
	private void convertDataToEntity(Query query, Object data,
			com.google.appengine.api.datastore.Entity ge) throws DaoException {

		Entity entity = query.getEntity();
		
		// reserve exclude properties
		Map eps = ge.getProperties();
		for (String k : eps.keySet()) {
			if (!query.shouldExclude(k)) {
				ge.removeProperty(k);
			}
		}
		
		// set properties except excludes
		for (EntityField ef : entity.getFields()) {
			if (ef.isReadonly() || query.shouldExclude(ef.getName())) {
				continue;
			}

			Object v = ef.getValue(data);
			v = convertValueToGae(v);

			String fn = ef.getColumn();
			ge.setProperty(fn, v);
		}
	}

	//-----------------------------------------------------------------------------
	/**
	 * is query for identity only? 
	 */
	private boolean isQueryIdentity(Query query) {
		return getQueryIdentity(query) != null;
	}
	
	/**
	 * get identity value from query
	 */
	private Object getQueryIdentity(Query query) {
		Entity entity = query.getEntity();
		if (entity == null) {
			return null;
		}
		
		EntityField eid = entity.getIdentity();
		if (eid == null) {
			return null;
		}
		
		if (!query.hasFilters() || query.getFilters().getFilters().size() > 1) {
			return null;
		}
		
		Object f = query.getFilters().first();
		if (!(f instanceof ValueFilter) 
				|| !Operator.EQUAL.equals(((ValueFilter)f).getOperator())) {
			return null;
		}
		
		ValueFilter vf = (ValueFilter)f;
		if (vf.getValue() == null || !eid.getName().equals(vf.getField())) {
			return null;
		}
		
		return vf.getValue();
	}
	
	//-----------------------------------------------------------------------------
	private EntityField getEntityField(Entity entity, String field, String name) {
		EntityField ef = entity.getField(field);
		if (ef == null) {
			throw new IllegalArgumentException("invalid " + name + " field '" + field + "' of entity " + entity.getType());
		}
		return ef;
	}
	
	private Filter where(Entity entity, ComboFilter cf) {
		List fs = new ArrayList();
		for (Object f : cf.getFilters()) {
			if (f instanceof ValueFilter) {
				ValueFilter vf = (ValueFilter)f;
				if (entity == null) {
					fs.add(whereValueFilter(vf.getField(), vf));
				}
				else {
					EntityField ef = getEntityField(entity, vf.getField(), "where");
					fs.add(whereValueFilter(ef.getColumn(), vf));
				}
			}
			else if (f instanceof ReferFilter) {
				throw Exceptions.unsupported("Field compare is not supported by Google App Engine.");
			}
			else if (f instanceof SimpleFilter) {
				SimpleFilter sf = (SimpleFilter)f;
				if (entity == null) {
					fs.add(whereSimpleFilter(sf.getField(), sf));
				}
				else {
					EntityField ef = getEntityField(entity, sf.getField(), "simple");
					fs.add(whereSimpleFilter(ef.getColumn(), sf));
				}
			}
			else if (f instanceof ComboFilter) {
				fs.add(where(entity, (ComboFilter)f));
			}
		}
		if (fs.size() == 1) {
			return fs.get(0);
		}
		return new CompositeFilter(cf.getLogical() == Logical.OR ? CompositeFilterOperator.OR : CompositeFilterOperator.AND, fs);
	}

	private void where(com.google.appengine.api.datastore.Query gq, Query query) {
		if (!query.hasFilters()) {
			return;
		}
		
		Entity entity = query.getEntity();
		
		Filter filter = where(entity, query.getFilters());

		gq.setFilter(filter);
	}

	private FilterPredicate whereSimpleFilter(String column, SimpleFilter sf) {
		Operator op = sf.getOperator();
		if (op == Operator.IS_NULL) {
			return new FilterPredicate(column, FilterOperator.EQUAL, null);
		}
		else if (op == Operator.IS_NOT_NULL) {
			return new FilterPredicate(column, FilterOperator.NOT_EQUAL, null);
		}
		else {
			throw Exceptions.unsupported("Operator " + op + " is not supported by Google App Engine.");
		}
	}
	
	private Filter whereValueFilter(String column, ValueFilter evc) {
		Operator op = evc.getOperator();
		if (op == Operator.BETWEEN) {
			List fs = new ArrayList();
			fs.add(new FilterPredicate(column, FilterOperator.GREATER_THAN_OR_EQUAL, convertValueToGae(evc.getValue(0))));
			fs.add(new FilterPredicate(column, FilterOperator.LESS_THAN_OR_EQUAL, convertValueToGae(evc.getValue(1))));
			return new CompositeFilter(CompositeFilterOperator.AND, fs);
		}
		else if (op == Operator.NOT_BETWEEN) {
			List fs = new ArrayList();
			fs.add(new FilterPredicate(column, FilterOperator.LESS_THAN, convertValueToGae(evc.getValue(0))));
			fs.add(new FilterPredicate(column, FilterOperator.GREATER_THAN, convertValueToGae(evc.getValue(1))));
			return new CompositeFilter(CompositeFilterOperator.OR, fs);
		}
		else {
			FilterOperator fo = null;
			if (op == Operator.LESS_THAN) {
				fo = FilterOperator.LESS_THAN;
			}
			else if (op == Operator.LESS_EQUAL) {
				fo = FilterOperator.LESS_THAN_OR_EQUAL;
			}
			else if (op == Operator.GREATER_THAN) {
				fo = FilterOperator.GREATER_THAN;
			}
			else if (op == Operator.GREATER_EQUAL) {
				fo = FilterOperator.GREATER_THAN_OR_EQUAL;
			}
			else if (op == Operator.EQUAL) {
				fo = FilterOperator.EQUAL;
			}
			else if (op == Operator.NOT_EQUAL) {
				fo = FilterOperator.NOT_EQUAL;
			}
			else if (op == Operator.IN) {
				// GAE SQL does not support IN operator
				// fo = FilterOperator.IN;
			}

			if (fo == null) {
				throw Exceptions.unsupported("GQL operator " + op + " is not supported by Google App Engine.");
			}
			return new FilterPredicate(column, fo, convertValueToGae(evc.getValue()));
		}
	}

	//-----------------------------------------------------------------------------
	private SortDirection getSortDirection(Order order) {
		if (Order.DESC.equals(order)) {
			return SortDirection.DESCENDING;
		}
		else if (Order.ASC.equals(order)) {
			return SortDirection.ASCENDING;
		}
		else {
			throw new IllegalArgumentException("Illegal sort direction: " + order);
		}
	}

	private void order(com.google.appengine.api.datastore.Query gq, Query query) {
		if (!query.hasOrders()) {
			return;
		}
		
		Entity entity = query.getEntity();
		if (entity == null) {
			for (Entry en : query.getOrders().entrySet()) {
				gq.addSort(en.getKey(), getSortDirection(en.getValue()));
			}
		}
		else {
			for (Entry en : query.getOrders().entrySet()) {
				EntityField ef = getEntityField(entity, en.getKey(), "order");
				gq.addSort(ef.getColumn(), getSortDirection(en.getValue()));
			}
		}
	}
	
	//-----------------------------------------------------------------------------
	private PreparedQuery prepareQuery(Query query) {
		return prepareQuery(query, false);
	}

	private PreparedQuery prepareQuery(Query query, boolean keyOnly) {
		com.google.appengine.api.datastore.Query gq = new com.google.appengine.api.datastore.Query(getTableName(query));

		if (keyOnly) {
			gq.setKeysOnly();
		}
		// @see https://developers.google.com/appengine/docs/java/datastore/projectionqueries#Java_Limitations_on_projections
//		else {
//			// excludes
//			for (EntityField ef : entity.getFields()) {
//				if (query != null && query.shouldExclude(ef.getName())) {
//					continue;
//				}
//				gq.addProjection(new PropertyProjection(ef.getColumn(), null));
//			}
//		}

		where(gq, query);
		order(gq, query);

		gq.setAncestor(getRootKey());
		if (log.isDebugEnabled()) {
			log.debug("QUERY: " + query);
		}
		return service.prepare(transaction, gq);
	}

	private FetchOptions getFetchOptions(Query q) {
		FetchOptions fo = FetchOptions.Builder.withDefaults();
		if (q.getStart() > 0) {
			fo.offset((int)q.getStart());
		}
		if (q.getLimit() > 0) {
			fo.limit((int)q.getLimit());
		}
		return fo;
	}

	private com.google.appengine.api.datastore.Entity fetchSingle(DataQuery query) {
		query.setLimit(1);
		PreparedQuery pq = prepareQuery(query);
		FetchOptions fo = getFetchOptions(query);
		Iterator it = pq.asIterator(fo);
		while (it.hasNext()) {
			return it.next();
		}
		return null;
	}

	//-----------------------------------------------------------------------------
	private void saveEntity(com.google.appengine.api.datastore.Entity ge) throws DaoException {
		if (log.isDebugEnabled()) {
			log.debug("PUT: " + ge);
		}
		service.put(transaction, ge);
	}

	private com.google.appengine.api.datastore.Entity getEntity(Key key) throws DaoException {
		com.google.appengine.api.datastore.Entity ge = null;

		try {
			ge = service.get(transaction, key);
		}
		catch (EntityNotFoundException e) {
		}
		finally {
			if (log.isDebugEnabled()) {
				log.debug("GET: (" + key + ") - " + ge);
			}
		}
		
		return ge;
	}

	private void deleteEntity(Key key) throws DaoException {
		if (log.isDebugEnabled()) {
			log.debug("DELETE: (" + key + ")");
		}
		service.delete(transaction, key);
	}

	@SuppressWarnings("unused")
	private void deleteEntity(Iterable keys) throws DaoException {
		if (log.isDebugEnabled()) {
			log.debug("DELETE: [" + keys + "]");
		}
		service.delete(transaction, keys);
	}

	//-----------------------------------------------------------------------------
	/**
	 * drop a table if exists
	 * 
	 * @param entity entity
	 */
	@Override
	public void drop(Entity entity) {
		// not support
	}

	/**
	 * drop a table if exists
	 * 
	 * @param table table name
	 */
	@Override
	public void drop(String table) {
		// not support
	}

	//--------------------------------------------------------------------
	/**
	 * create table
	 * 
	 * @param entity entity
	 */
	@Override
	public void create(Entity entity) {
		// not need
	}
	
	//--------------------------------------------------------------------
	/**
	 * create table ddl
	 * 
	 * @param entity entity
	 */
	@Override
	public String ddl(Entity entity) {
		log.info("ddl(entity) method is unsupported for GAE datastore.");
		return Strings.EMPTY;
	}

	//--------------------------------------------------------------------
	/**
	 * check a table exists in the data store.
	 * 
	 * @param table table name
	 * @return true if the record or the table exists in the data store
	 */
	@Override
	protected boolean existsByTable(String table) {
		return true;
	}

	/**
	 * check a record exists in the data store.
	 * if the keys is not supplied, then check the table existence.
	 * 
	 * @param entity entity
	 * @param keys record keys (int, string or java bean with keys)
	 * @return true if the record or the table exists in the data store
	 */
	@Override
	protected boolean existsByKeys(Entity entity, Object ... keys) {
		assertEntity(entity);

		if (keys == null || keys.length == 0) {
			return existsByTable(getTableName(entity));
		}

		DataQuery query = createQuery(entity);
		query.setLimit(1);
		queryPrimaryKey(query, keys);
		selectPrimaryKeys(query);
		
		return existsByQuery(query);
	}

	/**
	 * check a record exists in the data store.
	 * if the query is not supplied, then check the table existence.
	 * 
	 * @param query query
	 * @return true if the record or the table exists in the data store
	 */
	@Override
	protected boolean existsByQuery(DataQuery query) {
		if (!query.hasFilters()) {
			return existsByTable(getTableName(query));
		}

		if (log.isDebugEnabled()) {
			log.debug("existsByQuery: " + query);
		}
		
		autoStart();
		try {
			com.google.appengine.api.datastore.Entity ge = null;
			
			Object id = getQueryIdentity(query);
			if (id != null) {
				if (isValidIdentity(id)) {
					Key k = createKey(query.getEntity(), id);
					ge = getEntity(k);
				}
			}
			else {
				PreparedQuery pq = prepareQuery(query, true);
				ge = pq.asSingleEntity();
			}

			return (ge != null);
		}
		catch (Exception e) {
			throw new DaoException("Failed to fetch query " + getTableName(query) + ": " + query, e);
		}
		finally {
			autoClose();
		}
	}

	//--------------------------------------------------------------------
	/**
	 * get a record by the supplied query
	 * 
	 * @param query query
	 * @return record
	 */
	@Override
	protected  T fetchByQuery(DataQuery query) {
		if (log.isDebugEnabled()) {
			log.debug("fetchByQuery: " + query);
		}
		
		Entity entity = query.getEntity();
		
		autoStart();
		try {
			com.google.appengine.api.datastore.Entity ge = null;
			
			Object id = getQueryIdentity(query);
			if (id != null) {
				if (isValidIdentity(id)) {
					Key k = createKey(entity, id);
					ge = getEntity(k);
				}
			}
			else {
				ge = fetchSingle(query);
			}

			if (ge != null) {
				T data = createEntityData(entity);
				convertEntityToData(ge, data, query);
				return data;
			}
			return null;
		}
		catch (Exception e) {
			throw new DaoException("Failed to fetch entity " + getTableName(query) + ": " + query, e);
		}
		finally {
			autoClose();
		}
	}

	//--------------------------------------------------------------------
	/**
	 * count records by the supplied query.
	 * 
	 * @param query query
	 * @return record count
	 */
	@Override
	protected long countByQuery(Query query) {
		if (log.isDebugEnabled()) {
			log.debug("countByQuery: " + query);
		}
		
		autoStart();
		try {
			PreparedQuery pq = prepareQuery(query, true);
			FetchOptions fo = getFetchOptions(query);
			int cnt = pq.countEntities(fo);
			return cnt;
		}
		catch (Exception e) {
			throw new DaoException("Failed to count entity " + getTableName(query) + ": " + query, e);
		}
		finally {
			autoClose();
		}
	}

	//--------------------------------------------------------------------
	/**
	 * select records by the supplied query.
	 * if query is null then select all records.
	 * 
	* @param query query
	 * @return record list
	 */
	@Override
	protected  List selectByQuery(DataQuery query) {
		if (log.isDebugEnabled()) {
			log.debug("selectByQuery: " + query);
		}
		
		if (isQueryIdentity(query)) {
			T d = fetchByQuery(query);
			List list = new ArrayList();
			list.add(d);
			return list;
		}

		autoStart();
		try {
			Entity entity = query.getEntity();
			PreparedQuery pq = prepareQuery(query);
			FetchOptions fo = getFetchOptions(query);
			Iterator it = pq.asIterator(fo);
			List list = new ArrayList();
			while (it.hasNext()) {
				com.google.appengine.api.datastore.Entity ge = it.next();
				T data = createEntityData(entity);
				convertEntityToData(ge, data, query);
				list.add(data);
			}
			return list;
		}
		catch (Exception e) {
			throw new DaoException("Failed to select entity " + getTableName(query) + ": " + query, e);
		}
		finally {
			autoClose();
		}
	}

	/**
	 * select records by the supplied query.
	 * 
	* @param query query
	 * @return data iterator
	 */
	@Override
	protected  DaoIterator iterateByQuery(DataQuery query) {
		if (log.isDebugEnabled()) {
			log.debug("iterateByQuery: " + query);
		}
		
		if (isQueryIdentity(query)) {
			T d = fetchByQuery(query);
			return new OneDaoIterator(d);
		}

		autoStart();
		try {
			PreparedQuery pq = prepareQuery(query);
			FetchOptions fo = getFetchOptions(query);
			Iterator it = pq.asIterator(fo);

			return new GaeDaoIterator(query, it);
		}
		catch (Throwable e) {
			autoClose();
			throw new DaoException("Failed to select entity " + getTableName(query) + ": " + query, e);
		}
	}
	
	private static class OneDaoIterator extends SingleIterator implements DaoIterator {
		public OneDaoIterator(T data) {
			super(data);
		}
		
		@Override
		public void remove() {
			throw Exceptions.unsupported("Remove unsupported on OneDaoIterator");
		}

		@Override
		public void close() {
		}
	}

	private class GaeDaoIterator implements DaoIterator {
		DataQuery query;
		Iterator it;
		
		public GaeDaoIterator(DataQuery query, Iterator it) {
			this.query = query;
			this.it = it;
		}
		
		@Override
		public boolean hasNext() {
			return it.hasNext();
		}

		@Override
		public T next() {
			com.google.appengine.api.datastore.Entity ge = it.next();
			Entity entity = query.getEntity();
			T data = createEntityData(entity);
			convertEntityToData(ge, data, query);
			return data;
		}

		@Override
		public void remove() {
			throw Exceptions.unsupported("Remove unsupported on GaoDaoIterator");
		}

		@Override
		public void close() {
			autoClose();
		}
	}
	
	//--------------------------------------------------------------------
	/**
	 * delete record by the supplied query
	 * 
	 * @param query query
	 * @return deleted count
	 */
	@Override
	protected int deletesByQuery(Query query) {
		if (log.isDebugEnabled()) {
			log.debug("deletesByQuery: " + query);
		}
		
		autoStart();
		try {
			PreparedQuery pq = prepareQuery(query, true);
			FetchOptions fo = getFetchOptions(query);
			
			Iterator it = pq.asIterator(fo);
			int cnt = 0;
			while (it.hasNext()) {
				deleteEntity(it.next().getKey());
				cnt++;
			}
			autoCommit();
			return cnt;
		}
		catch (Exception e) {
			rollback();
			throw new DaoException("Failed to delete entity " + getTableName(query) + ": " + query, e);
		}
		finally {
			autoClose();
		}
	}

	//--------------------------------------------------------------------
	/**
	 * insert a record.
	 * 

* a '@Id' field will be set after insert. * set '@Id(auto=false)' to disable retrieving the primary key of the newly inserted row. *

* the '@Prep("SELECT ...")' sql will be executed before insert. *

* the '@Post("SELECT ...")' sql will be executed after insert. * * @param entity the Entity of the obj * @param data the record to be inserted (@Id property will be setted) * @return the inserted record */ @Override protected T insertData(Entity entity, T data) { if (log.isDebugEnabled()) { log.debug("insert: " + data); } com.google.appengine.api.datastore.Entity ge; Object kid = getDataIdentity(entity, data); if (isValidIdentity(kid)) { Key key = createKey(entity, kid); ge = new com.google.appengine.api.datastore.Entity(key); } else { EntityField ef = entity.getIdentity(); if (ef == null) { ge = new com.google.appengine.api.datastore.Entity(getTableName(entity), getRootKey()); } else { if (ef.isNumberIdentity()) { ge = new com.google.appengine.api.datastore.Entity(getTableName(entity), getRootKey()); } else { ge = new com.google.appengine.api.datastore.Entity(getTableName(entity), Randoms.randUUID32(), getRootKey()); } saveEntity(ge); setDataIdentity(entity, data, ge.getKey()); } } DataQuery query = createQuery(entity); convertDataToEntity(query, data, ge); saveEntity(ge); return data; } //-------------------------------------------------------------------- /** * update records by the supplied object and query * * @param data sample object * @param query where condition and update fields filter * @return updated count */ protected int updatesByQuery(Object data, DataQuery query, int limit) { if (log.isDebugEnabled()) { log.debug("updatesByQuery: " + query); } autoStart(); try { int cnt = 0; Entity entity = query.getEntity(); Object id = getQueryIdentity(query); if (id != null) { if (isValidIdentity(id)) { Key k = createKey(entity, id); com.google.appengine.api.datastore.Entity ge = getEntity(k); if (ge != null) { excludePrimaryKeys(query); convertDataToEntity(query, data, ge); saveEntity(ge); cnt = 1; } } } else { PreparedQuery pq = prepareQuery(query); FetchOptions fo = getFetchOptions(query); Iterator it = pq.asIterator(fo); excludePrimaryKeys(query); while (it.hasNext()) { com.google.appengine.api.datastore.Entity ge = it.next(); convertDataToEntity(query, data, ge); saveEntity(ge); if (++cnt > limit) { throw new DaoException("Too many (" + cnt + ") records updated."); } } } autoCommit(); return cnt; } catch (Exception e) { rollback(); throw new DaoException("Failed to update entity " + getTableName(query) + ": " + query, e); } finally { autoClose(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy