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

com.landawn.abacus.util.CouchbaseExecutor Maven / Gradle / Ivy

There is a newer version: 1.10.1
Show newest version
/*
 * Copyright (C) 2015 HaiYang Li
 *
 * 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.landawn.abacus.util;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.document.Document;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.query.QueryRow;
import com.landawn.abacus.DataSet;
import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.core.RowDataSet;
import com.landawn.abacus.exception.AbacusException;
import com.landawn.abacus.pool.KeyedObjectPool;
import com.landawn.abacus.pool.PoolFactory;
import com.landawn.abacus.pool.PoolableWrapper;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.u.Nullable;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalBoolean;
import com.landawn.abacus.util.u.OptionalByte;
import com.landawn.abacus.util.u.OptionalChar;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalFloat;
import com.landawn.abacus.util.u.OptionalInt;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.u.OptionalShort;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.ToBooleanFunction;
import com.landawn.abacus.util.function.ToByteFunction;
import com.landawn.abacus.util.function.ToCharFunction;
import com.landawn.abacus.util.function.ToDoubleFunction;
import com.landawn.abacus.util.function.ToFloatFunction;
import com.landawn.abacus.util.function.ToIntFunction;
import com.landawn.abacus.util.function.ToLongFunction;
import com.landawn.abacus.util.function.ToShortFunction;
import com.landawn.abacus.util.stream.Stream;

/**
 * It's a simple wrapper of Couchbase Java client.
 * Raw/ibatis(myBatis)/Couchbase style parameterized sql are supported. The parameters can be array/list/map/entity/JsonArray/JsonObject:
 * 
  • row parameterized sql with question mark: SELECT * FROM account WHERE accountId = ?
  • . *
  • ibatis/myBatis parameterized sql with named parameter: SELECT * FROM account WHERE accountId = #{accountId}
  • . *
  • Couchbase parameterized sql with named parameter: SELECT * FROM account WHERE accountId = $accountId or SELECT * FROM account WHERE accountId = $1
  • . * *
    *
    We recommend to define "id" property in java entity/bean as the object id in Couchbase to keep things as simple as possible.
    *
    * * @since 0.8 * * @author Haiyang Li */ public final class CouchbaseExecutor implements Closeable { /** * It's name of object id set in Map/Object array. */ public static final String _ID = "_id"; /** * Property name of id. */ public static final String ID = "id"; static final int POOLABLE_LENGTH = 1024; static final Set> supportedTypes = new HashSet<>(); static { supportedTypes.add(Boolean.class); supportedTypes.add(Integer.class); supportedTypes.add(Long.class); supportedTypes.add(Double.class); supportedTypes.add(String.class); supportedTypes.add(JsonObject.class); supportedTypes.add(JsonArray.class); } private static final Map, Method> classIdSetMethodPool = new ConcurrentHashMap<>(); private static final Map bucketIdNamePool = new ConcurrentHashMap<>(); private final KeyedObjectPool> stmtPool = PoolFactory.createKeyedObjectPool(1024, 3000); // private final KeyedObjectPool> preStmtPool = PoolFactory.createKeyedObjectPool(1024, 3000); private final Cluster cluster; private final Bucket bucket; private final SQLMapper sqlMapper; private final AsyncExecutor asyncExecutor; public CouchbaseExecutor(Cluster cluster) { this(cluster, cluster.openBucket()); } public CouchbaseExecutor(Cluster cluster, Bucket bucket) { this(cluster, bucket, null); } public CouchbaseExecutor(Cluster cluster, Bucket bucket, final SQLMapper sqlMapper) { this(cluster, bucket, sqlMapper, new AsyncExecutor(64, 300, TimeUnit.SECONDS)); } public CouchbaseExecutor(Cluster cluster, Bucket bucket, final SQLMapper sqlMapper, final AsyncExecutor asyncExecutor) { this.cluster = cluster; this.bucket = bucket; this.sqlMapper = sqlMapper; this.asyncExecutor = asyncExecutor; } public Cluster cluster() { return cluster; } public Bucket bucket() { return bucket; } /** * The object id ("_id") property will be read from/write to the specified property * @param cls * @param idPropertyName */ public static void registerIdProeprty(Class cls, String idPropertyName) { if (ClassUtil.getPropGetMethod(cls, idPropertyName) == null || ClassUtil.getPropSetMethod(cls, idPropertyName) == null) { throw new IllegalArgumentException("The specified class: " + ClassUtil.getCanonicalClassName(cls) + " doesn't have getter or setter method for the specified id propery: " + idPropertyName); } final Method setMethod = ClassUtil.getPropSetMethod(cls, idPropertyName); final Class parameterType = setMethod.getParameterTypes()[0]; if (!(String.class.isAssignableFrom(parameterType) || long.class.isAssignableFrom(parameterType) || Long.class.isAssignableFrom(parameterType))) { throw new IllegalArgumentException( "The parameter type of the specified id setter method must be 'String' or long/Long: " + setMethod.toGenericString()); } classIdSetMethodPool.put(cls, setMethod); } public static DataSet extractData(final QueryResult resultSet) { return extractData(Map.class, resultSet); } /** * * @param targetClass an entity class with getter/setter method or Map.class * @param resultSet * @return */ public static DataSet extractData(final Class targetClass, final QueryResult resultSet) { checkResultError(resultSet); checkTargetClass(targetClass); final List allRows = resultSet.allRows(); if (N.isNullOrEmpty(allRows)) { return N.newEmptyDataSet(); } final Set columnNames = new LinkedHashSet<>(); for (QueryRow row : allRows) { columnNames.addAll(row.value().getNames()); } final int rowCount = allRows.size(); final int columnCount = columnNames.size(); final List columnNameList = new ArrayList<>(columnNames); if (Map.class.isAssignableFrom(targetClass)) { final List> columnList = new ArrayList<>(columnCount); for (int i = 0; i < columnCount; i++) { columnList.add(new ArrayList<>(rowCount)); } JsonObject value = null; Object propValue = null; for (QueryRow row : allRows) { value = row.value(); for (int i = 0; i < columnCount; i++) { propValue = value.get(columnNameList.get(i)); if (propValue instanceof JsonObject) { columnList.get(i).add(((JsonObject) propValue).toMap()); } else if (propValue instanceof JsonArray) { columnList.get(i).add(((JsonArray) propValue).toList()); } else { columnList.get(i).add(propValue); } } } return new RowDataSet(columnNameList, columnList); } else { final List rowList = new ArrayList<>(rowCount); for (QueryRow row : allRows) { rowList.add(toEntity(targetClass, row.value())); } return N.newDataSet(columnNameList, rowList); } } /** * * @param targetClass an entity class with getter/setter method, Map.class or basic single value type(Primitive/String/Date...) * @param resultSet * @return */ public static List toList(Class targetClass, QueryResult resultSet) { checkResultError(resultSet); final Type type = N.typeOf(targetClass); final List rowList = resultSet.allRows(); if (N.isNullOrEmpty(rowList)) { return new ArrayList<>(); } final List resultList = new ArrayList<>(rowList.size()); if (targetClass.isAssignableFrom(JsonObject.class)) { for (QueryRow row : rowList) { resultList.add(row.value()); } } else if (type.isEntity() || type.isMap()) { for (QueryRow row : rowList) { resultList.add(toEntity(targetClass, row)); } } else { final JsonObject first = rowList.get(0).value(); if (first.getNames().size() == 1) { final String propName = first.getNames().iterator().next(); if (first.get(propName) != null && targetClass.isAssignableFrom(first.get(propName).getClass())) { for (QueryRow row : rowList) { resultList.add(row.value().get(propName)); } } else { for (QueryRow row : rowList) { resultList.add(N.convert(row.value().get(propName), targetClass)); } } } else { throw new IllegalArgumentException( "Can't covert result with columns: " + first.getNames().toString() + " to class: " + ClassUtil.getCanonicalClassName(targetClass)); } } return (List) resultList; } /** * * @param targetClass an entity class with getter/setter method or Map.class * @param row * @return */ public static T toEntity(final Class targetClass, final QueryRow row) { return toEntity(targetClass, row.value()); } /** * The id in the specified jsonDocument will be set to the returned object if and only if the id is not null or empty and the content in jsonDocument doesn't contain any "id" property, and it's acceptable to the targetClass. * * @param targetClass an entity class with getter/setter method or Map.class * @param jsonDocument * @return */ public static T toEntity(final Class targetClass, final JsonDocument jsonDocument) { final T result = toEntity(targetClass, jsonDocument.content()); final String id = jsonDocument.id(); if (N.notNullOrEmpty(id) && result != null) { if (Map.class.isAssignableFrom(targetClass)) { ((Map) result).put(_ID, id); } else { final Method idSetMethod = getObjectIdSetMethod(targetClass); if (idSetMethod != null) { ClassUtil.setPropValue(result, idSetMethod, id); } } } return result; } private static Method getObjectIdSetMethod(Class targetClass) { Method idSetMethod = classIdSetMethodPool.get(targetClass); if (idSetMethod == null) { // Method idPropSetMethod = N.getPropSetMethod(targetClass, ID); // Class parameterType = idPropSetMethod == null ? null : idPropSetMethod.getParameterTypes()[0]; // // if (parameterType != null && String.class.isAssignableFrom(parameterType)) { // idSetMethod = idPropSetMethod; // } // // if (idSetMethod == null) { // idSetMethod = N.METHOD_MASK; // } // // classIdSetMethodPool.put(targetClass, idSetMethod); classIdSetMethodPool.put(targetClass, ClassUtil.METHOD_MASK); } return idSetMethod == ClassUtil.METHOD_MASK ? null : idSetMethod; } /** * * @param targetClass an entity class with getter/setter method or Map.class * @param jsonObject * @return */ @SuppressWarnings("deprecation") public static T toEntity(final Class targetClass, final JsonObject jsonObject) { checkTargetClass(targetClass); if (jsonObject == null) { return null; } if (Map.class.isAssignableFrom(targetClass)) { final Map m = jsonObject.toMap(); if (targetClass.isAssignableFrom(m.getClass())) { return (T) m; } else { final Map result = (Map) N.newInstance(targetClass); result.putAll(m); return (T) result; } } else { final T entity = N.newInstance(targetClass); final List columnNameList = new ArrayList<>(jsonObject.getNames()); Method propSetMethod = null; Class parameterType = null; Object propValue = null; for (String propName : columnNameList) { propSetMethod = ClassUtil.getPropSetMethod(targetClass, propName); if (propSetMethod == null) { continue; } parameterType = propSetMethod.getParameterTypes()[0]; propValue = jsonObject.get(propName); if (propValue != null && !parameterType.isAssignableFrom(propValue.getClass())) { if (propValue instanceof JsonObject) { if (parameterType.isAssignableFrom(Map.class) || N.isEntity(parameterType)) { ClassUtil.setPropValue(entity, propSetMethod, toEntity(parameterType, (JsonObject) propValue)); } else { ClassUtil.setPropValue(entity, propSetMethod, N.valueOf(parameterType, N.stringOf(toEntity(Map.class, (JsonObject) propValue)))); } } else if (propValue instanceof JsonArray) { if (parameterType.isAssignableFrom(List.class)) { ClassUtil.setPropValue(entity, propSetMethod, ((JsonArray) propValue).toList()); } else { ClassUtil.setPropValue(entity, propSetMethod, N.valueOf(parameterType, N.stringOf(((JsonArray) propValue).toList()))); } } else { ClassUtil.setPropValue(entity, propSetMethod, propValue); } } else { ClassUtil.setPropValue(entity, propSetMethod, propValue); } } if (N.isDirtyMarker(entity.getClass())) { ((DirtyMarker) entity).markDirty(false); } return entity; } } public static String toJSON(JsonArray jsonArray) { return N.toJSON(jsonArray.toList()); } public static String toJSON(JsonObject jsonObject) { return N.toJSON(jsonObject.toMap()); } public static String toJSON(JsonDocument jsonDocument) { final Map m = jsonDocument.content().toMap(); if (N.notNullOrEmpty(jsonDocument.id())) { m.put(_ID, jsonDocument.id()); } return N.toJSON(m); } /** * Returns an instance of the specified target class with the property values from the specified JSON String. * * @param targetClass JsonArray.class, JsonObject.class or JsonDocument.class * @param json * @return */ public static T fromJSON(final Class targetClass, final String json) { if (targetClass.equals(JsonObject.class)) { return (T) JsonObject.from(N.fromJSON(Map.class, json)); } else if (targetClass.equals(JsonArray.class)) { return (T) JsonArray.from(N.fromJSON(List.class, json)); } else if (targetClass.equals(JsonDocument.class)) { final JsonObject jsonObject = JsonObject.from(N.fromJSON(Map.class, json)); final String id = N.stringOf(jsonObject.get(_ID)); jsonObject.removeKey(_ID); return (T) JsonDocument.create(id, jsonObject); } else { throw new IllegalArgumentException("Unsupported type: " + ClassUtil.getCanonicalClassName(targetClass)); } } /** * * @param obj an array of pairs of property name and value, or Map, or an entity with getter/setter methods. * @return */ public static JsonObject toJsonObject(final Object obj) { Map m = null; if (obj instanceof Map) { m = (Map) obj; } else if (N.isEntity(obj.getClass())) { m = Maps.entity2Map(obj); } else if (obj instanceof Object[]) { m = N.asProps(obj); } else { throw new IllegalArgumentException("The parameters must be a Map, or an entity class with getter/setter methods"); } final JsonObject result = JsonObject.create(); for (Map.Entry entry : m.entrySet()) { if (entry.getValue() == null || supportedTypes.contains(entry.getValue().getClass())) { result.put(entry.getKey(), entry.getValue()); } else { Type valueType = N.typeOf(entry.getValue().getClass()); if (valueType.isMap() || valueType.isEntity()) { result.put(entry.getKey(), toJsonObject(entry.getValue())); } else if (valueType.isObjectArray() || valueType.isCollection()) { result.put(entry.getKey(), toJsonArray(entry.getValue())); } else { result.put(entry.getKey(), N.stringOf(entry.getValue())); } } } return result; } @SafeVarargs public static JsonObject toJsonObject(final Object... a) { if (N.isNullOrEmpty(a)) { return JsonObject.empty(); } return a.length == 1 ? toJsonObject(a[0]) : toJsonObject((Object) a); } public static JsonArray toJsonArray(final Object obj) { final Type type = N.typeOf(obj.getClass()); final JsonArray jsonArray = JsonArray.create(); if (type.isObjectArray()) { for (Object e : (Object[]) obj) { if (e == null || supportedTypes.contains(e.getClass())) { jsonArray.add(e); } else { Type eType = N.typeOf(e.getClass()); if (eType.isMap() || eType.isEntity()) { jsonArray.add(toJsonObject(e)); } else if (eType.isObjectArray() || eType.isCollection()) { jsonArray.add(toJsonArray(e)); } else { jsonArray.add(N.stringOf(e)); } } } } else if (type.isCollection()) { for (Object e : (Collection) obj) { if (e == null || supportedTypes.contains(e.getClass())) { jsonArray.add(e); } else { Type eType = N.typeOf(e.getClass()); if (eType.isMap() || eType.isEntity()) { jsonArray.add(toJsonObject(e)); } else if (eType.isObjectArray() || eType.isCollection()) { jsonArray.add(toJsonArray(e)); } else { jsonArray.add(N.stringOf(e)); } } } } else { jsonArray.add(N.stringOf(obj)); } return jsonArray; } @SafeVarargs public static JsonArray toJsonArray(final Object... a) { return N.isNullOrEmpty(a) ? JsonArray.empty() : toJsonArray((Object) a); } /** * The id for the target document is got from the "id" property in the specified obj. * * @param obj an array of pairs of property name and value, or Map, or an entity with getter/setter methods. * @return * @throws IllegalArgumentException if the specified obj doesn't have any "id" property. */ public static JsonDocument toJsonDocument(final Object obj) { return toJsonDocument(obj, toJsonObject(obj)); } /** * The id for the target document is got from the "id" property in the specified a. * * @param a pairs of property name and value. * @return * @throws IllegalArgumentException if the specified a doesn't have any "id" property. */ @SafeVarargs public static JsonDocument toJsonDocument(final Object... a) { return a.length == 1 ? toJsonDocument(a[0], toJsonObject(a[0])) : toJsonDocument(a, toJsonObject(a)); } static JsonDocument toJsonDocument(final Object obj, final JsonObject jsonObject) { final Class cls = obj.getClass(); final Method idSetMethod = getObjectIdSetMethod(obj.getClass()); final String idPropertyName = N.isEntity(cls) ? (idSetMethod == null ? null : ClassUtil.getPropNameByMethod(idSetMethod)) : _ID; String id = null; if (idPropertyName != null && jsonObject.containsKey(idPropertyName)) { id = N.stringOf(jsonObject.get(idPropertyName)); jsonObject.removeKey(idPropertyName); } if (N.isNullOrEmpty(id)) { throw new IllegalArgumentException("No id property included the specified object: " + N.toString(jsonObject)); } return JsonDocument.create(id, jsonObject); } public static String idNameOf(final String bucketName) { String idName = bucketIdNamePool.get(bucketName); if (idName == null) { idName = "meta(" + bucketName + ").id".intern(); bucketIdNamePool.put(bucketName, idName); } return idName; } /** * * @param id * @return * * @see com.couchbase.client.java.Bucket#get(String) */ public Optional get(final String id) { return Optional.ofNullable(gett(id)); } /** * * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#get(String, long, TimeUnit) */ public Optional get(final String id, final long timeout, final TimeUnit timeUnit) { return Optional.ofNullable(gett(id, timeout, timeUnit)); } /** * * @param targetClass * @param id * @return * * @see com.couchbase.client.java.Bucket#get(String, Class) */ public Optional get(final Class targetClass, final String id) { return Optional.ofNullable(gett(targetClass, id)); } /** * * @param targetClass * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#get(String, Class, long, TimeUnit) */ public Optional get(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return Optional.ofNullable(gett(targetClass, id, timeout, timeUnit)); } /** * * @param id * @return * * @see com.couchbase.client.java.Bucket#get(String) */ public JsonDocument gett(final String id) { return bucket.get(id); } /** * * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#get(String, long, TimeUnit) */ public JsonDocument gett(final String id, final long timeout, final TimeUnit timeUnit) { return bucket.get(id, timeout, timeUnit); } /** * * @param targetClass * @param id * @return * * @see com.couchbase.client.java.Bucket#get(String, Class) */ public T gett(final Class targetClass, final String id) { return toEntityForGet(targetClass, gett(id)); } /** * * @param targetClass * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#get(String, Class, long, TimeUnit) */ public T gett(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return toEntityForGet(targetClass, gett(id, timeout, timeUnit)); } private T toEntityForGet(final Class targetClass, final JsonDocument doc) { if ((doc == null || doc.content() == null || doc.content().size() == 0)) { return null; } if (targetClass.isAssignableFrom(doc.getClass())) { return (T) doc; } return toEntity(targetClass, doc); } @SafeVarargs public final Optional findFirst(final Class targetClass, final String query, final Object... parameters) { final QueryResult resultSet = execute(query, parameters); final Iterator it = resultSet.rows(); final JsonObject jsonObject = it.hasNext() ? it.next().value() : null; if (jsonObject == null || jsonObject.size() == 0) { return Optional.empty(); } else { return Optional.of(toEntity(targetClass, jsonObject)); } } /** * * @param targetClass an entity class with getter/setter method, Map.class or basic single value type(Primitive/String/Date...) * @param query * @param parameters * @return */ @SafeVarargs public final List list(final Class targetClass, final String query, final Object... parameters) { final QueryResult resultSet = execute(query, parameters); return toList(targetClass, resultSet); } /** * Always remember to set "LIMIT 1" in the sql statement for better performance. * * @param query * @param parameters * @return */ @SafeVarargs public final boolean exists(final String query, final Object... parameters) { final QueryResult resultSet = execute(query, parameters); return resultSet.iterator().hasNext(); } @SafeVarargs public final long count(final String query, final Object... parameters) { return queryForSingleResult(long.class, query, parameters).orElse(0L); } @Beta @SafeVarargs public final OptionalBoolean queryForBoolean(final String query, final Object... parameters) { return queryForSingleResult(Boolean.class, query, parameters).mapToBoolean(ToBooleanFunction.UNBOX); } @Beta @SafeVarargs public final OptionalChar queryForChar(final String query, final Object... parameters) { return queryForSingleResult(Character.class, query, parameters).mapToChar(ToCharFunction.UNBOX); } @Beta @SafeVarargs public final OptionalByte queryForByte(final String query, final Object... parameters) { return queryForSingleResult(Byte.class, query, parameters).mapToByte(ToByteFunction.UNBOX); } @Beta @SafeVarargs public final OptionalShort queryForShort(final String query, final Object... parameters) { return queryForSingleResult(Short.class, query, parameters).mapToShort(ToShortFunction.UNBOX); } @Beta @SafeVarargs public final OptionalInt queryForInt(final String query, final Object... parameters) { return queryForSingleResult(Integer.class, query, parameters).mapToInt(ToIntFunction.UNBOX); } @Beta @SafeVarargs public final OptionalLong queryForLong(final String query, final Object... parameters) { return queryForSingleResult(Long.class, query, parameters).mapToLong(ToLongFunction.UNBOX); } @Beta @SafeVarargs public final OptionalFloat queryForFloat(final String query, final Object... parameters) { return queryForSingleResult(Float.class, query, parameters).mapToFloat(ToFloatFunction.UNBOX); } @Beta @SafeVarargs public final OptionalDouble queryForDouble(final String query, final Object... parameters) { return queryForSingleResult(Double.class, query, parameters).mapToDouble(ToDoubleFunction.UNBOX); } @Beta @SafeVarargs public final Nullable queryForString(final String query, final Object... parameters) { return this.queryForSingleResult(String.class, query, parameters); } @Beta @SafeVarargs public final Nullable queryForDate(final String query, final Object... parameters) { return this.queryForSingleResult(Date.class, query, parameters); } @Beta @SafeVarargs public final Nullable queryForDate(final Class targetClass, final String query, final Object... parameters) { return this.queryForSingleResult(targetClass, query, parameters); } @SafeVarargs public final Nullable queryForSingleResult(final Class targetClass, final String query, final Object... parameters) { final QueryResult resultSet = execute(query, parameters); final Iterator it = resultSet.rows(); final JsonObject jsonObject = it.hasNext() ? it.next().value() : null; if (jsonObject == null || jsonObject.size() == 0) { return Nullable.empty(); } else { return Nullable.of(N.convert(jsonObject.get(jsonObject.getNames().iterator().next()), targetClass)); } } @SafeVarargs public final DataSet query(final String query, final Object... parameters) { return extractData(execute(query, parameters)); } @SafeVarargs public final DataSet query(final Class targetClass, final String query, final Object... parameters) { return extractData(targetClass, execute(query, parameters)); } public DataSet query(final Query query) { return extractData(execute(query)); } public DataSet query(final Class targetClass, final Query query) { return extractData(targetClass, execute(query)); } public DataSet query(final Query query, final long timeout, final TimeUnit timeUnit) { return extractData(execute(query, timeout, timeUnit)); } public DataSet query(final Class targetClass, final Query query, final long timeout, final TimeUnit timeUnit) { return extractData(targetClass, execute(query, timeout, timeUnit)); } @SafeVarargs public final Stream stream(final String query, final Object... parameters) { return Stream.of(execute(query, parameters).rows()).map(new Function() { @Override public JsonObject apply(QueryRow t) { return t.value(); } }); } @SafeVarargs public final Stream stream(final Class targetClass, final String query, final Object... parameters) { return Stream.of(execute(query, parameters).rows()).map(new Function() { @Override public T apply(QueryRow t) { return toEntity(targetClass, t.value()); } }); } public Stream stream(final Query query) { return Stream.of(execute(query).rows()).map(new Function() { @Override public JsonObject apply(QueryRow t) { return t.value(); } }); } public Stream stream(final Class targetClass, final Query query) { return Stream.of(execute(query).rows()).map(new Function() { @Override public T apply(QueryRow t) { return toEntity(targetClass, t.value()); } }); } public Stream stream(final Query query, final long timeout, final TimeUnit timeUnit) { return Stream.of(execute(query, timeout, timeUnit).rows()).map(new Function() { @Override public JsonObject apply(QueryRow t) { return t.value(); } }); } public Stream stream(final Class targetClass, final Query query, final long timeout, final TimeUnit timeUnit) { return Stream.of(execute(query, timeout, timeUnit).rows()).map(new Function() { @Override public T apply(QueryRow t) { return toEntity(targetClass, t.value()); } }); } /** * * @param document * @return * * @see com.couchbase.client.java.Bucket#insert(Document) */ public T insert(final T document) { return (T) toEntityForUpdate(document.getClass(), bucket.insert(toDocument(document))); } /** * * @param document * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#insert(Document, long, TimeUnit) */ public T insert(final T document, final long timeout, final TimeUnit timeUnit) { return (T) toEntityForUpdate(document.getClass(), bucket.insert(toDocument(document), timeout, timeUnit)); } /** * All the signed properties will be updated/inserted into data store. * * @param document * @return * * @see com.couchbase.client.java.Bucket#upsert(Document) */ public T upsert(final T document) { return (T) toEntityForUpdate(document.getClass(), bucket.upsert(toDocument(document))); } /** * All the signed properties will be updated/inserted into data store. * * @param document * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#upsert(Document, long, TimeUnit) */ public T upsert(final T document, final long timeout, final TimeUnit timeUnit) { return (T) toEntityForUpdate(document.getClass(), bucket.upsert(toDocument(document), timeout, timeUnit)); } /** * * @param document * @return * * @see com.couchbase.client.java.Bucket#replace(Document) */ public T replace(final T document) { return (T) toEntityForUpdate(document.getClass(), bucket.replace(toDocument(document))); } /** * * @param document * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#replace(Document, long, TimeUnit) */ public T replace(final T document, final long timeout, final TimeUnit timeUnit) { return (T) toEntityForUpdate(document.getClass(), bucket.replace(toDocument(document), timeout, timeUnit)); } private Document toDocument(final Object obj) { final Class cls = obj.getClass(); if (Document.class.isAssignableFrom(cls)) { return (Document) obj; } else { return (Document) toJsonDocument(obj); } } private T toEntityForUpdate(Class cls, final Document document) { if (cls.isAssignableFrom(document.getClass())) { return (T) document; } else { return toEntity(cls, (JsonDocument) document); } } /** * * @param id * @return * * @see com.couchbase.client.java.Bucket#remove(String) */ public JsonDocument remove(String id) { return bucket.remove(id); } /** * * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#remove(String, long, TimeUnit) */ public JsonDocument remove(final String id, final long timeout, final TimeUnit timeUnit) { return bucket.remove(id, timeout, timeUnit); } /** * * @param targetClass * @param id * @return * * @see com.couchbase.client.java.Bucket#remove(String, Class) */ public T remove(final Class targetClass, final String id) { return toEntityForUpdate(targetClass, bucket.remove(id, JsonDocument.class)); } /** * * @param targetClass * @param id * @param timeout * @param timeUnit * @return * * @see com.couchbase.client.java.Bucket#remove(String, Class, long, TimeUnit) */ public T remove(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return toEntityForUpdate(targetClass, bucket.remove(id, JsonDocument.class, timeout, timeUnit)); } /** * * @param document * @return * * @see com.couchbase.client.java.Bucket#remove(Document) */ public T remove(final T document) { return (T) toEntityForUpdate(document.getClass(), bucket.remove(toDocument(document))); } /** * * @param document * @param timeout * @param timeUnit * @return * @see com.couchbase.client.java.Bucket#remove(Document, long, TimeUnit) */ public T remove(final T document, final long timeout, final TimeUnit timeUnit) { return (T) toEntityForUpdate(document.getClass(), bucket.remove(toDocument(document), timeout, timeUnit)); } public QueryResult execute(final String query) { return execute(prepareQuery(query)); } @SafeVarargs public final QueryResult execute(final String query, final Object... parameters) { return execute(prepareQuery(query, parameters)); } public QueryResult execute(final Query query) { final QueryResult resultSet = bucket.query(query); checkResultError(resultSet); return resultSet; } public QueryResult execute(final Query query, final long timeout, final TimeUnit timeUnit) { final QueryResult resultSet = bucket.query(query, timeout, timeUnit); checkResultError(resultSet); return resultSet; } public ContinuableFuture> asyncGet(final String id) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return get(id); } }); } public ContinuableFuture> asyncGet(final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return get(id, timeout, timeUnit); } }); } public ContinuableFuture> asyncGet(final Class targetClass, final String id) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return get(targetClass, id); } }); } public ContinuableFuture> asyncGet(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return get(targetClass, id, timeout, timeUnit); } }); } public ContinuableFuture asyncGett(final String id) { return asyncExecutor.execute(new Callable() { @Override public JsonDocument call() throws Exception { return gett(id); } }); } public ContinuableFuture asyncGett(final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public JsonDocument call() throws Exception { return gett(id, timeout, timeUnit); } }); } public ContinuableFuture asyncGett(final Class targetClass, final String id) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return gett(targetClass, id); } }); } public ContinuableFuture asyncGett(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return gett(targetClass, id, timeout, timeUnit); } }); } /** * Always remember to set "LIMIT 1" in the sql statement for better performance. * * @param query * @param parameters * @return */ @SafeVarargs public final ContinuableFuture asyncExists(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return exists(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncCount(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public Long call() throws Exception { return count(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForBoolean(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalBoolean call() throws Exception { return queryForBoolean(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForChar(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalChar call() throws Exception { return queryForChar(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForByte(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalByte call() throws Exception { return queryForByte(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForShort(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalShort call() throws Exception { return queryForShort(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForInt(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalInt call() throws Exception { return queryForInt(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForLong(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalLong call() throws Exception { return queryForLong(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForFloat(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalFloat call() throws Exception { return queryForFloat(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQueryForDouble(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public OptionalDouble call() throws Exception { return queryForDouble(query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncQueryForString(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return queryForString(query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncQueryForDate(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return queryForDate(query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncQueryForDate(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return queryForDate(targetClass, query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncQueryForSingleResult(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return queryForSingleResult(targetClass, query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncFindFirst(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return findFirst(targetClass, query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncList(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return list(targetClass, query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQuery(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(query, parameters); } }); } @SafeVarargs public final ContinuableFuture asyncQuery(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(targetClass, query, parameters); } }); } public ContinuableFuture asyncQuery(final Query query) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(query); } }); } public ContinuableFuture asyncQuery(final Class targetClass, final Query query) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(targetClass, query); } }); } public ContinuableFuture asyncQuery(final Query query, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(query, timeout, timeUnit); } }); } public ContinuableFuture asyncQuery(final Class targetClass, final Query query, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(targetClass, query, timeout, timeUnit); } }); } @SafeVarargs public final ContinuableFuture> asyncStream(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(query, parameters); } }); } @SafeVarargs public final ContinuableFuture> asyncStream(final Class targetClass, final String query, final Object... parameters) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(targetClass, query, parameters); } }); } public ContinuableFuture> asyncStream(final Query query) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(query); } }); } public ContinuableFuture> asyncStream(final Class targetClass, final Query query) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(targetClass, query); } }); } public ContinuableFuture> asyncStream(final Query query, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(query, timeout, timeUnit); } }); } public ContinuableFuture> asyncStream(final Class targetClass, final Query query, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable>() { @Override public Stream call() throws Exception { return stream(targetClass, query, timeout, timeUnit); } }); } public ContinuableFuture asyncInsert(final T document) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return insert(document); } }); } public ContinuableFuture asyncInsert(final T document, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return insert(document, timeout, timeUnit); } }); } public ContinuableFuture asyncUpsert(final T document) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return upsert(document); } }); } public ContinuableFuture asyncUpsert(final T document, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return upsert(document, timeout, timeUnit); } }); } public ContinuableFuture asyncReplace(final T document) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return replace(document); } }); } public ContinuableFuture asyncReplace(final T document, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return replace(document, timeout, timeUnit); } }); } public ContinuableFuture asyncRemove(final String id) { return asyncExecutor.execute(new Callable() { @Override public JsonDocument call() throws Exception { return remove(id); } }); } public ContinuableFuture asyncRemove(final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public JsonDocument call() throws Exception { return remove(id, timeout, timeUnit); } }); } public ContinuableFuture asyncRemove(final Class targetClass, final String id) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return remove(targetClass, id); } }); } public ContinuableFuture asyncRemove(final Class targetClass, final String id, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return remove(targetClass, id, timeout, timeUnit); } }); } public ContinuableFuture asyncRemove(final T document) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return remove(document); } }); } public ContinuableFuture asyncRemove(final T document, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return remove(document, timeout, timeUnit); } }); } public ContinuableFuture asyncExecute(final String query) { return asyncExecutor.execute(new Callable() { @Override public QueryResult call() throws Exception { return execute(query); } }); } @SafeVarargs public final ContinuableFuture asyncExecute(final String query, final Object... parameters) { return asyncExecutor.execute(new Callable() { @Override public QueryResult call() throws Exception { return execute(query, parameters); } }); } public ContinuableFuture asyncExecute(final Query query) { return asyncExecutor.execute(new Callable() { @Override public QueryResult call() throws Exception { return execute(query); } }); } public ContinuableFuture asyncExecute(final Query query, final long timeout, final TimeUnit timeUnit) { return asyncExecutor.execute(new Callable() { @Override public QueryResult call() throws Exception { return execute(query, timeout, timeUnit); } }); } private static void checkTargetClass(final Class targetClass) { if (!(N.isEntity(targetClass) || Map.class.isAssignableFrom(targetClass))) { throw new IllegalArgumentException("The target class must be an entity class with getter/setter methods or Map.class. But it is: " + ClassUtil.getCanonicalClassName(targetClass)); } } private static void checkResultError(QueryResult resultSet) { if (N.notNullOrEmpty(resultSet.errors())) { throw new AbacusException("Errors in query result: " + resultSet.errors()); } } private Query prepareQuery(final String query) { Query result = null; if (query.length() <= POOLABLE_LENGTH) { PoolableWrapper wrapper = stmtPool.get(query); if (wrapper != null) { result = wrapper.value(); } } if (result == null) { result = Query.simple(query); if (query.length() <= POOLABLE_LENGTH) { stmtPool.put(query, PoolableWrapper.of(result)); } } return result; } private Query prepareQuery(String query, Object... parameters) { if (N.isNullOrEmpty(parameters)) { return prepareQuery(query); } final NamedSQL namedSQL = getNamedSQL(query); final String sql = namedSQL.getPureSQL(true); final int parameterCount = namedSQL.getParameterCount(true); final List namedParameters = namedSQL.getNamedParameters(true); // Prepared query plan doens't work in Couchbase 4.0 Beta version? // QueryPlan queryPlan = null; // // if (query.length() <= POOLABLE_LENGTH) { // Wrapper wrapper = preStmtPool.get(query); // if (wrapper != null && wrapper.get() != null) { // queryPlan = wrapper.get(); // } // } // // if (queryPlan == null) { // queryPlan = bucket.prepare(sql); // // if (query.length() <= POOLABLE_LENGTH) { // preStmtPool.put(query, Wrapper.valueOf(queryPlan)); // } // } // if (parameterCount == 0) { return Query.simple(query); } else if (N.isNullOrEmpty(parameters)) { throw new IllegalArgumentException("Null or empty parameters for parameterized query: " + query); } // if (parameters.length == 1) { // if (parameters[0] instanceof JsonArray) { // return Query.parametrized(sql, (JsonArray) parameters[0]); // } else if (parameters[0] instanceof JsonObject) { // return Query.parametrized(sql, (JsonObject) parameters[0]); // } // } Object[] values = parameters; if (N.notNullOrEmpty(namedParameters) && parameters.length == 1 && (parameters[0] instanceof Map || parameters[0] instanceof JsonObject || N.isEntity(parameters[0].getClass()))) { values = new Object[parameterCount]; final Object parameter_0 = parameters[0]; String parameterName = null; if (parameter_0 instanceof Map) { @SuppressWarnings("unchecked") Map m = (Map) parameter_0; for (int i = 0; i < parameterCount; i++) { parameterName = namedParameters.get(i); values[i] = m.get(parameterName); if ((values[i] == null) && !m.containsKey(parameterName)) { throw new IllegalArgumentException("Parameter for property '" + parameterName + "' is missed"); } } } else if (parameter_0 instanceof JsonObject) { @SuppressWarnings("unchecked") JsonObject jsonObject = (JsonObject) parameter_0; for (int i = 0; i < parameterCount; i++) { parameterName = namedParameters.get(i); values[i] = jsonObject.get(parameterName); if ((values[i] == null) && !jsonObject.containsKey(parameterName)) { throw new IllegalArgumentException("Parameter for property '" + parameterName + "' is missed"); } } } else { Object entity = parameter_0; Class clazz = entity.getClass(); Method propGetMethod = null; for (int i = 0; i < parameterCount; i++) { parameterName = namedParameters.get(i); propGetMethod = ClassUtil.getPropGetMethod(clazz, parameterName); if (propGetMethod == null) { throw new IllegalArgumentException("Parameter for property '" + parameterName + "' is missed"); } values[i] = ClassUtil.invokeMethod(entity, propGetMethod); } } } else if ((parameters.length == 1) && (parameters[0] != null)) { if (parameters[0] instanceof Object[] && ((((Object[]) parameters[0]).length) >= parameterCount)) { values = (Object[]) parameters[0]; } else if (parameters[0] instanceof List && (((List) parameters[0]).size() >= parameterCount)) { final Collection c = (Collection) parameters[0]; values = c.toArray(new Object[c.size()]); } } if (values.length == 1 && values[0] instanceof JsonArray) { return Query.parametrized(sql, (JsonArray) values[0]); } else if (values.length > parameterCount) { return Query.parametrized(sql, JsonArray.from(N.copyOfRange(values, 0, parameterCount))); } else { return Query.parametrized(sql, JsonArray.from(values)); } } private NamedSQL getNamedSQL(String sql) { NamedSQL namedSQL = null; if (sqlMapper != null) { namedSQL = sqlMapper.get(sql); } if (namedSQL == null) { namedSQL = NamedSQL.parse(sql); } return namedSQL; } @Override public void close() throws IOException { try { bucket.close(); } finally { cluster.disconnect(); } } }