IGNITE-13519 Add thin client support to ignite-spring-data - Fixes #26.
Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com>
diff --git a/modules/spring-data-2.0-ext/pom.xml b/modules/spring-data-2.0-ext/pom.xml
index 00f7fa5..f096b93 100644
--- a/modules/spring-data-2.0-ext/pom.xml
+++ b/modules/spring-data-2.0-ext/pom.xml
@@ -149,6 +149,12 @@
<version>${ignite.version}</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-spring-data-commons</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<profiles>
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
index 7b44d19..2db7c25 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/Query.java
@@ -21,6 +21,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
/**
* Annotation to provide a user defined query for a method.
@@ -36,7 +38,10 @@
String value() default "";
/**
- * Whether annotated repository method must use TextQuery search.
+ * Whether annotated repository method must use TextQuery search. Note that text queries are not supported if
+ * {@link IgniteClient} is used for accessing the Ignite cluster, use {@link Ignite} node instance instead.
+ *
+ * @see RepositoryConfig#igniteInstance()
*/
boolean textQuery() default false;
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
index 359b75b..a156d81 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/RepositoryConfig.java
@@ -23,6 +23,8 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
/**
@@ -44,16 +46,14 @@
String cacheName() default "";
/**
- * Ignite instance string. Default "igniteInstance".
- *
- * @return {@link Ignite} instance spring bean name
+ * Name of the Spring Bean that must provide {@link Ignite} or {@link IgniteClient} instance for accessing the
+ * Ignite cluster.
*/
String igniteInstance() default "igniteInstance";
/**
- * Ignite cfg string. Default "igniteCfg".
- *
- * @return {@link IgniteConfiguration} spring bean name
+ * Name of the Spring Bean that must provide {@link IgniteConfiguration} or {@link ClientConfiguration} that is used
+ * for instantination of Ignite node or Ignite thin client respectively for accessing the Ignite cluster.
*/
String igniteCfg() default "igniteCfg";
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
index 96ff57b..03b0bb4 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/query/IgniteRepositoryQuery.java
@@ -17,6 +17,9 @@
package org.apache.ignite.springdata20.repository.query;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -32,27 +35,21 @@
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.cache.Cache;
import org.apache.commons.lang.ArrayUtils;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.binary.BinaryObjectBuilder;
-import org.apache.ignite.binary.BinaryType;
+import org.apache.commons.lang.reflect.FieldUtils;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.Query;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.cache.query.TextQuery;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.binary.BinaryUtils;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
-import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.cache.binary.IgniteBinaryImpl;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
-import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
-import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheClientProxy;
+import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.springdata20.repository.config.DynamicQueryConfig;
import org.apache.ignite.springdata20.repository.query.StringQuery.ParameterBinding;
@@ -75,6 +72,8 @@
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.StringUtils;
+import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME;
+import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME;
import static org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory.isFieldQuery;
/**
@@ -160,9 +159,6 @@
*/
@SuppressWarnings("unchecked")
public class IgniteRepositoryQuery implements RepositoryQuery {
- /** */
- private static final TreeMap<String, Class<?>> binaryFieldClass = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
/**
* Defines the way how to process query result
*/
@@ -201,20 +197,14 @@
/** */
private final Class<?> type;
+ /** Domain entitiy field descriptors mapped to their names in lower case. */
+ private final Map<String, Field> domainEntitiyFields;
+
/** */
private final IgniteQuery staticQuery;
/** */
- private final IgniteCache cache;
-
- /** */
- private final Ignite ignite;
-
- /** Required by qryStr field query type for binary manipulation */
- private IgniteBinaryImpl igniteBinary;
-
- /** */
- private BinaryType igniteBinType;
+ private final IgniteCacheProxy<?, ?> cache;
/** */
private final Method mtd;
@@ -258,7 +248,6 @@
/**
* Instantiates a new Ignite repository query.
*
- * @param ignite the ignite
* @param metadata Metadata.
* @param staticQuery Query.
* @param mtd Method.
@@ -267,12 +256,12 @@
* @param staticQueryConfiguration the query configuration
* @param queryMethodEvaluationContextProvider the query method evaluation context provider
*/
- public IgniteRepositoryQuery(Ignite ignite,
+ public IgniteRepositoryQuery(
RepositoryMetadata metadata,
@Nullable IgniteQuery staticQuery,
Method mtd,
ProjectionFactory factory,
- IgniteCache cache,
+ IgniteCacheProxy<? ,?> cache,
@Nullable DynamicQueryConfig staticQueryConfiguration,
EvaluationContextProvider queryMethodEvaluationContextProvider) {
this.metadata = metadata;
@@ -280,8 +269,10 @@
this.factory = factory;
type = metadata.getDomainType();
+ domainEntitiyFields = Arrays.stream(type.getDeclaredFields())
+ .collect(Collectors.toMap(field -> field.getName().toLowerCase(), field -> field));
+
this.cache = cache;
- this.ignite = ignite;
this.staticQueryConfiguration = staticQueryConfiguration;
this.staticQuery = staticQuery;
@@ -313,8 +304,6 @@
"When passing dynamicQuery = true via org.apache.ignite.springdata.repository.config.Query "
+ "annotation, you must provide a non null method parameter of type DynamicQueryConfig");
}
- // ensure domain class is registered on marshaller to transform row to entity
- registerClassOnMarshaller(((IgniteEx)ignite).context(), type);
}
/**
@@ -351,7 +340,20 @@
Query iQry = prepareQuery(qry, config, returnStgy, parameters);
- QueryCursor qryCursor = cache.query(iQry);
+ QueryCursor qryCursor;
+
+ try {
+ qryCursor = cache.query(iQry);
+ }
+ catch (IllegalArgumentException e) {
+ if (cache instanceof IgniteCacheClientProxy) {
+ throw new IllegalStateException(String.format("Query of type %s is not supported by thin client." +
+ " Check %s#%s method configuration or use Ignite node instance to connect to the Ignite cluster.",
+ iQry.getClass().getSimpleName(), mtd.getDeclaringClass().getName(), mtd.getName()), e);
+ }
+
+ throw e;
+ }
return transformQueryCursor(qry, returnStgy, parameters, qryCursor);
}
@@ -384,22 +386,6 @@
return index;
}
- /** */
- private synchronized IgniteBinaryImpl binary() {
- if (igniteBinary == null)
- igniteBinary = (IgniteBinaryImpl)ignite.binary();
-
- return igniteBinary;
- }
-
- /** */
- private synchronized BinaryType binType() {
- if (igniteBinType == null)
- igniteBinType = binary().type(type);
-
- return igniteBinType;
- }
-
/**
* @param mtd Method.
* @param isFieldQry Is field query.
@@ -561,15 +547,12 @@
// take control over single primite result from queries, i.e. DELETE, SELECT COUNT, UPDATE ...
boolean singlePrimitiveResult = isPrimitiveOrWrapper(returnClass);
- final List<GridQueryFieldMetadata> meta = ((QueryCursorEx)qryCursor).fieldsMeta();
+ FieldsQueryCursor<?> fieldQryCur = (FieldsQueryCursor<?>)qryCursor;
Function<List<?>, ?> cWrapperTransformFunction = null;
- if (type.equals(returnClass)) {
- IgniteBinaryImpl binary = binary();
- BinaryType binType = binType();
- cWrapperTransformFunction = row -> rowToEntity(binary, binType, row, meta);
- }
+ if (type.equals(returnClass))
+ cWrapperTransformFunction = row -> rowToEntity(row, fieldQryCur);
else {
if (hasProjection || singlePrimitiveResult) {
if (singlePrimitiveResult)
@@ -577,11 +560,11 @@
else {
// Map row -> projection class
cWrapperTransformFunction = row -> factory
- .createProjection(returnClass, rowToMap(row, meta));
+ .createProjection(returnClass, rowToMap(row, fieldQryCur));
}
}
else
- cWrapperTransformFunction = row -> rowToMap(row, meta);
+ cWrapperTransformFunction = row -> rowToMap(row, fieldQryCur);
}
QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<List<?>>)qryCursor,
@@ -613,17 +596,15 @@
}
}
else {
- Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
-
- Function<CacheEntryImpl, ?> cWrapperTransformFunction;
+ Function<Cache.Entry<?, ?>, ?> cWrapperTransformFunction;
if (hasProjection && !type.equals(returnClass))
cWrapperTransformFunction = row -> factory.createProjection(returnClass, row.getValue());
else
- cWrapperTransformFunction = row -> row.getValue();
+ cWrapperTransformFunction = Cache.Entry::getValue;
- QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<CacheEntryImpl>)qryCursor,
- cWrapperTransformFunction);
+ QueryCursorWrapper<Cache.Entry<?, ?>, ?> cWrapper = new QueryCursorWrapper<>(
+ (QueryCursor<Cache.Entry<?, ?>>)qryCursor, cWrapperTransformFunction);
switch (returnStgy) {
case PAGE_OF_VALUES:
@@ -641,7 +622,7 @@
}
return null;
case CACHE_ENTRY:
- Iterator<?> iter2 = qryIter.iterator();
+ Iterator<?> iter2 = qryCursor.iterator();
if (iter2.hasNext()) {
Object resp2 = iter2.next();
U.closeQuiet(qryCursor);
@@ -838,98 +819,59 @@
return query;
}
- /** */
- private static Map<String, Object> rowToMap(final List<?> row, final List<GridQueryFieldMetadata> meta) {
+ /**
+ * @param row SQL result row.
+ * @param cursor SQL query result cursor through which {@param row} was obtained.
+ * @return SQL result row values mapped to corresponding column names.
+ */
+ private static Map<String, Object> rowToMap(final List<?> row, FieldsQueryCursor<?> cursor) {
// use treemap with case insensitive property name
final TreeMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (int i = 0; i < meta.size(); i++) {
+
+ for (int i = 0; i < row.size(); i++) {
// don't want key or val columns
- final String metaField = meta.get(i).fieldName().toLowerCase();
- if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
- QueryUtils.VAL_FIELD_NAME))
- map.put(metaField, row.get(i));
+ final String fieldName = cursor.getFieldName(i).toLowerCase();
+
+ if (!KEY_FIELD_NAME.equalsIgnoreCase(fieldName) && !VAL_FIELD_NAME.equalsIgnoreCase(fieldName))
+ map.put(fieldName, row.get(i));
}
+
return map;
}
/**
* convert row ( with list of field values) into domain entity
+ *
+ * @param row SQL query result row.
+ * @param cursor SQL query result cursor through which {@param row} was obtained.
+ * @return Entitiy instance.
*/
- private <V> V rowToEntity(final IgniteBinaryImpl binary,
- final BinaryType binType,
- final List<?> row,
- final List<GridQueryFieldMetadata> meta) {
- // additional data returned by query not present on domain object type
- final TreeMap<String, Object> metadata = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- final BinaryObjectBuilder bldr = binary.builder(binType.typeName());
+ private <V> V rowToEntity(List<?> row, FieldsQueryCursor<?> cursor) {
+ Constructor<?> ctor;
- for (int i = 0; i < row.size(); i++) {
- final GridQueryFieldMetadata fMeta = meta.get(i);
- final String metaField = fMeta.fieldName();
- // add existing entity fields to binary object
- if (binType.field(fMeta.fieldName()) != null && !metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME)
- && !metaField.equalsIgnoreCase(QueryUtils.VAL_FIELD_NAME)) {
- final Object fieldValue = row.get(i);
- if (fieldValue != null) {
- final Class<?> clazz = getClassForBinaryField(binary, binType, fMeta);
- // null values must not be set into binary objects
- bldr.setField(metaField, fixExpectedType(fieldValue, clazz));
- }
- }
- else {
- // don't want key or val column... but wants null values
- if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
- QueryUtils.VAL_FIELD_NAME))
- metadata.put(metaField, row.get(i));
- }
- }
- return bldr.build().deserialize();
- }
-
- /**
- * Obtains real field class from resultset metadata field whether it's available
- */
- private Class<?> getClassForBinaryField(final IgniteBinaryImpl binary,
- final BinaryType binType,
- final GridQueryFieldMetadata fieldMeta) {
try {
- final String fieldId = fieldMeta.schemaName() + "." + fieldMeta.typeName() + "." + fieldMeta.fieldName();
+ ctor = type.getDeclaredConstructor();
- if (binaryFieldClass.containsKey(fieldId))
- return binaryFieldClass.get(fieldId);
+ ctor.setAccessible(true);
+ }
+ catch (NoSuchMethodException | SecurityException ignored) {
+ ctor = null;
+ }
- Class<?> clazz = null;
+ try {
+ Object res = ctor == null ? GridUnsafe.allocateInstance(type) : ctor.newInstance();
- synchronized (binaryFieldClass) {
+ for (int i = 0; i < row.size(); i++) {
+ Field entityField = domainEntitiyFields.get(cursor.getFieldName(i).toLowerCase());
- if (binaryFieldClass.containsKey(fieldId))
- return binaryFieldClass.get(fieldId);
-
- String fieldName = null;
-
- // search field name on binary type (query returns case insensitive
- // field name) but BinaryType is not case insensitive
- for (final String fname : binType.fieldNames()) {
- if (fname.equalsIgnoreCase(fieldMeta.fieldName())) {
- fieldName = fname;
- break;
- }
- }
-
- final CacheObjectBinaryProcessorImpl proc = (CacheObjectBinaryProcessorImpl)binary.processor();
-
- // search for class by typeId, if not found use
- // fieldMeta.fieldTypeName() class
- clazz = BinaryUtils.resolveClass(proc.binaryContext(), binary.typeId(binType.fieldTypeName(fieldName)),
- fieldMeta.fieldTypeName(), ignite.configuration().getClassLoader(), true);
-
- binaryFieldClass.put(fieldId, clazz);
+ if (entityField != null)
+ FieldUtils.writeField(entityField, res, fixExpectedType(row.get(i), entityField.getType()), true);
}
- return clazz;
+ return (V)res;
}
- catch (final Exception e) {
- return null;
+ catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new IgniteException("Unable to allocate instance of domain entity class " + type.getName(), e);
}
}
@@ -954,21 +896,6 @@
}
/**
- * @param ctx Context.
- * @param clazz Clazz.
- */
- private static void registerClassOnMarshaller(final GridKernalContext ctx, final Class<?> clazz) {
- try {
- // ensure class registration for marshaller on cluster...
- if (!U.isJdk(clazz))
- U.marshal(ctx, clazz.newInstance());
- }
- catch (final Exception ignored) {
- // silent
- }
- }
-
- /**
* Ignite QueryCursor wrapper.
* <p>
* Ensures closing underline cursor when there is no data.
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
index 87f637d..dacb3e4 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java
@@ -20,10 +20,15 @@
import java.util.Map;
import java.util.Optional;
import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
import org.apache.ignite.springdata20.repository.IgniteRepository;
import org.apache.ignite.springdata20.repository.config.DynamicQueryConfig;
import org.apache.ignite.springdata20.repository.config.Query;
@@ -73,7 +78,7 @@
private final Map<Class<?>, String> repoToCache = new HashMap<>();
/** Mapping of a repository to a ignite instance. */
- private final Map<Class<?>, Ignite> repoToIgnite = new HashMap<>();
+ private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>();
/**
* Creates the factory with initialized {@link Ignite} instance.
@@ -89,35 +94,51 @@
}
/** */
- private Ignite igniteForRepoConfig(RepositoryConfig config) {
+ private IgniteProxy igniteForRepoConfig(RepositoryConfig config) {
try {
- String igniteInstanceName = evaluateExpression(config.igniteInstance());
- return (Ignite)ctx.getBean(igniteInstanceName);
+ Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance()));
+
+ if (igniteInstanceBean instanceof Ignite)
+ return new IgniteProxyImpl((Ignite)igniteInstanceBean);
+ else if (igniteInstanceBean instanceof IgniteClient)
+ return new IgniteClientProxy((IgniteClient)igniteInstanceBean);
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+ " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+ Ignite.class.getName() + ", " + IgniteClient.class.getName());
}
catch (BeansException ex) {
try {
- String igniteConfigName = evaluateExpression(config.igniteCfg());
- IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean(igniteConfigName);
- try {
- // first try to attach to existing ignite instance
- return Ignition.ignite(cfg.getIgniteInstanceName());
+ Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg()));
+
+ if (igniteCfgBean instanceof IgniteConfiguration) {
+ try {
+ // first try to attach to existing ignite instance
+ return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName()));
+ }
+ catch (Exception ignored) {
+ // nop
+ }
+ return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean));
}
- catch (Exception ignored) {
- // nop
- }
- return Ignition.start(cfg);
+ else if (igniteCfgBean instanceof ClientConfiguration)
+ return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean));
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+ " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+ IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
+
}
catch (BeansException ex2) {
try {
String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath());
String path = (String)ctx.getBean(igniteSpringCfgPath);
- return Ignition.start(path);
+ return new IgniteProxyImpl(Ignition.start(path));
}
catch (BeansException ex3) {
- throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or"
- + " IgniteConfiguration or a path to Ignite's spring XML "
- + "configuration must be defined in the"
- + " application configuration");
+ throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" +
+ " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," +
+ " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class.");
}
}
}
@@ -176,14 +197,14 @@
}
/** Control underlying cache creation to avoid cache creation by mistake */
- private IgniteCache getRepositoryCache(Class<?> repoIf) {
- Ignite ignite = repoToIgnite.get(repoIf);
+ private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) {
+ IgniteProxy ignite = repoToIgnite.get(repoIf);
RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class);
String cacheName = repoToCache.get(repoIf);
- IgniteCache c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
+ IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
if (c == null) {
throw new IllegalStateException(
@@ -198,7 +219,7 @@
/** {@inheritDoc} */
@Override protected Object getTargetRepository(RepositoryInformation metadata) {
- Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
+ IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface());
return getTargetRepositoryViaReflection(metadata, ignite,
getRepositoryCache(metadata.getRepositoryInterface()));
@@ -209,8 +230,6 @@
EvaluationContextProvider evaluationContextProvider) {
return Optional.of((mtd, metadata, factory, namedQueries) -> {
final Query annotation = mtd.getAnnotation(Query.class);
- final Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
-
if (annotation != null && (StringUtils.hasText(annotation.value()) || annotation.textQuery() || annotation
.dynamicQuery())) {
@@ -224,7 +243,7 @@
annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null;
if (key != QueryLookupStrategy.Key.CREATE) {
- return new IgniteRepositoryQuery(ignite, metadata, query, mtd, factory,
+ return new IgniteRepositoryQuery(metadata, query, mtd, factory,
getRepositoryCache(metadata.getRepositoryInterface()),
annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null,
evaluationContextProvider);
@@ -237,7 +256,7 @@
+ ".config.Query annotation.");
}
- return new IgniteRepositoryQuery(ignite, metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
+ return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
factory, getRepositoryCache(metadata.getRepositoryInterface()),
DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider);
});
diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
index 50a7d4c..b089c22 100644
--- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java
@@ -29,7 +29,12 @@
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
import org.apache.ignite.springdata20.repository.IgniteRepository;
+import org.apache.ignite.springdata20.repository.config.RepositoryConfig;
import org.jetbrains.annotations.Nullable;
import org.springframework.context.annotation.Conditional;
@@ -44,15 +49,19 @@
*/
@Conditional(ConditionFalse.class)
public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> {
+ /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */
+ private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" +
+ " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance.";
+
/**
* Ignite Cache bound to the repository
*/
- private final IgniteCache<K, V> cache;
+ private final IgniteCacheProxy<K, V> cache;
/**
* Ignite instance bound to the repository
*/
- private final Ignite ignite;
+ private final IgniteProxy ignite;
/**
* Repository constructor.
@@ -60,19 +69,25 @@
* @param ignite the ignite
* @param cache Initialized cache instance.
*/
- public IgniteRepositoryImpl(Ignite ignite, IgniteCache<K, V> cache) {
+ public IgniteRepositoryImpl(IgniteProxy ignite, IgniteCacheProxy<K, V> cache) {
this.cache = cache;
this.ignite = ignite;
}
/** {@inheritDoc} */
@Override public IgniteCache<K, V> cache() {
- return cache;
+ if (cache instanceof IgniteCacheProxyImpl)
+ return ((IgniteCacheProxyImpl<K, V>)cache).delegate();
+
+ throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
}
/** {@inheritDoc} */
@Override public Ignite ignite() {
- return ignite;
+ if (ignite instanceof IgniteProxyImpl)
+ return ((IgniteProxyImpl)ignite).delegate();
+
+ throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
}
/** {@inheritDoc} */
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(CityRepository.class);
+ }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..ae42434
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.IgniteClientConfigRepository;
+import org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ }
+
+ /** Text queries are not supported when {@link IgniteClient} is used for acessing the Ignite cluster. */
+ @Override public void testUpdateQueryMixedCaseProjectionIndexedParameterLuceneTextQuery() {
+ GridTestUtils.assertThrows(log,
+ () -> repo.textQueryByFirstNameWithProjectionNamedParameter("person"), IllegalStateException.class,
+ "Query of type TextQuery is not supported by thin client. Check" +
+ " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" +
+ " method configuration or use Ignite node instance to connect to the Ignite cluster.");
+ }
+
+ /**
+ * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+ */
+ @Test
+ public void testRepositoryWithClientConfiguration() {
+ IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx);
+
+ IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class);
+
+ assertTrue(repo.count() > 0);
+ }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..27d3645
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryOtherIgniteInstance;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ repo2 = ctx.getBean(PersonSecondRepository.class);
+ repoTWO = ctx.getBean(PersonRepositoryOtherIgniteInstance.class);
+
+ for (int i = 0; i < CACHE_SIZE; i++) {
+ repo.save(i, new Person("person" + Integer.toHexString(i),
+ "lastName" + Integer.toHexString((i + 16) % 256)));
+ repoTWO.save(i, new Person("TWOperson" + Integer.toHexString(i),
+ "lastName" + Integer.toHexString((i + 16) % 256)));
+ }
+ }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index 6a69f32..06e0c28 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -36,10 +36,10 @@
* */
public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
/** Application context */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** City repository */
- private static CityRepository repo;
+ protected static CityRepository repo;
/** Cache name */
private static final String CACHE_NAME = "City";
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 38785ef..a543044 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -37,10 +37,10 @@
*/
public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** Number of entries to store */
private static int CACHE_SIZE = 1000;
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index 9c16f6d..0445e0d 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -20,6 +20,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import javax.cache.Cache;
import org.apache.ignite.springdata.misc.ApplicationConfiguration;
import org.apache.ignite.springdata.misc.Person;
@@ -39,23 +40,23 @@
*/
public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Repository 2. */
- private static PersonSecondRepository repo2;
+ protected static PersonSecondRepository repo2;
/**
* Repository Ignite Instance cluster TWO.
*/
- private static PersonRepositoryOtherIgniteInstance repoTWO;
+ protected static PersonRepositoryOtherIgniteInstance repoTWO;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/**
* Number of entries to store
*/
- private static int CACHE_SIZE = 1000;
+ protected static int CACHE_SIZE = 1000;
/**
* Performs context initialization before tests.
@@ -406,4 +407,35 @@
assertTrue(list.get(0) instanceof Integer);
}
}
+
+ /** Tests conversion of SQL select query result to domain entity objects. */
+ @Test
+ public void testRowToEntityConversion() {
+ Set<Person> res = new HashSet<>(repo.queryWithRowToEntityConversion());
+
+ Set<Person> exp = new HashSet<>();
+
+ repo.findAll().forEach(exp::add);
+
+ assertEquals(exp, res);
+ }
+
+ /**
+ * Tests conversion of SQL select query result to domain entity objects if result rows don't contain all fields
+ * of domain entity class.
+ */
+ @Test
+ public void testIncompleteRowToEntityConversion() {
+ Set<Person> res = new HashSet<>(repo.queryWithIncompleteRowToEntityConversion());
+
+ Set<Person> exp = new HashSet<>();
+
+ repo.findAll().forEach(p -> {
+ p.setSecondName(null);
+
+ exp.add(p);
+ });
+
+ assertEquals(exp, res);
+ }
}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..2122231
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.springdata.misc.SampleEvaluationContextExtension.SamplePassParamExtension;
+import org.apache.ignite.springdata20.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.repository.query.spi.EvaluationContextExtension;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_ONE;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_TWO;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+ /** Test cache name. */
+ public static final String CACHE_NAME = "PersonCache";
+
+ /** */
+ @Bean
+ public CacheNamesBean cacheNames() {
+ CacheNamesBean bean = new CacheNamesBean();
+
+ bean.setPersonCacheName(CACHE_NAME);
+
+ return bean;
+ }
+
+ /** */
+ @Bean
+ public EvaluationContextExtension sampleSpELExtension() {
+ return new SampleEvaluationContextExtension();
+ }
+
+ /** */
+ @Bean(value = "sampleExtensionBean")
+ public SamplePassParamExtension sampleExtensionBean() {
+ return new SamplePassParamExtension();
+ }
+
+ /** */
+ @Bean
+ public Ignite igniteServerNode() {
+ return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT));
+ }
+
+ /** Ignite client instance bean with default name. */
+ @Bean
+ public IgniteClient igniteInstance() {
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT)
+ .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false)));
+ }
+
+ /** Ignite client instance bean with non-default name. */
+ @Bean
+ public IgniteClient igniteInstanceTWO() {
+ Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801));
+
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801"));
+ }
+
+ /** Ignite client configuraition bean. */
+ @Bean
+ public ClientConfiguration clientConfiguration() {
+ return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+ }
+
+ /** Ingite configuration for server node. */
+ private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) {
+ return new IgniteConfiguration()
+ .setIgniteInstanceName(igniteInstanceName)
+ .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true)))
+ .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME).setIndexedTypes(Integer.class, Person.class))
+ .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(cliConnPort))
+ .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+ }
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
new file mode 100644
index 0000000..ccbfa08
--- /dev/null
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.misc;
+
+import java.io.Serializable;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata20.repository.IgniteRepository;
+import org.apache.ignite.springdata20.repository.config.RepositoryConfig;
+
+/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */
+@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration")
+public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> {
+ // No-op.
+}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
index 531c67f..62c3054 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
@@ -17,6 +17,7 @@
package org.apache.ignite.springdata.misc;
+import java.util.Date;
import java.util.Objects;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.cache.query.annotations.QueryTextField;
@@ -34,6 +35,10 @@
@QuerySqlField(index = true)
private String secondName;
+ /** Birthday. */
+ @QuerySqlField
+ private Date birthday;
+
/**
* @param firstName First name.
* @param secondName Second name.
@@ -41,6 +46,7 @@
public Person(String firstName, String secondName) {
this.firstName = firstName;
this.secondName = secondName;
+ birthday = new Date();
}
/**
@@ -76,6 +82,7 @@
return "Person{" +
"firstName='" + firstName + '\'' +
", secondName='" + secondName + '\'' +
+ ", birthday='" + birthday + '\'' +
'}';
}
@@ -90,11 +97,12 @@
Person person = (Person)o;
return Objects.equals(firstName, person.firstName) &&
- Objects.equals(secondName, person.secondName);
+ Objects.equals(secondName, person.secondName) &&
+ Objects.equals(birthday, person.birthday);
}
/** {@inheritDoc} */
@Override public int hashCode() {
- return Objects.hash(firstName, secondName);
+ return Objects.hash(firstName, secondName, birthday);
}
}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
index c50a2d0..7ea699b 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
@@ -145,4 +145,12 @@
/** Update using @Query but with errors on the query */
@Query("UPDATE Person SET secondName = ? WHERE firstName = ? AND ERRORS = 'ERRORS'")
public int setWrongFixedSecondName(String secondName, String firstName);
+
+ /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+ @Query(value = "SELECT firstName, secondName, birthday, _key, _val, NULL as one FROM Person", forceFieldsQuery = true)
+ public List<Person> queryWithRowToEntityConversion();
+
+ /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+ @Query(value = "SELECT firstName, birthday FROM Person", forceFieldsQuery = true)
+ public List<Person> queryWithIncompleteRowToEntityConversion();
}
diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
index 4efc48b..6c26078 100644
--- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
+++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java
@@ -17,6 +17,9 @@
package org.apache.ignite.testsuites;
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest;
import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
@@ -32,7 +35,10 @@
IgniteSpringDataCrudSelfTest.class,
IgniteSpringDataQueriesSelfTest.class,
IgniteSpringDataCrudSelfExpressionTest.class,
- IgniteSpringDataCompoundKeyTest.class
+ IgniteSpringDataCompoundKeyTest.class,
+ IgniteClientSpringDataCrudSelfTest.class,
+ IgniteClientSpringDataQueriesSelfTest.class,
+ IgniteClientSpringDataCompoundKeyTest.class
})
public class IgniteSpringData2TestSuite {
}
diff --git a/modules/spring-data-2.2-ext/pom.xml b/modules/spring-data-2.2-ext/pom.xml
index b0c00ba..6ac1b21 100644
--- a/modules/spring-data-2.2-ext/pom.xml
+++ b/modules/spring-data-2.2-ext/pom.xml
@@ -149,6 +149,12 @@
<version>${ignite.version}</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-spring-data-commons</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
index d1af467..1ea4bf3 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/Query.java
@@ -21,6 +21,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
/**
* Annotation to provide a user defined query for a method.
@@ -36,7 +38,10 @@
String value() default "";
/**
- * Whether annotated repository method must use TextQuery search.
+ * Whether annotated repository method must use TextQuery search. Note that text queries are not supported if
+ * {@link IgniteClient} is used for accessing the Ignite cluster, use {@link Ignite} node instance instead.
+ *
+ * @see RepositoryConfig#igniteInstance()
*/
boolean textQuery() default false;
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
index 6699ed6..e890052 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/RepositoryConfig.java
@@ -25,6 +25,8 @@
import java.lang.annotation.Target;
import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
/**
@@ -46,16 +48,14 @@
String cacheName() default "";
/**
- * Ignite instance string. Default "igniteInstance".
- *
- * @return {@link Ignite} instance spring bean name
+ * Name of the Spring Bean that must provide {@link Ignite} or {@link IgniteClient} instance for accessing the
+ * Ignite cluster.
*/
String igniteInstance() default "igniteInstance";
/**
- * Ignite cfg string. Default "igniteCfg".
- *
- * @return {@link IgniteConfiguration} spring bean name
+ * Name of the Spring Bean that must provide {@link IgniteConfiguration} or {@link ClientConfiguration} that is used
+ * for instantination of Ignite node or Ignite thin client respectively for accessing the Ignite cluster.
*/
String igniteCfg() default "igniteCfg";
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
index 9ab9606..56e5c4c 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/query/IgniteRepositoryQuery.java
@@ -17,6 +17,9 @@
package org.apache.ignite.springdata22.repository.query;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -32,27 +35,21 @@
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.cache.Cache;
import org.apache.commons.lang.ArrayUtils;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.binary.BinaryObjectBuilder;
-import org.apache.ignite.binary.BinaryType;
+import org.apache.commons.lang.reflect.FieldUtils;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.Query;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.cache.query.TextQuery;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.binary.BinaryUtils;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
-import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.cache.binary.IgniteBinaryImpl;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
-import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
-import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheClientProxy;
+import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.springdata22.repository.config.DynamicQueryConfig;
import org.apache.ignite.springdata22.repository.query.StringQuery.ParameterBinding;
@@ -75,6 +72,8 @@
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.StringUtils;
+import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME;
+import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME;
import static org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory.isFieldQuery;
/**
@@ -160,9 +159,6 @@
*/
@SuppressWarnings("unchecked")
public class IgniteRepositoryQuery implements RepositoryQuery {
- /** */
- private static final TreeMap<String, Class<?>> binaryFieldClass = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
/**
* Defines the way how to process query result
*/
@@ -201,20 +197,14 @@
/** */
private final Class<?> type;
+ /** Domain entitiy field descriptors mapped to their names in lower case. */
+ private final Map<String, Field> domainEntitiyFields;
+
/** */
private final IgniteQuery staticQuery;
/** */
- private final IgniteCache cache;
-
- /** */
- private final Ignite ignite;
-
- /** Required by qryStr field query type for binary manipulation */
- private IgniteBinaryImpl igniteBinary;
-
- /** */
- private BinaryType igniteBinType;
+ private final IgniteCacheProxy<?, ?> cache;
/** */
private final Method mtd;
@@ -258,7 +248,6 @@
/**
* Instantiates a new Ignite repository query.
*
- * @param ignite the ignite
* @param metadata Metadata.
* @param staticQuery Query.
* @param mtd Method.
@@ -267,12 +256,12 @@
* @param staticQueryConfiguration the query configuration
* @param queryMethodEvaluationContextProvider the query method evaluation context provider
*/
- public IgniteRepositoryQuery(Ignite ignite,
+ public IgniteRepositoryQuery(
RepositoryMetadata metadata,
@Nullable IgniteQuery staticQuery,
Method mtd,
ProjectionFactory factory,
- IgniteCache cache,
+ IgniteCacheProxy<? ,?> cache,
@Nullable DynamicQueryConfig staticQueryConfiguration,
QueryMethodEvaluationContextProvider queryMethodEvaluationContextProvider) {
this.metadata = metadata;
@@ -280,8 +269,10 @@
this.factory = factory;
type = metadata.getDomainType();
+ domainEntitiyFields = Arrays.stream(type.getDeclaredFields())
+ .collect(Collectors.toMap(field -> field.getName().toLowerCase(), field -> field));
+
this.cache = cache;
- this.ignite = ignite;
this.staticQueryConfiguration = staticQueryConfiguration;
this.staticQuery = staticQuery;
@@ -313,8 +304,6 @@
"When passing dynamicQuery = true via org.apache.ignite.springdata.repository.config.Query "
+ "annotation, you must provide a non null method parameter of type DynamicQueryConfig");
}
- // ensure domain class is registered on marshaller to transform row to entity
- registerClassOnMarshaller(((IgniteEx)ignite).context(), type);
}
/**
@@ -351,7 +340,20 @@
Query iQry = prepareQuery(qry, config, returnStgy, parameters);
- QueryCursor qryCursor = cache.query(iQry);
+ QueryCursor qryCursor;
+
+ try {
+ qryCursor = cache.query(iQry);
+ }
+ catch (IllegalArgumentException e) {
+ if (cache instanceof IgniteCacheClientProxy) {
+ throw new IllegalStateException(String.format("Query of type %s is not supported by thin client." +
+ " Check %s#%s method configuration or use Ignite node instance to connect to the Ignite cluster.",
+ iQry.getClass().getSimpleName(), mtd.getDeclaringClass().getName(), mtd.getName()), e);
+ }
+
+ throw e;
+ }
return transformQueryCursor(qry, returnStgy, parameters, qryCursor);
}
@@ -384,22 +386,6 @@
return index;
}
- /** */
- private synchronized IgniteBinaryImpl binary() {
- if (igniteBinary == null)
- igniteBinary = (IgniteBinaryImpl)ignite.binary();
-
- return igniteBinary;
- }
-
- /** */
- private synchronized BinaryType binType() {
- if (igniteBinType == null)
- igniteBinType = binary().type(type);
-
- return igniteBinType;
- }
-
/**
* @param mtd Method.
* @param isFieldQry Is field query.
@@ -561,15 +547,12 @@
// take control over single primite result from queries, i.e. DELETE, SELECT COUNT, UPDATE ...
boolean singlePrimitiveResult = isPrimitiveOrWrapper(returnClass);
- final List<GridQueryFieldMetadata> meta = ((QueryCursorEx)qryCursor).fieldsMeta();
+ FieldsQueryCursor<?> fieldQryCur = (FieldsQueryCursor<?>)qryCursor;
Function<List<?>, ?> cWrapperTransformFunction = null;
- if (type.equals(returnClass)) {
- IgniteBinaryImpl binary = binary();
- BinaryType binType = binType();
- cWrapperTransformFunction = row -> rowToEntity(binary, binType, row, meta);
- }
+ if (type.equals(returnClass))
+ cWrapperTransformFunction = row -> rowToEntity(row, fieldQryCur);
else {
if (hasProjection || singlePrimitiveResult) {
if (singlePrimitiveResult)
@@ -577,11 +560,11 @@
else {
// Map row -> projection class
cWrapperTransformFunction = row -> factory
- .createProjection(returnClass, rowToMap(row, meta));
+ .createProjection(returnClass, rowToMap(row, fieldQryCur));
}
}
else
- cWrapperTransformFunction = row -> rowToMap(row, meta);
+ cWrapperTransformFunction = row -> rowToMap(row, fieldQryCur);
}
QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<List<?>>)qryCursor,
@@ -613,17 +596,15 @@
}
}
else {
- Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
-
- Function<CacheEntryImpl, ?> cWrapperTransformFunction;
+ Function<Cache.Entry<?, ?>, ?> cWrapperTransformFunction;
if (hasProjection && !type.equals(returnClass))
cWrapperTransformFunction = row -> factory.createProjection(returnClass, row.getValue());
else
- cWrapperTransformFunction = row -> row.getValue();
+ cWrapperTransformFunction = Cache.Entry::getValue;
- QueryCursorWrapper<?, ?> cWrapper = new QueryCursorWrapper<>((QueryCursor<CacheEntryImpl>)qryCursor,
- cWrapperTransformFunction);
+ QueryCursorWrapper<Cache.Entry<?, ?>, ?> cWrapper = new QueryCursorWrapper<>(
+ (QueryCursor<Cache.Entry<?, ?>>)qryCursor, cWrapperTransformFunction);
switch (returnStgy) {
case PAGE_OF_VALUES:
@@ -641,7 +622,7 @@
}
return null;
case CACHE_ENTRY:
- Iterator<?> iter2 = qryIter.iterator();
+ Iterator<?> iter2 = qryCursor.iterator();
if (iter2.hasNext()) {
Object resp2 = iter2.next();
U.closeQuiet(qryCursor);
@@ -838,98 +819,59 @@
return query;
}
- /** */
- private static Map<String, Object> rowToMap(final List<?> row, final List<GridQueryFieldMetadata> meta) {
+ /**
+ * @param row SQL result row.
+ * @param cursor SQL query result cursor through which {@param row} was obtained.
+ * @return SQL result row values mapped to corresponding column names.
+ */
+ private static Map<String, Object> rowToMap(final List<?> row, FieldsQueryCursor<?> cursor) {
// use treemap with case insensitive property name
final TreeMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (int i = 0; i < meta.size(); i++) {
+
+ for (int i = 0; i < row.size(); i++) {
// don't want key or val columns
- final String metaField = meta.get(i).fieldName().toLowerCase();
- if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
- QueryUtils.VAL_FIELD_NAME))
- map.put(metaField, row.get(i));
+ final String fieldName = cursor.getFieldName(i).toLowerCase();
+
+ if (!KEY_FIELD_NAME.equalsIgnoreCase(fieldName) && !VAL_FIELD_NAME.equalsIgnoreCase(fieldName))
+ map.put(fieldName, row.get(i));
}
+
return map;
}
/**
* convert row ( with list of field values) into domain entity
+ *
+ * @param row SQL query result row.
+ * @param cursor SQL query result cursor through which {@param row} was obtained.
+ * @return Entitiy instance.
*/
- private <V> V rowToEntity(final IgniteBinaryImpl binary,
- final BinaryType binType,
- final List<?> row,
- final List<GridQueryFieldMetadata> meta) {
- // additional data returned by query not present on domain object type
- final TreeMap<String, Object> metadata = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- final BinaryObjectBuilder bldr = binary.builder(binType.typeName());
+ private <V> V rowToEntity(List<?> row, FieldsQueryCursor<?> cursor) {
+ Constructor<?> ctor;
- for (int i = 0; i < row.size(); i++) {
- final GridQueryFieldMetadata fMeta = meta.get(i);
- final String metaField = fMeta.fieldName();
- // add existing entity fields to binary object
- if (binType.field(fMeta.fieldName()) != null && !metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME)
- && !metaField.equalsIgnoreCase(QueryUtils.VAL_FIELD_NAME)) {
- final Object fieldValue = row.get(i);
- if (fieldValue != null) {
- final Class<?> clazz = getClassForBinaryField(binary, binType, fMeta);
- // null values must not be set into binary objects
- bldr.setField(metaField, fixExpectedType(fieldValue, clazz));
- }
- }
- else {
- // don't want key or val column... but wants null values
- if (!metaField.equalsIgnoreCase(QueryUtils.KEY_FIELD_NAME) && !metaField.equalsIgnoreCase(
- QueryUtils.VAL_FIELD_NAME))
- metadata.put(metaField, row.get(i));
- }
- }
- return bldr.build().deserialize();
- }
-
- /**
- * Obtains real field class from resultset metadata field whether it's available
- */
- private Class<?> getClassForBinaryField(final IgniteBinaryImpl binary,
- final BinaryType binType,
- final GridQueryFieldMetadata fieldMeta) {
try {
- final String fieldId = fieldMeta.schemaName() + "." + fieldMeta.typeName() + "." + fieldMeta.fieldName();
+ ctor = type.getDeclaredConstructor();
- if (binaryFieldClass.containsKey(fieldId))
- return binaryFieldClass.get(fieldId);
+ ctor.setAccessible(true);
+ }
+ catch (NoSuchMethodException | SecurityException ignored) {
+ ctor = null;
+ }
- Class<?> clazz = null;
+ try {
+ Object res = ctor == null ? GridUnsafe.allocateInstance(type) : ctor.newInstance();
- synchronized (binaryFieldClass) {
+ for (int i = 0; i < row.size(); i++) {
+ Field entityField = domainEntitiyFields.get(cursor.getFieldName(i).toLowerCase());
- if (binaryFieldClass.containsKey(fieldId))
- return binaryFieldClass.get(fieldId);
-
- String fieldName = null;
-
- // search field name on binary type (query returns case insensitive
- // field name) but BinaryType is not case insensitive
- for (final String fname : binType.fieldNames()) {
- if (fname.equalsIgnoreCase(fieldMeta.fieldName())) {
- fieldName = fname;
- break;
- }
- }
-
- final CacheObjectBinaryProcessorImpl proc = (CacheObjectBinaryProcessorImpl)binary.processor();
-
- // search for class by typeId, if not found use
- // fieldMeta.fieldTypeName() class
- clazz = BinaryUtils.resolveClass(proc.binaryContext(), binary.typeId(binType.fieldTypeName(fieldName)),
- fieldMeta.fieldTypeName(), ignite.configuration().getClassLoader(), true);
-
- binaryFieldClass.put(fieldId, clazz);
+ if (entityField != null)
+ FieldUtils.writeField(entityField, res, fixExpectedType(row.get(i), entityField.getType()), true);
}
- return clazz;
+ return (V)res;
}
- catch (final Exception e) {
- return null;
+ catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new IgniteException("Unable to allocate instance of domain entity class " + type.getName(), e);
}
}
@@ -954,21 +896,6 @@
}
/**
- * @param ctx Context.
- * @param clazz Clazz.
- */
- private static void registerClassOnMarshaller(final GridKernalContext ctx, final Class<?> clazz) {
- try {
- // ensure class registration for marshaller on cluster...
- if (!U.isJdk(clazz))
- U.marshal(ctx, clazz.newInstance());
- }
- catch (final Exception ignored) {
- // silent
- }
- }
-
- /**
* Ignite QueryCursor wrapper.
* <p>
* Ensures closing underline cursor when there is no data.
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
index 97d31e9..a9c7ad2 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java
@@ -20,10 +20,15 @@
import java.util.Map;
import java.util.Optional;
import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
import org.apache.ignite.springdata22.repository.IgniteRepository;
import org.apache.ignite.springdata22.repository.config.DynamicQueryConfig;
import org.apache.ignite.springdata22.repository.config.Query;
@@ -73,7 +78,7 @@
private final Map<Class<?>, String> repoToCache = new HashMap<>();
/** Mapping of a repository to a ignite instance. */
- private final Map<Class<?>, Ignite> repoToIgnite = new HashMap<>();
+ private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>();
/**
* Creates the factory with initialized {@link Ignite} instance.
@@ -89,35 +94,51 @@
}
/** */
- private Ignite igniteForRepoConfig(RepositoryConfig config) {
+ private IgniteProxy igniteForRepoConfig(RepositoryConfig config) {
try {
- String igniteInstanceName = evaluateExpression(config.igniteInstance());
- return (Ignite)ctx.getBean(igniteInstanceName);
+ Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance()));
+
+ if (igniteInstanceBean instanceof Ignite)
+ return new IgniteProxyImpl((Ignite)igniteInstanceBean);
+ else if (igniteInstanceBean instanceof IgniteClient)
+ return new IgniteClientProxy((IgniteClient)igniteInstanceBean);
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+ " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+ Ignite.class.getName() + ", " + IgniteClient.class.getName());
}
catch (BeansException ex) {
try {
- String igniteConfigName = evaluateExpression(config.igniteCfg());
- IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean(igniteConfigName);
- try {
- // first try to attach to existing ignite instance
- return Ignition.ignite(cfg.getIgniteInstanceName());
+ Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg()));
+
+ if (igniteCfgBean instanceof IgniteConfiguration) {
+ try {
+ // first try to attach to existing ignite instance
+ return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName()));
+ }
+ catch (Exception ignored) {
+ // nop
+ }
+ return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean));
}
- catch (Exception ignored) {
- // nop
- }
- return Ignition.start(cfg);
+ else if (igniteCfgBean instanceof ClientConfiguration)
+ return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean));
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+ " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+ IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
+
}
catch (BeansException ex2) {
try {
String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath());
String path = (String)ctx.getBean(igniteSpringCfgPath);
- return Ignition.start(path);
+ return new IgniteProxyImpl(Ignition.start(path));
}
catch (BeansException ex3) {
- throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or"
- + " IgniteConfiguration or a path to Ignite's spring XML "
- + "configuration must be defined in the"
- + " application configuration");
+ throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" +
+ " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," +
+ " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class.");
}
}
}
@@ -176,14 +197,14 @@
}
/** Control underlying cache creation to avoid cache creation by mistake */
- private IgniteCache getRepositoryCache(Class<?> repoIf) {
- Ignite ignite = repoToIgnite.get(repoIf);
+ private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) {
+ IgniteProxy ignite = repoToIgnite.get(repoIf);
RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class);
String cacheName = repoToCache.get(repoIf);
- IgniteCache c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
+ IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName);
if (c == null) {
throw new IllegalStateException(
@@ -198,7 +219,7 @@
/** {@inheritDoc} */
@Override protected Object getTargetRepository(RepositoryInformation metadata) {
- Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
+ IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface());
return getTargetRepositoryViaReflection(metadata, ignite,
getRepositoryCache(metadata.getRepositoryInterface()));
@@ -209,8 +230,6 @@
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of((mtd, metadata, factory, namedQueries) -> {
final Query annotation = mtd.getAnnotation(Query.class);
- final Ignite ignite = repoToIgnite.get(metadata.getRepositoryInterface());
-
if (annotation != null && (StringUtils.hasText(annotation.value()) || annotation.textQuery() || annotation
.dynamicQuery())) {
@@ -224,7 +243,7 @@
annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null;
if (key != QueryLookupStrategy.Key.CREATE) {
- return new IgniteRepositoryQuery(ignite, metadata, query, mtd, factory,
+ return new IgniteRepositoryQuery(metadata, query, mtd, factory,
getRepositoryCache(metadata.getRepositoryInterface()),
annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null,
evaluationContextProvider);
@@ -237,7 +256,7 @@
+ ".config.Query annotation.");
}
- return new IgniteRepositoryQuery(ignite, metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
+ return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd,
factory, getRepositoryCache(metadata.getRepositoryInterface()),
DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider);
});
diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
index c4d9834..de0573b 100644
--- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java
@@ -29,7 +29,12 @@
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
import org.jetbrains.annotations.Nullable;
import org.springframework.context.annotation.Conditional;
@@ -44,15 +49,19 @@
*/
@Conditional(ConditionFalse.class)
public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> {
+ /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */
+ private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" +
+ " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance.";
+
/**
* Ignite Cache bound to the repository
*/
- private final IgniteCache<K, V> cache;
+ private final IgniteCacheProxy<K, V> cache;
/**
* Ignite instance bound to the repository
*/
- private final Ignite ignite;
+ private final IgniteProxy ignite;
/**
* Repository constructor.
@@ -60,19 +69,25 @@
* @param ignite the ignite
* @param cache Initialized cache instance.
*/
- public IgniteRepositoryImpl(Ignite ignite, IgniteCache<K, V> cache) {
+ public IgniteRepositoryImpl(IgniteProxy ignite, IgniteCacheProxy<K, V> cache) {
this.cache = cache;
this.ignite = ignite;
}
/** {@inheritDoc} */
@Override public IgniteCache<K, V> cache() {
- return cache;
+ if (cache instanceof IgniteCacheProxyImpl)
+ return ((IgniteCacheProxyImpl<K, V>)cache).delegate();
+
+ throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
}
/** {@inheritDoc} */
@Override public Ignite ignite() {
- return ignite;
+ if (ignite instanceof IgniteProxyImpl)
+ return ((IgniteProxyImpl)ignite).delegate();
+
+ throw new UnsupportedOperationException(UNSUPPORTED_ERR_MSG);
}
/** {@inheritDoc} */
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(CityRepository.class);
+ }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..d1597d9
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.IgniteClientConfigRepository;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ }
+
+ /** Text queries are not supported when {@link IgniteClient} is used for acessing the Ignite cluster. */
+ @Override public void testUpdateQueryMixedCaseProjectionIndexedParameterLuceneTextQuery() {
+ GridTestUtils.assertThrows(log,
+ () -> repo.textQueryByFirstNameWithProjectionNamedParameter("person"), IllegalStateException.class,
+ "Query of type TextQuery is not supported by thin client. Check" +
+ " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" +
+ " method configuration or use Ignite node instance to connect to the Ignite cluster.");
+ }
+
+ /**
+ * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+ */
+ @Test
+ public void testRepositoryWithClientConfiguration() {
+ IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx);
+
+ IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class);
+
+ assertTrue(repo.count() > 0);
+ }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..27d3645
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryOtherIgniteInstance;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ repo2 = ctx.getBean(PersonSecondRepository.class);
+ repoTWO = ctx.getBean(PersonRepositoryOtherIgniteInstance.class);
+
+ for (int i = 0; i < CACHE_SIZE; i++) {
+ repo.save(i, new Person("person" + Integer.toHexString(i),
+ "lastName" + Integer.toHexString((i + 16) % 256)));
+ repoTWO.save(i, new Person("TWOperson" + Integer.toHexString(i),
+ "lastName" + Integer.toHexString((i + 16) % 256)));
+ }
+ }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index c4f9c0e..06e0c28 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -29,15 +29,17 @@
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
/**
* Test with using conpoud key in spring-data
* */
public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
/** Application context */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** City repository */
- private static CityRepository repo;
+ protected static CityRepository repo;
/** Cache name */
private static final String CACHE_NAME = "City";
@@ -101,7 +103,7 @@
if (ignite.cacheNames().contains(CACHE_NAME))
ignite.destroyCache(CACHE_NAME);
- try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:" + CompoundKeyApplicationConfiguration.CLI_CONN_PORT + '/')) {
+ try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:" + CLI_CONN_PORT + '/')) {
Statement st = conn.createStatement();
st.execute("DROP TABLE IF EXISTS City");
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 38785ef..a543044 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -37,10 +37,10 @@
*/
public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** Number of entries to store */
private static int CACHE_SIZE = 1000;
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index d35c781..19a9f63 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -20,6 +20,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import javax.cache.Cache;
import org.apache.ignite.springdata.misc.ApplicationConfiguration;
import org.apache.ignite.springdata.misc.Person;
@@ -39,23 +40,23 @@
*/
public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Repository 2. */
- private static PersonSecondRepository repo2;
+ protected static PersonSecondRepository repo2;
/**
* Repository Ignite Instance cluster TWO.
*/
- private static PersonRepositoryOtherIgniteInstance repoTWO;
+ protected static PersonRepositoryOtherIgniteInstance repoTWO;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/**
* Number of entries to store
*/
- private static int CACHE_SIZE = 1000;
+ protected static int CACHE_SIZE = 1000;
/**
* Performs context initialization before tests.
@@ -406,4 +407,35 @@
assertTrue(list.get(0) instanceof Integer);
}
}
+
+ /** Tests conversion of SQL select query result to domain entity objects. */
+ @Test
+ public void testRowToEntityConversion() {
+ Set<Person> res = new HashSet<>(repo.queryWithRowToEntityConversion());
+
+ Set<Person> exp = new HashSet<>();
+
+ repo.findAll().forEach(exp::add);
+
+ assertEquals(exp, res);
+ }
+
+ /**
+ * Tests conversion of SQL select query result to domain entity objects if result rows don't contain all fields
+ * of domain entity class.
+ */
+ @Test
+ public void testIncompleteRowToEntityConversion() {
+ Set<Person> res = new HashSet<>(repo.queryWithIncompleteRowToEntityConversion());
+
+ Set<Person> exp = new HashSet<>();
+
+ repo.findAll().forEach(p -> {
+ p.setSecondName(null);
+
+ exp.add(p);
+ });
+
+ assertEquals(exp, res);
+ }
}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..c5d159a
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.springdata.misc.SampleEvaluationContextExtension.SamplePassParamExtension;
+import org.apache.ignite.springdata22.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.spel.spi.EvaluationContextExtension;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_ONE;
+import static org.apache.ignite.springdata.misc.ApplicationConfiguration.IGNITE_INSTANCE_TWO;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+ /** Test cache name. */
+ public static final String CACHE_NAME = "PersonCache";
+
+ /** */
+ @Bean
+ public CacheNamesBean cacheNames() {
+ CacheNamesBean bean = new CacheNamesBean();
+
+ bean.setPersonCacheName(CACHE_NAME);
+
+ return bean;
+ }
+
+ /** */
+ @Bean
+ public EvaluationContextExtension sampleSpELExtension() {
+ return new SampleEvaluationContextExtension();
+ }
+
+ /** */
+ @Bean(value = "sampleExtensionBean")
+ public SamplePassParamExtension sampleExtensionBean() {
+ return new SamplePassParamExtension();
+ }
+
+ /** */
+ @Bean
+ public Ignite igniteServerNode() {
+ return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT));
+ }
+
+ /** Ignite client instance bean with default name. */
+ @Bean
+ public IgniteClient igniteInstance() {
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT)
+ .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false)));
+ }
+
+ /** Ignite client instance bean with non-default name. */
+ @Bean
+ public IgniteClient igniteInstanceTWO() {
+ Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801));
+
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801"));
+ }
+
+ /** Ignite client configuraition bean. */
+ @Bean
+ public ClientConfiguration clientConfiguration() {
+ return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+ }
+
+ /** Ingite configuration for server node. */
+ private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) {
+ return new IgniteConfiguration()
+ .setIgniteInstanceName(igniteInstanceName)
+ .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true)))
+ .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME).setIndexedTypes(Integer.class, Person.class))
+ .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(cliConnPort))
+ .setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+ }
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
new file mode 100644
index 0000000..23a9d8e
--- /dev/null
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.misc;
+
+import java.io.Serializable;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.springdata22.repository.IgniteRepository;
+import org.apache.ignite.springdata22.repository.config.RepositoryConfig;
+
+/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */
+@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration")
+public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> {
+ // No-op.
+}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
index 531c67f..62c3054 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/Person.java
@@ -17,6 +17,7 @@
package org.apache.ignite.springdata.misc;
+import java.util.Date;
import java.util.Objects;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.cache.query.annotations.QueryTextField;
@@ -34,6 +35,10 @@
@QuerySqlField(index = true)
private String secondName;
+ /** Birthday. */
+ @QuerySqlField
+ private Date birthday;
+
/**
* @param firstName First name.
* @param secondName Second name.
@@ -41,6 +46,7 @@
public Person(String firstName, String secondName) {
this.firstName = firstName;
this.secondName = secondName;
+ birthday = new Date();
}
/**
@@ -76,6 +82,7 @@
return "Person{" +
"firstName='" + firstName + '\'' +
", secondName='" + secondName + '\'' +
+ ", birthday='" + birthday + '\'' +
'}';
}
@@ -90,11 +97,12 @@
Person person = (Person)o;
return Objects.equals(firstName, person.firstName) &&
- Objects.equals(secondName, person.secondName);
+ Objects.equals(secondName, person.secondName) &&
+ Objects.equals(birthday, person.birthday);
}
/** {@inheritDoc} */
@Override public int hashCode() {
- return Objects.hash(firstName, secondName);
+ return Objects.hash(firstName, secondName, birthday);
}
}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
index 0a5826f..4fd3e07 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/PersonRepository.java
@@ -148,4 +148,12 @@
/** Update using @Query but with errors on the query */
@Query("UPDATE Person SET secondName = ? WHERE firstName = ? AND ERRORS = 'ERRORS'")
public int setWrongFixedSecondName(String secondName, String firstName);
+
+ /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+ @Query(value = "SELECT firstName, secondName, birthday, _key, _val, NULL as one FROM Person", forceFieldsQuery = true)
+ public List<Person> queryWithRowToEntityConversion();
+
+ /** Produces a list of domain entity classes whose fields are obtained from the query result row. */
+ @Query(value = "SELECT firstName, birthday FROM Person", forceFieldsQuery = true)
+ public List<Person> queryWithIncompleteRowToEntityConversion();
}
diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
index 872fe9f..4363f74 100644
--- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
+++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java
@@ -17,6 +17,9 @@
package org.apache.ignite.testsuites;
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest;
import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
@@ -32,7 +35,10 @@
IgniteSpringDataCrudSelfTest.class,
IgniteSpringDataQueriesSelfTest.class,
IgniteSpringDataCrudSelfExpressionTest.class,
- IgniteSpringDataCompoundKeyTest.class
+ IgniteSpringDataCompoundKeyTest.class,
+ IgniteClientSpringDataCrudSelfTest.class,
+ IgniteClientSpringDataQueriesSelfTest.class,
+ IgniteClientSpringDataCompoundKeyTest.class
})
public class IgniteSpringData22TestSuite {
}
diff --git a/modules/spring-data-commons/licenses/apache-2.0.txt b/modules/spring-data-commons/licenses/apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/modules/spring-data-commons/licenses/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/modules/spring-data-commons/pom.xml b/modules/spring-data-commons/pom.xml
new file mode 100644
index 0000000..5e9be93
--- /dev/null
+++ b/modules/spring-data-commons/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<!--
+ POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-extensions-parent</artifactId>
+ <version>1</version>
+ <relativePath>../../parent</relativePath>
+ </parent>
+
+ <artifactId>ignite-spring-data-commons</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <url>http://ignite.apache.org</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-core</artifactId>
+ <version>${ignite.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java
new file mode 100644
index 0000000..10fef14
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheClientProxy.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.client.ClientCache;
+import org.apache.ignite.client.ClientException;
+import org.jetbrains.annotations.NotNull;
+
+/** Implementation of {@link IgniteCacheProxy} that provides access to Ignite cache through {@link ClientCache} instance. */
+public class IgniteCacheClientProxy<K, V> implements IgniteCacheProxy<K, V> {
+ /** {@link ClientCache} instance to which operations are delegated. */
+ private final ClientCache<K, V> cache;
+
+ /** */
+ public IgniteCacheClientProxy(ClientCache<K, V> cache) {
+ this.cache = cache;
+ }
+
+ /** {@inheritDoc} */
+ @Override public V get(K key) throws ClientException {
+ return cache.get(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void put(K key, V val) throws ClientException {
+ cache.put(key, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size(CachePeekMode... peekModes) throws ClientException {
+ return cache.size(peekModes);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<K, V> getAll(Set<? extends K> keys) throws ClientException {
+ return cache.getAll(keys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void putAll(Map<? extends K, ? extends V> map) throws ClientException {
+ cache.putAll(map);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean remove(K key) throws ClientException {
+ return cache.remove(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeAll(Set<? extends K> keys) throws ClientException {
+ cache.removeAll(keys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clear() throws ClientException {
+ cache.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc) {
+ return new IgniteCacheClientProxy<>(cache.withExpirePolicy(expirePlc));
+ }
+
+ /** {@inheritDoc} */
+ @Override public <R> QueryCursor<R> query(Query<R> qry) {
+ return cache.query(qry);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean containsKey(K key) {
+ return cache.containsKey(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public @NotNull Iterator<Cache.Entry<K, V>> iterator() {
+ return cache.<Cache.Entry<K, V>>query(new ScanQuery<>()).getAll().iterator();
+ }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java
new file mode 100644
index 0000000..ac29eb4
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.client.ClientCache;
+
+/** Reperesents Ignite cache operations required by Spring Data. */
+public interface IgniteCacheProxy<K, V> extends Iterable<Cache.Entry<K, V>> {
+ /**
+ * Gets an entry from the cache.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the element, or null, if it does not exist.
+ */
+ public V get(K key);
+
+ /**
+ * Associates the specified value with the specified key in the cache.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param val value to be associated with the specified key.
+ */
+ public void put(K key, V val);
+
+ /**
+ * Gets the number of all entries cached across all nodes.
+ *
+ * @param peekModes Optional peek modes. If not provided, then total cache size is returned.
+ */
+ public int size(CachePeekMode... peekModes);
+
+ /**
+ * Gets a collection of entries from the Ignite cache, returning them as
+ * {@link Map} of the values associated with the set of keys requested.
+ *
+ * @param keys The keys whose associated values are to be returned.
+ * @return A map of entries that were found for the given keys.
+ */
+ public Map<K, V> getAll(Set<? extends K> keys);
+
+ /**
+ * Copies all of the entries from the specified map to the Ignite cache.
+ *
+ * @param map Mappings to be stored in this cache.
+ */
+ public void putAll(Map<? extends K, ? extends V> map);
+
+ /**
+ * Removes the mapping for a key from this cache if it is present.
+ *
+ * @param key Key whose mapping is to be removed from the cache.
+ * @return <tt>false</tt> if there was no matching key.
+ */
+ public boolean remove(K key);
+
+ /**
+ * Removes entries for the specified keys.
+ *
+ * @param keys The keys to remove.
+ */
+ public void removeAll(Set<? extends K> keys);
+
+ /** Clears the contents of the cache. */
+ public void clear();
+
+ /**
+ * Returns cache with the specified expired policy set. This policy will be used for each operation invoked on
+ * the returned cache.
+ *
+ * @return Cache instance with the specified expiry policy set.
+ */
+ public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc);
+
+ /**
+ * Execute SQL query and get cursor to iterate over results.
+ *
+ * @param qry SQL query.
+ * @return Query result cursor.
+ */
+ public <R> QueryCursor<R> query(Query<R> qry) ;
+
+ /**
+ * Determines if the {@link ClientCache} contains an entry for the specified key.
+ *
+ * @param key key whose presence in this cache is to be tested.
+ * @return <tt>true</tt> if this map contains a mapping for the specified key.
+ */
+ public boolean containsKey(K key);
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java
new file mode 100644
index 0000000..7a4c716
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxyImpl.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.client.ClientException;
+import org.jetbrains.annotations.NotNull;
+
+/** Implementation of {@link IgniteCacheProxy} that provides access to Ignite cache through {@link IgniteCache} instance. */
+public class IgniteCacheProxyImpl<K, V> implements IgniteCacheProxy<K, V> {
+ /** {@link IgniteCache} instance to which operations are delegated. */
+ private final IgniteCache<K, V> cache;
+
+ /** */
+ public IgniteCacheProxyImpl(IgniteCache<K, V> cache) {
+ this.cache = cache;
+ }
+
+ /** {@inheritDoc} */
+ @Override public V get(K key) throws ClientException {
+ return cache.get(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void put(K key, V val) throws ClientException {
+ cache.put(key, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size(CachePeekMode... peekModes) throws ClientException {
+ return cache.size(peekModes);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<K, V> getAll(Set<? extends K> keys) throws ClientException {
+ return cache.getAll(keys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void putAll(Map<? extends K, ? extends V> map) throws ClientException {
+ cache.putAll(map);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean remove(K key) throws ClientException {
+ return cache.remove(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeAll(Set<? extends K> keys) throws ClientException {
+ cache.removeAll(keys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clear() throws ClientException {
+ cache.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteCacheProxy<K, V> withExpiryPolicy(ExpiryPolicy expirePlc) {
+ return new IgniteCacheProxyImpl<>(cache.withExpiryPolicy(expirePlc));
+ }
+
+ /** {@inheritDoc} */
+ @Override public <R> QueryCursor<R> query(Query<R> qry) {
+ return cache.query(qry);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean containsKey(K key) {
+ return cache.containsKey(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public @NotNull Iterator<Cache.Entry<K, V>> iterator() {
+ return cache.<Cache.Entry<K, V>>query(new ScanQuery<>()).getAll().iterator();
+ }
+
+ /** @return {@link IgniteCache} instance to which operations are delegated. */
+ public IgniteCache<K, V> delegate() {
+ return cache;
+ }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java
new file mode 100644
index 0000000..ae50823
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+import org.apache.ignite.client.IgniteClient;
+
+/** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link IgniteClient} instance. */
+public class IgniteClientProxy implements IgniteProxy {
+ /** {@link IgniteClient} instance to which operations are delegated. */
+ private final IgniteClient cli;
+
+ /** */
+ public IgniteClientProxy(IgniteClient cli) {
+ this.cli = cli;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name) {
+ return new IgniteCacheClientProxy<>(cli.getOrCreateCache(name));
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) {
+ return new IgniteCacheClientProxy<>(cli.cache(name));
+ }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java
new file mode 100644
index 0000000..e798274
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+/** Reperesents Ignite cluster operations required by Spring Data. */
+public interface IgniteProxy {
+ /**
+ * Gets existing cache with the given name or creates new one.
+ *
+ * @param name Cache name.
+ * @return Cache proxy that provides access to cache with given name.
+ */
+ public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name);
+
+ /**
+ * Gets cache with the given name.
+ *
+ * @param name Cache name.
+ * @return Cache proxy that provides access to cache with specified name or {@code null} if it doesn't exist.
+ */
+ public <K, V> IgniteCacheProxy<K, V> cache(String name);
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java
new file mode 100644
index 0000000..96b46a7
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.proxy;
+
+import org.apache.ignite.Ignite;
+
+/** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link Ignite} instance. */
+public class IgniteProxyImpl implements IgniteProxy {
+ /** {@link Ignite} instance to which operations are delegated. */
+ private final Ignite ignite;
+
+ /** */
+ public IgniteProxyImpl(Ignite ignite) {
+ this.ignite = ignite;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> IgniteCacheProxy<K, V> getOrCreateCache(String name) {
+ return new IgniteCacheProxyImpl<>(ignite.getOrCreateCache(name));
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) {
+ return new IgniteCacheProxyImpl<>(ignite.cache(name));
+ }
+
+ /** @return {@link Ignite} instance to which operations are delegated. */
+ public Ignite delegate() {
+ return ignite;
+ }
+}
diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java
new file mode 100644
index 0000000..cbb8373
--- /dev/null
+++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 description. -->
+ * Contains classes that provide client-independent access to the Ignite operations required by Spring Data.
+ */
+package org.apache.ignite.springdata.proxy;
diff --git a/modules/spring-data-ext/pom.xml b/modules/spring-data-ext/pom.xml
index b7547f1..3015102 100644
--- a/modules/spring-data-ext/pom.xml
+++ b/modules/spring-data-ext/pom.xml
@@ -95,6 +95,12 @@
<version>${ignite.version}</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-spring-data-commons</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
index e351c61..79ce6c0 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/query/IgniteRepositoryQuery.java
@@ -25,12 +25,11 @@
import java.util.Iterator;
import java.util.List;
import javax.cache.Cache;
-import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.Query;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
-import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.domain.Pageable;
@@ -84,7 +83,7 @@
private final IgniteQuery qry;
/** Cache. */
- private final IgniteCache cache;
+ private final IgniteCacheProxy<?, ?> cache;
/** Method. */
private final Method mtd;
@@ -106,7 +105,7 @@
* @param cache Cache.
*/
public IgniteRepositoryQuery(RepositoryMetadata metadata, IgniteQuery qry,
- Method mtd, ProjectionFactory factory, IgniteCache cache) {
+ Method mtd, ProjectionFactory factory, IgniteCacheProxy<?, ?> cache) {
type = metadata.getDomainType();
this.qry = qry;
this.cache = cache;
@@ -238,19 +237,19 @@
}
}
else {
- Iterable<CacheEntryImpl> qryIter = (Iterable<CacheEntryImpl>)qryCursor;
+ Iterable<Cache.Entry<?, ?>> qryIter = (Iterable<Cache.Entry<?, ?>>)qryCursor;
switch (returnStgy) {
case LIST_OF_VALUES:
List list = new ArrayList<>();
- for (CacheEntryImpl entry : qryIter)
+ for (Cache.Entry<?, ?> entry : qryIter)
list.add(entry.getValue());
return list;
case ONE_VALUE:
- Iterator<CacheEntryImpl> iter1 = qryIter.iterator();
+ Iterator<Cache.Entry<?, ?>> iter1 = qryIter.iterator();
if (iter1.hasNext())
return iter1.next().getValue();
@@ -258,7 +257,7 @@
return null;
case CACHE_ENTRY:
- Iterator<CacheEntryImpl> iter2 = qryIter.iterator();
+ Iterator<Cache.Entry<?, ?>> iter2 = qryIter.iterator();
if (iter2.hasNext())
return iter2.next();
@@ -268,7 +267,7 @@
case SLICE_OF_VALUES:
List content = new ArrayList<>();
- for (CacheEntryImpl entry : qryIter)
+ for (Cache.Entry<?, ?> entry : qryIter)
content.add(entry.getValue());
return new SliceImpl(content, (Pageable)prmtrs[prmtrs.length - 1], true);
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
index de2549f..5fc0ba2 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java
@@ -22,7 +22,12 @@
import java.util.Map;
import org.apache.ignite.Ignite;
import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.springdata.proxy.IgniteProxy;
+import org.apache.ignite.springdata.proxy.IgniteProxyImpl;
+import org.apache.ignite.springdata.proxy.IgniteClientProxy;
import org.apache.ignite.springdata.repository.IgniteRepository;
import org.apache.ignite.springdata.repository.config.Query;
import org.apache.ignite.springdata.repository.config.RepositoryConfig;
@@ -47,7 +52,7 @@
*/
public class IgniteRepositoryFactory extends RepositoryFactorySupport {
/** Ignite instance */
- private Ignite ignite;
+ private IgniteProxy ignite;
/** Mapping of a repository to a cache. */
private final Map<Class<?>, String> repoToCache = new HashMap<>();
@@ -58,7 +63,12 @@
* @param ignite
*/
public IgniteRepositoryFactory(Ignite ignite) {
- this.ignite = ignite;
+ this.ignite = new IgniteProxyImpl(ignite);
+ }
+
+ /** Creates the factory with initialized {@link IgniteClient} instance. */
+ public IgniteRepositoryFactory(IgniteClient cli) {
+ ignite = new IgniteClientProxy(cli);
}
/**
@@ -68,7 +78,15 @@
* @param cfg Ignite configuration.
*/
public IgniteRepositoryFactory(IgniteConfiguration cfg) {
- this.ignite = Ignition.start(cfg);
+ this.ignite = new IgniteProxyImpl(Ignition.start(cfg));
+ }
+
+ /**
+ * Initializes the factory with provided {@link ClientConfiguration} that is used to start up an underlying
+ * {@link IgniteClient} instance.
+ */
+ public IgniteRepositoryFactory(ClientConfiguration cfg) {
+ this.ignite = new IgniteClientProxy(Ignition.startClient(cfg));
}
/**
@@ -78,7 +96,7 @@
* @param springCfgPath A path to Ignite configuration.
*/
public IgniteRepositoryFactory(String springCfgPath) {
- this.ignite = Ignition.start(springCfgPath);
+ this.ignite = new IgniteProxyImpl(Ignition.start(springCfgPath));
}
/** {@inheritDoc} */
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
index f87dce5..71200c2 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java
@@ -20,6 +20,8 @@
import java.io.Serializable;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.springdata.repository.IgniteRepository;
import org.springframework.beans.BeansException;
@@ -35,8 +37,8 @@
* The repository requires to define one of the parameters below in your Spring application configuration in order
* to get an access to Apache Ignite cluster:
* <ul>
- * <li>{@link Ignite} instance bean named "igniteInstance"</li>
- * <li>{@link IgniteConfiguration} bean named "igniteCfg"</li>
+ * <li>{@link Ignite} or {@link IgniteClient} instance bean named "igniteInstance"</li>
+ * <li>{@link IgniteConfiguration} or {@link ClientConfiguration} bean named "igniteCfg"</li>
* <li>A path to Ignite's Spring XML configuration named "igniteSpringCfgPath"</li>
* <ul/>
*
@@ -64,15 +66,29 @@
/** {@inheritDoc} */
@Override protected RepositoryFactorySupport createRepositoryFactory() {
try {
- Ignite ignite = (Ignite)ctx.getBean("igniteInstance");
+ Object igniteInstanceBean = ctx.getBean("igniteInstance");
- return new IgniteRepositoryFactory(ignite);
+ if (igniteInstanceBean instanceof Ignite)
+ return new IgniteRepositoryFactory((Ignite)igniteInstanceBean);
+ else if (igniteInstanceBean instanceof IgniteClient)
+ return new IgniteRepositoryFactory((IgniteClient)igniteInstanceBean);
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" +
+ " \"igniteInstance\" property of repository configuration must be one of the following types: " +
+ Ignite.class.getName() + ", " + IgniteClient.class.getName());
}
catch (BeansException ex) {
try {
- IgniteConfiguration cfg = (IgniteConfiguration)ctx.getBean("igniteCfg");
+ Object igniteCfgBean = ctx.getBean("igniteCfg");
- return new IgniteRepositoryFactory(cfg);
+ if (igniteCfgBean instanceof IgniteConfiguration)
+ return new IgniteRepositoryFactory((IgniteConfiguration)igniteCfgBean);
+ else if (igniteCfgBean instanceof ClientConfiguration)
+ return new IgniteRepositoryFactory((ClientConfiguration)igniteCfgBean);
+
+ throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" +
+ " the \"igniteCfg\" property of repository configuration must be one of the following types: [" +
+ IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']');
}
catch (BeansException ex2) {
try {
@@ -81,9 +97,9 @@
return new IgniteRepositoryFactory(path);
}
catch (BeansException ex3) {
- throw new IgniteException("Failed to initialize Ignite repository factory. Ignite instance or" +
- " IgniteConfiguration or a path to Ignite's spring XML configuration must be defined in the" +
- " application configuration");
+ throw new IgniteException("Failed to initialize Ignite repository factory. One of the following" +
+ " beans must be defined in application configuration: \"igniteInstance\", \"igniteCfg\"," +
+ " \"igniteSpringCfgPath\".");
}
}
}
diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
index 95d933b..e8b8824 100644
--- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
+++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryImpl.java
@@ -23,8 +23,8 @@
import java.util.Set;
import java.util.TreeSet;
import javax.cache.Cache;
-import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.springdata.proxy.IgniteCacheProxy;
import org.apache.ignite.springdata.repository.IgniteRepository;
import static java.util.Collections.emptySet;
@@ -34,14 +34,14 @@
*/
public class IgniteRepositoryImpl<T, ID extends Serializable> implements IgniteRepository<T, ID> {
/** Ignite Cache bound to the repository */
- private final IgniteCache<ID, T> cache;
+ private final IgniteCacheProxy<ID, T> cache;
/**
* Repository constructor.
*
* @param cache Initialized cache instance.
*/
- public IgniteRepositoryImpl(IgniteCache<ID, T> cache) {
+ public IgniteRepositoryImpl(IgniteCacheProxy<ID, T> cache) {
this.cache = cache;
}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
new file mode 100644
index 0000000..6dcadb8
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.compoundkey.CityRepository;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data operation with compound key when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompoundKeyTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(CityRepository.class);
+ }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
new file mode 100644
index 0000000..a887ad8
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonRepositoryWithCompoundKey;
+import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
+/** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ repoWithCompoundKey = ctx.getBean(PersonRepositoryWithCompoundKey.class);
+ ignite = ctx.getBean(IgniteEx.class);
+ }
+
+ /**
+ * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster.
+ */
+ @Test
+ public void testRepositoryWithClientConfiguration() {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(TestApplicationConfiguration.class);
+ ctx.refresh();
+
+ assertTrue(ctx.getBean(PersonRepository.class).count() > 0);
+ }
+
+ /** */
+ @Configuration
+ @EnableIgniteRepositories("org.apache.ignite.springdata.misc")
+ static class TestApplicationConfiguration {
+ /** Ignite client configuration bean. */
+ @Bean
+ public ClientConfiguration igniteCfg() {
+ return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT);
+ }
+ }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
new file mode 100644
index 0000000..0410e20
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataQueriesSelfTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata;
+
+import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration;
+import org.apache.ignite.springdata.misc.Person;
+import org.apache.ignite.springdata.misc.PersonRepository;
+import org.apache.ignite.springdata.misc.PersonSecondRepository;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/** Tests Spring Data query operations when thin client is used for accessing the Ignite cluster. */
+public class IgniteClientSpringDataQueriesSelfTest extends IgniteSpringDataQueriesSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ ctx = new AnnotationConfigApplicationContext();
+
+ ctx.register(IgniteClientApplicationConfiguration.class);
+ ctx.refresh();
+
+ repo = ctx.getBean(PersonRepository.class);
+ repo2 = ctx.getBean(PersonSecondRepository.class);
+
+ for (int i = 0; i < CACHE_SIZE; i++) {
+ repo.save(i, new Person("person" + Integer.toHexString(i),
+ "lastName" + Integer.toHexString((i + 16) % 256)));
+ }
+ }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
index 62ff8c5..655ef40 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java
@@ -34,10 +34,10 @@
* */
public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest {
/** Application context */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** City repository */
- private static CityRepository repo;
+ protected static CityRepository repo;
/** Cache name */
private static final String CACHE_NAME = "City";
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
index 65f9239..3f8cecc 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCrudSelfTest.java
@@ -41,23 +41,23 @@
*/
public class IgniteSpringDataCrudSelfTest extends GridCommonAbstractTest {
/** Number of entries to store. */
- private static final int CACHE_SIZE = 1000;
+ protected static final int CACHE_SIZE = 1000;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Repository. */
- private static PersonRepositoryWithCompoundKey repoWithCompoundKey;
+ protected static PersonRepositoryWithCompoundKey repoWithCompoundKey;
/** */
@Rule
public final ExpectedException expected = ExpectedException.none();
/** */
- private static IgniteEx ignite;
+ protected static IgniteEx ignite;
/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
index 51492c1..39568f9 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataQueriesSelfTest.java
@@ -37,16 +37,16 @@
*/
public class IgniteSpringDataQueriesSelfTest extends GridCommonAbstractTest {
/** Repository. */
- private static PersonRepository repo;
+ protected static PersonRepository repo;
/** Repository 2. */
- private static PersonSecondRepository repo2;
+ protected static PersonSecondRepository repo2;
/** Context. */
- private static AnnotationConfigApplicationContext ctx;
+ protected static AnnotationConfigApplicationContext ctx;
/** Number of entries to store */
- private static int CACHE_SIZE = 1000;
+ protected static int CACHE_SIZE = 1000;
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
new file mode 100644
index 0000000..66379f7
--- /dev/null
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.springdata.misc;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT;
+
+/** Spring Application configuration for repository testing in case thin client is used for accessing the cluster. */
+@Configuration
+@EnableIgniteRepositories({"org.apache.ignite.springdata.compoundkey", "org.apache.ignite.springdata.misc"})
+public class IgniteClientApplicationConfiguration {
+ /** @return {@link Ignite} node instance. */
+ @Bean
+ public IgniteEx igniteServerNode() {
+ IgniteConfiguration cfg = new IgniteConfiguration()
+ .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT))
+ .setCacheConfiguration(new CacheConfiguration<Integer, Person>("PersonCache")
+ .setIndexedTypes(Integer.class, Person.class));
+
+ return (IgniteEx)Ignition.start(cfg);
+ }
+
+ /** Ignite client instance bean with default name. */
+ @Bean
+ public IgniteClient igniteInstance() {
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT));
+ }
+}
diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
index cc063a6..cb6e34c 100644
--- a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
+++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java
@@ -17,6 +17,9 @@
package org.apache.ignite.testsuites;
+import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest;
+import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest;
import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest;
import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest;
import org.apache.ignite.springdata.IgniteSpringDataQueriesSelfTest;
@@ -30,7 +33,10 @@
@Suite.SuiteClasses({
IgniteSpringDataCrudSelfTest.class,
IgniteSpringDataQueriesSelfTest.class,
- IgniteSpringDataCompoundKeyTest.class
+ IgniteSpringDataCompoundKeyTest.class,
+ IgniteClientSpringDataCrudSelfTest.class,
+ IgniteClientSpringDataQueriesSelfTest.class,
+ IgniteClientSpringDataCompoundKeyTest.class
})
public class IgniteSpringDataTestSuite {
}
diff --git a/pom.xml b/pom.xml
index a3232d1..957acfb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,7 @@
<module>modules/spring-data-ext</module>
<module>modules/spring-data-2.0-ext</module>
<module>modules/spring-data-2.2-ext</module>
+ <module>modules/spring-data-commons</module>
</modules>
<profiles>