http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStore.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStore.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStore.java index d78ea48..aa013b9 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStore.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStore.java @@ -21,226 +21,392 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.cache.CacheException; import javax.cache.integration.CacheLoaderException; -import org.apache.ignite.cache.CacheTypeFieldMetadata; -import org.apache.ignite.cache.CacheTypeMetadata; +import org.apache.ignite.binary.BinaryObject; +import org.apache.ignite.binary.BinaryObjectBuilder; import org.apache.ignite.cache.store.CacheStore; import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; /** * Implementation of {@link CacheStore} backed by JDBC and POJO via reflection. * - * This implementation stores objects in underlying database using java beans mapping description via reflection. - * <p> + * This implementation stores objects in underlying database using java beans mapping description via reflection. <p> * Use {@link CacheJdbcPojoStoreFactory} factory to pass {@link CacheJdbcPojoStore} to {@link CacheConfiguration}. */ public class CacheJdbcPojoStore<K, V> extends CacheAbstractJdbcStore<K, V> { + /** POJO methods cache. */ + private volatile Map<String, Map<String, PojoMethodsCache>> pojosMthds = Collections.emptyMap(); + /** - * POJO methods cache. + * Get field value from object for use as query parameter. + * + * @param cacheName Cache name. + * @param typeName Type name. + * @param fldName Field name. + * @param obj Cache object. + * @return Field value from object. + * @throws CacheException in case of error. */ - protected static class PojoMethodsCache { - /** POJO class. */ - protected final Class<?> cls; - - /** Constructor for POJO object. */ - private Constructor ctor; - - /** {@code true} if object is a simple type. */ - private final boolean simple; - - /** Cached setters for POJO object. */ - private Map<String, Method> getters; - - /** Cached getters for POJO object. */ - private Map<String, Method> setters; - - /** - * POJO methods cache. - * - * @param clsName Class name. - * @param fields Fields. - * - * @throws CacheException If failed to construct type cache. - */ - public PojoMethodsCache(String clsName, Collection<CacheTypeFieldMetadata> fields) throws CacheException { - try { - cls = Class.forName(clsName); - - if (simple = simpleType(cls)) - return; + @Override @Nullable protected Object extractParameter(@Nullable String cacheName, String typeName, TypeKind typeKind, + String fldName, Object obj) throws CacheException { + switch (typeKind) { + case BUILT_IN: + return obj; + case POJO: + return extractPojoParameter(cacheName, typeName, fldName, obj); + default: + return extractBinaryParameter(fldName, obj); + } + } - ctor = cls.getDeclaredConstructor(); + /** + * Get field value from POJO for use as query parameter. + * + * @param cacheName Cache name. + * @param typeName Type name. + * @param fldName Field name. + * @param obj Cache object. + * @return Field value from object. + * @throws CacheException in case of error. + */ + @Nullable private Object extractPojoParameter(@Nullable String cacheName, String typeName, String fldName, + Object obj) throws CacheException { + try { + Map<String, PojoMethodsCache> cacheMethods = pojosMthds.get(cacheName); - if (!ctor.isAccessible()) - ctor.setAccessible(true); - } - catch (ClassNotFoundException e) { - throw new CacheException("Failed to find class: " + clsName, e); - } - catch (NoSuchMethodException e) { - throw new CacheException("Failed to find default constructor for class: " + clsName, e); - } + if (cacheMethods == null) + throw new CacheException("Failed to find POJO type metadata for cache: " + U.maskName(cacheName)); - setters = U.newHashMap(fields.size()); + PojoMethodsCache mc = cacheMethods.get(typeName); - getters = U.newHashMap(fields.size()); + if (mc == null) + throw new CacheException("Failed to find POJO type metadata for type: " + typeName); - for (CacheTypeFieldMetadata field : fields) { - String prop = capitalFirst(field.getJavaName()); + Method getter = mc.getters.get(fldName); - try { - getters.put(field.getJavaName(), cls.getMethod("get" + prop)); - } - catch (NoSuchMethodException ignored) { - try { - getters.put(field.getJavaName(), cls.getMethod("is" + prop)); - } - catch (NoSuchMethodException e) { - throw new CacheException("Failed to find getter in POJO class [clsName=" + clsName + - ", prop=" + field.getJavaName() + "]", e); - } - } + if (getter == null) + throw new CacheLoaderException("Failed to find getter in POJO class [class=" + typeName + + ", prop=" + fldName + "]"); - try { - setters.put(field.getJavaName(), cls.getMethod("set" + prop, field.getJavaType())); - } - catch (NoSuchMethodException e) { - throw new CacheException("Failed to find setter in POJO class [clsName=" + clsName + - ", prop=" + field.getJavaName() + "]", e); - } - } + return getter.invoke(obj); } - - /** - * Capitalizes the first character of the given string. - * - * @param str String. - * @return String with capitalized first character. - */ - @Nullable private String capitalFirst(@Nullable String str) { - return str == null ? null : - str.isEmpty() ? "" : Character.toUpperCase(str.charAt(0)) + str.substring(1); + catch (Exception e) { + throw new CacheException("Failed to read object of class: " + typeName, e); } } - /** Methods cache. */ - protected volatile Map<String, Map<String, PojoMethodsCache>> mtdsCache = Collections.emptyMap(); + /** + * Get field value from Binary object for use as query parameter. + * + * @param fieldName Field name to extract query parameter for. + * @param obj Object to process. + * @return Field value from object. + * @throws CacheException in case of error. + */ + private Object extractBinaryParameter(String fieldName, Object obj) throws CacheException { + if (obj instanceof BinaryObject) + return ((BinaryObject)obj).field(fieldName); + + throw new CacheException("Failed to read property value from non binary object [class=" + + obj.getClass() + ", property=" + fieldName + "]"); + } /** {@inheritDoc} */ - @Override protected void prepareBuilders(@Nullable String cacheName, Collection<CacheTypeMetadata> types) - throws CacheException { - Map<String, PojoMethodsCache> typeMethods = U.newHashMap(types.size() * 2); + @Override protected <R> R buildObject(@Nullable String cacheName, String typeName, TypeKind typeKind, + JdbcTypeField[] flds, Collection<String> hashFlds, Map<String, Integer> loadColIdxs, ResultSet rs) + throws CacheLoaderException { + switch (typeKind) { + case BUILT_IN: + return (R)buildBuiltinObject(typeName, flds, loadColIdxs, rs); + case POJO: + return (R)buildPojoObject(cacheName, typeName, flds, loadColIdxs, rs); + default: + return (R)buildBinaryObject(typeName, flds, hashFlds, loadColIdxs, rs); + } + } - for (CacheTypeMetadata type : types) { - String keyType = type.getKeyType(); - typeMethods.put(keyType, new PojoMethodsCache(keyType, type.getKeyFields())); + /** + * Construct Java built in object from query result. + * + * @param typeName Type name. + * @param fields Fields descriptors. + * @param loadColIdxs Select query columns indexes. + * @param rs ResultSet to take data from. + * @return Constructed object. + * @throws CacheLoaderException If failed to construct POJO. + */ + private Object buildBuiltinObject(String typeName, JdbcTypeField[] fields, Map<String, Integer> loadColIdxs, + ResultSet rs) throws CacheLoaderException { + try { + JdbcTypeField field = fields[0]; - String valType = type.getValueType(); - typeMethods.put(valType, new PojoMethodsCache(valType, type.getValueFields())); + return getColumnValue(rs, loadColIdxs.get(field.getDatabaseFieldName()), field.getJavaFieldType()); } + catch (SQLException e) { + throw new CacheLoaderException("Failed to read object of class: " + typeName, e); + } + } - Map<String, Map<String, PojoMethodsCache>> newMtdsCache = new HashMap<>(mtdsCache); + /** + * Construct POJO from query result. + * + * @param cacheName Cache name. + * @param typeName Type name. + * @param flds Fields descriptors. + * @param loadColIdxs Select query columns index. + * @param rs ResultSet. + * @return Constructed POJO. + * @throws CacheLoaderException If failed to construct POJO. + */ + private Object buildPojoObject(@Nullable String cacheName, String typeName, + JdbcTypeField[] flds, Map<String, Integer> loadColIdxs, ResultSet rs) + throws CacheLoaderException { - newMtdsCache.put(cacheName, typeMethods); + Map<String, PojoMethodsCache> cacheMethods = pojosMthds.get(cacheName); - mtdsCache = newMtdsCache; - } + if (cacheMethods == null) + throw new CacheLoaderException("Failed to find POJO types metadata for cache: " + U.maskName(cacheName)); - /** {@inheritDoc} */ - @Override protected <R> R buildObject(String cacheName, String typeName, Collection<CacheTypeFieldMetadata> fields, - Map<String, Integer> loadColIdxs, ResultSet rs) throws CacheLoaderException { - PojoMethodsCache mc = mtdsCache.get(cacheName).get(typeName); + PojoMethodsCache mc = cacheMethods.get(typeName); if (mc == null) - throw new CacheLoaderException("Failed to find cache type metadata for type: " + typeName); + throw new CacheLoaderException("Failed to find POJO type metadata for type: " + typeName); try { - if (mc.simple) { - CacheTypeFieldMetadata field = F.first(fields); - - return (R)getColumnValue(rs, loadColIdxs.get(field.getDatabaseName()), mc.cls); - } - Object obj = mc.ctor.newInstance(); - for (CacheTypeFieldMetadata field : fields) { - String fldJavaName = field.getJavaName(); + for (JdbcTypeField fld : flds) { + String fldJavaName = fld.getJavaFieldName(); Method setter = mc.setters.get(fldJavaName); if (setter == null) - throw new IllegalStateException("Failed to find setter in POJO class [clsName=" + typeName + + throw new IllegalStateException("Failed to find setter in POJO class [type=" + typeName + ", prop=" + fldJavaName + "]"); - String fldDbName = field.getDatabaseName(); + String fldDbName = fld.getDatabaseFieldName(); Integer colIdx = loadColIdxs.get(fldDbName); try { - setter.invoke(obj, getColumnValue(rs, colIdx, field.getJavaType())); + Object colVal = getColumnValue(rs, colIdx, fld.getJavaFieldType()); + + try { + setter.invoke(obj, colVal); + } + catch (Exception e) { + throw new CacheLoaderException("Failed to set property in POJO class [type=" + typeName + + ", prop=" + fldJavaName + ", col=" + colIdx + ", dbName=" + fldDbName + "]", e); + } } - catch (Exception e) { - throw new IllegalStateException("Failed to set property in POJO class [clsName=" + typeName + + catch (SQLException e) { + throw new CacheLoaderException("Failed to read object property [type= " + typeName + ", prop=" + fldJavaName + ", col=" + colIdx + ", dbName=" + fldDbName + "]", e); } } - return (R)obj; - } - catch (SQLException e) { - throw new CacheLoaderException("Failed to read object of class: " + typeName, e); + return obj; } catch (Exception e) { throw new CacheLoaderException("Failed to construct instance of class: " + typeName, e); } } - /** {@inheritDoc} */ - @Nullable @Override protected Object extractParameter(String cacheName, String typeName, String fieldName, - Object obj) - throws CacheException { + /** + * Construct binary object from query result. + * + * @param typeName Type name. + * @param fields Fields descriptors. + * @param hashFields Collection of fields to build hash for. + * @param loadColIdxs Select query columns index. + * @param rs ResultSet. + * @return Constructed binary object. + * @throws CacheLoaderException If failed to construct binary object. + */ + protected Object buildBinaryObject(String typeName, JdbcTypeField[] fields, + Collection<String> hashFields, Map<String, Integer> loadColIdxs, ResultSet rs) throws CacheLoaderException { try { - PojoMethodsCache mc = mtdsCache.get(cacheName).get(typeName); + BinaryObjectBuilder builder = ignite.binary().builder(typeName); - if (mc == null) - throw new CacheException("Failed to find cache type metadata for type: " + typeName); + boolean calcHash = hashFields != null; - if (mc.simple) - return obj; + Collection<Object> hashValues = calcHash ? new ArrayList<>(hashFields.size()) : null; - Method getter = mc.getters.get(fieldName); + for (JdbcTypeField field : fields) { + Integer colIdx = loadColIdxs.get(field.getDatabaseFieldName()); - if (getter == null) - throw new CacheLoaderException("Failed to find getter in POJO class [clsName=" + typeName + - ", prop=" + fieldName + "]"); + Object colVal = getColumnValue(rs, colIdx, field.getJavaFieldType()); - return getter.invoke(obj); + builder.setField(field.getJavaFieldName(), colVal); + + if (calcHash) + hashValues.add(colVal); + } + + if (calcHash) + builder.hashCode(hasher.hashCode(hashValues)); + + return builder.build(); } - catch (Exception e) { - throw new CacheException("Failed to read object of class: " + typeName, e); + catch (SQLException e) { + throw new CacheException("Failed to read binary object", e); } } - /** {@inheritDoc} */ - @Override protected Object keyTypeId(Object key) throws CacheException { - return key.getClass(); + /** + * Calculate type ID for object. + * + * @param obj Object to calculate type ID for. + * @return Type ID. + * @throws CacheException If failed to calculate type ID for given object. + */ + @Override protected Object typeIdForObject(Object obj) throws CacheException { + if (obj instanceof BinaryObject) + return ((BinaryObject)obj).typeId(); + + return obj.getClass(); } /** {@inheritDoc} */ - @Override protected Object keyTypeId(String type) throws CacheException { + @Override protected Object typeIdForTypeName(TypeKind kind, String typeName) throws CacheException { + if (kind == TypeKind.BINARY) + return ignite.binary().typeId(typeName); + try { - return Class.forName(type); + return Class.forName(typeName); } catch (ClassNotFoundException e) { - throw new CacheException("Failed to find class: " + type, e); + throw new CacheException("Failed to find class: " + typeName, e); + } + } + + /** + * Prepare internal store specific builders for provided types metadata. + * + * @param cacheName Cache name to prepare builders for. + * @param types Collection of types. + * @throws CacheException If failed to prepare internal builders for types. + */ + @Override protected void prepareBuilders(@Nullable String cacheName, Collection<JdbcType> types) + throws CacheException { + Map<String, PojoMethodsCache> pojoMethods = U.newHashMap(types.size() * 2); + + for (JdbcType type : types) { + String keyTypeName = type.getKeyType(); + + TypeKind keyKind = kindForName(keyTypeName); + + if (keyKind == TypeKind.POJO) { + if (pojoMethods.containsKey(keyTypeName)) + throw new CacheException("Found duplicate key type [cache=" + U.maskName(cacheName) + + ", keyType=" + keyTypeName + "]"); + + pojoMethods.put(keyTypeName, new PojoMethodsCache(keyTypeName, type.getKeyFields())); + } + + String valTypeName = type.getValueType(); + + TypeKind valKind = kindForName(valTypeName); + + if (valKind == TypeKind.POJO) + pojoMethods.put(valTypeName, new PojoMethodsCache(valTypeName, type.getValueFields())); + } + + if (!pojoMethods.isEmpty()) { + Map<String, Map<String, PojoMethodsCache>> newPojosMethods = new HashMap<>(pojosMthds); + + newPojosMethods.put(cacheName, pojoMethods); + + pojosMthds = newPojosMethods; + } + } + + /** + * POJO methods cache. + */ + private static class PojoMethodsCache { + /** POJO class. */ + private final Class<?> cls; + + /** Constructor for POJO object. */ + private Constructor ctor; + + /** Cached setters for POJO object. */ + private Map<String, Method> getters; + + /** Cached getters for POJO object. */ + private Map<String, Method> setters; + + /** + * POJO methods cache. + * + * @param clsName Class name. + * @param fields Fields. + * @throws CacheException If failed to construct type cache. + */ + private PojoMethodsCache(String clsName, JdbcTypeField[] fields) throws CacheException { + try { + cls = Class.forName(clsName); + + ctor = cls.getDeclaredConstructor(); + + if (!ctor.isAccessible()) + ctor.setAccessible(true); + } + catch (ClassNotFoundException e) { + throw new CacheException("Failed to find class: " + clsName, e); + } + catch (NoSuchMethodException e) { + throw new CacheException("Failed to find default constructor for class: " + clsName, e); + } + + setters = U.newHashMap(fields.length); + + getters = U.newHashMap(fields.length); + + for (JdbcTypeField field : fields) { + String prop = capitalFirst(field.getJavaFieldName()); + + try { + getters.put(field.getJavaFieldName(), cls.getMethod("get" + prop)); + } + catch (NoSuchMethodException ignored) { + try { + getters.put(field.getJavaFieldName(), cls.getMethod("is" + prop)); + } + catch (NoSuchMethodException e) { + throw new CacheException("Failed to find getter in POJO class [class=" + clsName + + ", prop=" + field.getJavaFieldName() + "]", e); + } + } + + try { + setters.put(field.getJavaFieldName(), cls.getMethod("set" + prop, field.getJavaFieldType())); + } + catch (NoSuchMethodException e) { + throw new CacheException("Failed to find setter in POJO class [class=" + clsName + + ", prop=" + field.getJavaFieldName() + "]", e); + } + } + } + + /** + * Capitalizes the first character of the given string. + * + * @param str String. + * @return String with capitalized first character. + */ + @Nullable private String capitalFirst(@Nullable String str) { + return str == null ? null : + str.isEmpty() ? "" : Character.toUpperCase(str.charAt(0)) + str.substring(1); } } -} \ No newline at end of file +}
http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreFactory.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreFactory.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreFactory.java index c90a69b..ded83ce 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreFactory.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreFactory.java @@ -34,17 +34,53 @@ import org.apache.ignite.resources.SpringApplicationContextResource; * * <h2 class="header">Spring Example</h2> * <pre name="code" class="xml"> - * <bean id= "simpleDataSource" class="org.h2.jdbcx.JdbcDataSource"/> + * <bean id= "myDataSource" class="org.h2.jdbcx.JdbcDataSource"/> * * <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration"> * ... * <property name="cacheConfiguration"> * <list> * <bean class="org.apache.ignite.configuration.CacheConfiguration"> + * <property name="name" value="myCache" /> * ... * <property name="cacheStoreFactory"> * <bean class="org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory"> - * <property name="dataSourceBean" value = "simpleDataSource" /> + * <property name="dataSourceBean" value="myDataSource" /> + * <property name="types"> + * <list> + * <bean class="org.apache.ignite.cache.store.jdbc.JdbcType"> + * <property name="cacheName" value="myCache" /> + * <property name="databaseSchema" value="MY_DB_SCHEMA" /> + * <property name="databaseTable" value="PERSON" /> + * <property name="keyType" value="java.lang.Integer" /> + * <property name="keyFields"> + * <list> + * <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> + * <property name="databaseFieldType" > + * <util:constant static-field="java.sql.Types.INTEGER"/> + * </property> + * <property name="databaseFieldName" value="ID" /> + * <property name="javaFieldType" value="java.lang.Integer" /> + * <property name="javaFieldName" value="id" /> + * </bean> + * </list> + * </property> + * <property name="valueType" value="my.company.Person" /> + * <property name="valueFields"> + * <list> + * <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> + * <property name="databaseFieldType" > + * <util:constant static-field="java.sql.Types.VARCHAR"/> + * </property> + * <property name="databaseFieldName" value="NAME" /> + * <property name="javaFieldType" value="java.lang.String" /> + * <property name="javaFieldName" value="name" /> + * </bean> + * </list> + * </property> + * </bean> + * </list> + * </property> * </bean> * </property> * </bean> @@ -57,47 +93,81 @@ import org.apache.ignite.resources.SpringApplicationContextResource; * <br> * For information about Spring framework visit <a href="http://www.springframework.org/">www.springframework.org</a> */ -public class CacheJdbcPojoStoreFactory<K, V> implements Factory<CacheJdbcPojoStore<K, V>> { +public class CacheJdbcPojoStoreFactory<K, V> implements Factory<CacheAbstractJdbcStore<K, V>> { /** */ private static final long serialVersionUID = 0L; + /** Default value for write attempts. */ + public static final int DFLT_WRITE_ATTEMPTS = 2; + + /** Default batch size for put and remove operations. */ + public static final int DFLT_BATCH_SIZE = 512; + + /** Default batch size for put and remove operations. */ + public static final int DFLT_PARALLEL_LOAD_CACHE_MINIMUM_THRESHOLD = 512; + + /** Maximum batch size for writeAll and deleteAll operations. */ + private int batchSizw = DFLT_BATCH_SIZE; + /** Name of data source bean. */ private String dataSrcBean; - /** Data source. */ - private transient DataSource dataSrc; - /** Database dialect. */ private JdbcDialect dialect; + /** Max workers thread count. These threads are responsible for load cache. */ + private int maxPoolSize = Runtime.getRuntime().availableProcessors(); + + /** Maximum write attempts in case of database error. */ + private int maxWriteAttempts = DFLT_WRITE_ATTEMPTS; + + /** Parallel load cache minimum threshold. If {@code 0} then load sequentially. */ + private int parallelLoadCacheMinThreshold = DFLT_PARALLEL_LOAD_CACHE_MINIMUM_THRESHOLD; + + /** Hash calculator. */ + private JdbcTypeHasher hasher = JdbcTypeDefaultHasher.INSTANCE; + + /** Types that store could process. */ + private JdbcType[] types; + + /** Data source. */ + private transient DataSource dataSrc; + /** Application context. */ @SpringApplicationContextResource - private transient Object appContext; + private transient Object appCtx; /** {@inheritDoc} */ @Override public CacheJdbcPojoStore<K, V> create() { CacheJdbcPojoStore<K, V> store = new CacheJdbcPojoStore<>(); + store.setBatchSize(batchSizw); store.setDialect(dialect); + store.setMaximumPoolSize(maxPoolSize); + store.setMaximumWriteAttempts(maxWriteAttempts); + store.setParallelLoadCacheMinimumThreshold(parallelLoadCacheMinThreshold); + store.setTypes(types); if (dataSrc != null) store.setDataSource(dataSrc); - else if (dataSrcBean != null) { - if (appContext == null) - throw new IgniteException("Spring application context resource is not injected."); + else { + if (dataSrcBean != null) { + if (appCtx == null) + throw new IgniteException("Spring application context resource is not injected."); - IgniteSpringHelper spring; + IgniteSpringHelper spring; - try { - spring = IgniteComponentType.SPRING.create(false); + try { + spring = IgniteComponentType.SPRING.create(false); - DataSource data = spring.loadBeanFromAppContext(appContext, dataSrcBean); + DataSource data = spring.loadBeanFromAppContext(appCtx, dataSrcBean); - store.setDataSource(data); - } - catch (Exception e) { - throw new IgniteException("Failed to load bean in application context [beanName=" + dataSrcBean + - ", igniteConfig=" + appContext + ']', e); + store.setDataSource(data); + } + catch (Exception e) { + throw new IgniteException("Failed to load bean in application context [beanName=" + dataSrcBean + + ", igniteConfig=" + appCtx + ']', e); + } } } @@ -105,43 +175,188 @@ public class CacheJdbcPojoStoreFactory<K, V> implements Factory<CacheJdbcPojoSto } /** - * Sets name of the data source bean. + * Sets data source. Data source should be fully configured and ready-to-use. * - * @param dataSrcBean Data source bean name. + * @param dataSrc Data source. * @return {@code This} for chaining. * @see CacheJdbcPojoStore#setDataSource(DataSource) */ - public CacheJdbcPojoStoreFactory<K, V> setDataSourceBean(String dataSrcBean) { - this.dataSrcBean = dataSrcBean; + public CacheJdbcPojoStoreFactory<K, V> setDataSource(DataSource dataSrc) { + this.dataSrc = dataSrc; return this; } /** - * Sets data source. Data source should be fully configured and ready-to-use. + * Get maximum batch size for delete and delete operations. * - * @param dataSrc Data source. + * @return Maximum batch size. + */ + public int getBatchSize() { + return batchSizw; + } + + /** + * Set maximum batch size for write and delete operations. + * + * @param batchSize Maximum batch size. * @return {@code This} for chaining. - * @see CacheJdbcPojoStore#setDataSource(DataSource) */ - public CacheJdbcPojoStoreFactory<K, V> setDataSource(DataSource dataSrc) { - this.dataSrc = dataSrc; + public CacheJdbcPojoStoreFactory setBatchSize(int batchSize) { + this.batchSizw = batchSize; + + return this; + } + + /** + * Gets name of the data source bean. + * + * @return Data source bean name. + */ + public String getDataSourceBean() { + return dataSrcBean; + } + + /** + * Sets name of the data source bean. + * + * @param dataSrcBean Data source bean name. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setDataSourceBean(String dataSrcBean) { + this.dataSrcBean = dataSrcBean; return this; } /** + * Get database dialect. + * + * @return Database dialect. + */ + public JdbcDialect getDialect() { + return dialect; + } + + /** * Set database dialect. * * @param dialect Database dialect. - * @see CacheJdbcPojoStore#setDialect(JdbcDialect) + * @return {@code This} for chaining. */ - public void setDialect(JdbcDialect dialect) { + public CacheJdbcPojoStoreFactory setDialect(JdbcDialect dialect) { this.dialect = dialect; + + return this; + } + + /** + * Get maximum workers thread count. These threads are responsible for queries execution. + * + * @return Maximum workers thread count. + */ + public int getMaximumPoolSize() { + return maxPoolSize; + } + + /** + * Set Maximum workers thread count. These threads are responsible for queries execution. + * + * @param maxPoolSize Max workers thread count. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setMaximumPoolSize(int maxPoolSize) { + this.maxPoolSize = maxPoolSize; + + return this; + } + + /** + * Gets maximum number of write attempts in case of database error. + * + * @return Maximum number of write attempts. + */ + public int getMaximumWriteAttempts() { + return maxWriteAttempts; + } + + /** + * Sets maximum number of write attempts in case of database error. + * + * @param maxWrtAttempts Number of write attempts. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setMaximumWriteAttempts(int maxWrtAttempts) { + this.maxWriteAttempts = maxWrtAttempts; + + return this; + } + + /** + * Parallel load cache minimum row count threshold. + * + * @return If {@code 0} then load sequentially. + */ + public int getParallelLoadCacheMinimumThreshold() { + return parallelLoadCacheMinThreshold; + } + + /** + * Parallel load cache minimum row count threshold. + * + * @param parallelLoadCacheMinThreshold Minimum row count threshold. If {@code 0} then load sequentially. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setParallelLoadCacheMinimumThreshold(int parallelLoadCacheMinThreshold) { + this.parallelLoadCacheMinThreshold = parallelLoadCacheMinThreshold; + + return this; + } + + /** + * Gets types known by store. + * + * @return Types known by store. + */ + public JdbcType[] getTypes() { + return types; + } + + /** + * Sets store configurations. + * + * @param types Store should process. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setTypes(JdbcType... types) { + this.types = types; + + return this; + } + + /** + * Gets hash code calculator. + * + * @return Hash code calculator. + */ + public JdbcTypeHasher getHasher() { + return hasher; + } + + /** + * Sets hash code calculator. + * + * @param hasher Hash code calculator. + * @return {@code This} for chaining. + */ + public CacheJdbcPojoStoreFactory setHasher(JdbcTypeHasher hasher) { + this.hasher = hasher; + + return this; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(CacheJdbcPojoStoreFactory.class, this); } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcType.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcType.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcType.java new file mode 100644 index 0000000..2107240 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcType.java @@ -0,0 +1,255 @@ +/* + * 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.cache.store.jdbc; + +import java.io.Serializable; +import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Description for type that could be stored into database by store. + */ +public class JdbcType implements Serializable { + /** */ + private static final long serialVersionUID = 0L; + + /** Cache name. */ + private String cacheName; + + /** Schema name in database. */ + private String dbSchema; + + /** Table name in database. */ + private String dbTbl; + + /** Key class used to store key in cache. */ + private String keyType; + + /** List of fields descriptors for key object. */ + @GridToStringInclude + private JdbcTypeField[] keyFields; + + /** Value class used to store value in cache. */ + private String valType; + + /** List of fields descriptors for value object. */ + @GridToStringInclude + private JdbcTypeField[] valFlds; + + /** Custom type hasher. */ + private JdbcTypeHasher hasher; + + /** + * Empty constructor (all values are initialized to their defaults). + */ + public JdbcType() { + /* No-op. */ + } + + /** + * Copy constructor. + * + * @param type Type to copy. + */ + public JdbcType(JdbcType type) { + cacheName = type.getCacheName(); + + dbSchema = type.getDatabaseSchema(); + dbTbl = type.getDatabaseTable(); + + keyType = type.getKeyType(); + keyFields = type.getKeyFields(); + + valType = type.getValueType(); + valFlds = type.getValueFields(); + } + + /** + * Gets associated cache name. + * + * @return Сache name. + */ + public String getCacheName() { + return cacheName; + } + + /** + * Sets associated cache name. + * + * @param cacheName Cache name. + */ + public JdbcType setCacheName(String cacheName) { + this.cacheName = cacheName; + + return this; + } + + /** + * Gets database schema name. + * + * @return Schema name. + */ + public String getDatabaseSchema() { + return dbSchema; + } + + /** + * Sets database schema name. + * + * @param dbSchema Schema name. + */ + public JdbcType setDatabaseSchema(String dbSchema) { + this.dbSchema = dbSchema; + + return this; + } + + /** + * Gets table name in database. + * + * @return Table name in database. + */ + public String getDatabaseTable() { + return dbTbl; + } + + /** + * Table name in database. + * + * @param dbTbl Table name in database. + * @return {@code this} for chaining. + */ + public JdbcType setDatabaseTable(String dbTbl) { + this.dbTbl = dbTbl; + + return this; + } + + /** + * Gets key type. + * + * @return Key type. + */ + public String getKeyType() { + return keyType; + } + + /** + * Sets key type. + * + * @param keyType Key type. + * @return {@code this} for chaining. + */ + public JdbcType setKeyType(String keyType) { + this.keyType = keyType; + + return this; + } + + /** + * Sets key type. + * + * @param cls Key type class. + * @return {@code this} for chaining. + */ + public JdbcType setKeyType(Class<?> cls) { + setKeyType(cls.getName()); + + return this; + } + + /** + * Gets value type. + * + * @return Key type. + */ + public String getValueType() { + return valType; + } + + /** + * Sets value type. + * + * @param valType Value type. + * @return {@code this} for chaining. + */ + public JdbcType setValueType(String valType) { + this.valType = valType; + + return this; + } + + /** + * Sets value type. + * + * @param cls Value type class. + * @return {@code this} for chaining. + */ + public JdbcType setValueType(Class<?> cls) { + setValueType(cls.getName()); + + return this; + } + + /** + * Gets optional persistent key fields (needed only if {@link CacheJdbcPojoStore} is used). + * + * @return Persistent key fields. + */ + public JdbcTypeField[] getKeyFields() { + return keyFields; + } + + /** + * Sets optional persistent key fields (needed only if {@link CacheJdbcPojoStore} is used). + * + * @param keyFlds Persistent key fields. + * @return {@code this} for chaining. + */ + public JdbcType setKeyFields(JdbcTypeField... keyFlds) { + this.keyFields = keyFlds; + + return this; + } + + /** + * Gets optional persistent value fields (needed only if {@link CacheJdbcPojoStore} is used). + * + * @return Persistent value fields. + */ + public JdbcTypeField[] getValueFields() { + return valFlds; + } + + /** + * Sets optional persistent value fields (needed only if {@link CacheJdbcPojoStore} is used). + * + * @param valFlds Persistent value fields. + * @return {@code this} for chaining. + */ + public JdbcType setValueFields(JdbcTypeField... valFlds) { + this.valFlds = valFlds; + + return this; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcType.class, this); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeDefaultHasher.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeDefaultHasher.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeDefaultHasher.java new file mode 100644 index 0000000..3baad3b --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeDefaultHasher.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.cache.store.jdbc; + +import java.util.Collection; + +/** + * Default implementation of {@link JdbcTypeHasher}. + * + * This implementation ignores type and field names. + */ +public class JdbcTypeDefaultHasher implements JdbcTypeHasher { + /** */ + private static final long serialVersionUID = 0L; + + /** Singleton instance to use. */ + public static final JdbcTypeHasher INSTANCE = new JdbcTypeDefaultHasher(); + + /** {@inheritDoc} */ + @Override public int hashCode(Collection<?> values) { + int hash = 0; + + for (Object val : values) + hash = 31 * hash + (val != null ? val.hashCode() : 0); + + return hash; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeField.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeField.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeField.java new file mode 100644 index 0000000..3396b81 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeField.java @@ -0,0 +1,172 @@ +/* + * 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.cache.store.jdbc; + +import java.io.Serializable; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Description of how field declared in database and in cache. + */ +public class JdbcTypeField implements Serializable { + /** */ + private static final long serialVersionUID = 0L; + + /** Field JDBC type in database. */ + private int dbFldType; + + /** Field name in database. */ + private String dbFldName; + + /** Field java type. */ + private Class<?> javaFldType; + + /** Field name in java object. */ + private String javaFldName; + + /** + * Default constructor. + */ + public JdbcTypeField() { + // No-op. + } + + /** + * Full constructor. + * + * @param dbFldType Field JDBC type in database. + * @param dbFldName Field name in database. + * @param javaFldType Field java type. + * @param javaFldName Field name in java object. + */ + public JdbcTypeField(int dbFldType, String dbFldName, Class<?> javaFldType, String javaFldName) { + this.dbFldType = dbFldType; + this.dbFldName = dbFldName; + this.javaFldType = javaFldType; + this.javaFldName = javaFldName; + } + + /** + * Copy constructor. + * + * @param field Field to copy. + */ + public JdbcTypeField(JdbcTypeField field) { + this(field.getDatabaseFieldType(), field.getDatabaseFieldName(), + field.getJavaFieldType(), field.getJavaFieldName()); + } + + /** + * @return Column JDBC type in database. + */ + public int getDatabaseFieldType() { + return dbFldType; + } + + /** + * @param dbFldType Column JDBC type in database. + * @return {@code this} for chaining. + */ + public JdbcTypeField setDatabaseFieldType(int dbFldType) { + this.dbFldType = dbFldType; + + return this; + } + + + /** + * @return Column name in database. + */ + public String getDatabaseFieldName() { + return dbFldName; + } + + /** + * @param dbFldName Column name in database. + * @return {@code this} for chaining. + */ + public JdbcTypeField setDatabaseFieldName(String dbFldName) { + this.dbFldName = dbFldName; + + return this; + } + + /** + * @return Field java type. + */ + public Class<?> getJavaFieldType() { + return javaFldType; + } + + /** + * @param javaFldType Corresponding java type. + * @return {@code this} for chaining. + */ + public JdbcTypeField setJavaFieldType(Class<?> javaFldType) { + this.javaFldType = javaFldType; + + return this; + } + + /** + * @return Field name in java object. + */ + public String getJavaFieldName() { + return javaFldName; + } + + /** + * @param javaFldName Field name in java object. + * @return {@code this} for chaining. + */ + public JdbcTypeField setJavaFieldName(String javaFldName) { + this.javaFldName = javaFldName; + + return this; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (!(o instanceof JdbcTypeField)) + return false; + + JdbcTypeField that = (JdbcTypeField)o; + + return dbFldType == that.dbFldType && dbFldName.equals(that.dbFldName) && + javaFldType == that.javaFldType && javaFldName.equals(that.javaFldName); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = dbFldType; + res = 31 * res + dbFldName.hashCode(); + + res = 31 * res + javaFldType.hashCode(); + res = 31 * res + javaFldName.hashCode(); + + return res; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcTypeField.class, this); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeHasher.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeHasher.java b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeHasher.java new file mode 100644 index 0000000..9d1fcea --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypeHasher.java @@ -0,0 +1,34 @@ +/* + * 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.cache.store.jdbc; + +import java.io.Serializable; +import java.util.Collection; + +/** + * API for implementing custom hashing logic for binary objects on server side. + */ +public interface JdbcTypeHasher extends Serializable { + /** + * Calculate hash code for specified object and fields. + * + * @param values Collection of values that should participate in hash code calculation. + * @return Hash code. + */ + public int hashCode(Collection<?> values); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java index fbe54e0..d0eeeb1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java @@ -260,6 +260,10 @@ public class GridQueryProcessor extends GridProcessorAdapter { if (F.isEmpty(meta.getValueType())) throw new IgniteCheckedException("Value type is not set: " + meta); + if (meta.getQueryFields().isEmpty() && meta.getAscendingFields().isEmpty() && + meta.getDescendingFields().isEmpty() && meta.getGroups().isEmpty()) + continue; + TypeDescriptor desc = new TypeDescriptor(); // Key and value classes still can be available if they are primitive or JDK part. @@ -2314,4 +2318,4 @@ public class GridQueryProcessor extends GridProcessorAdapter { private enum IndexType { ASC, DESC, TEXT } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCache.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCache.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCache.java index 6171ff8..1bbcf2f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCache.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCache.java @@ -229,7 +229,9 @@ public class VisorCache implements Serializable { protected void estimateMemorySize(IgniteEx ignite, GridCacheAdapter ca, int sample) throws IgniteCheckedException { int size = ca.size(); - Set<GridCacheEntryEx> set = ca.map().entries0(); + Set<GridCacheEntryEx> set = ca.context().isNear() + ? ((GridNearCacheAdapter)ca).dht().map().entries0() + : ca.map().entries0(); long memSz = 0; http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreAbstractSelfTest.java new file mode 100644 index 0000000..0f40bd9 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreAbstractSelfTest.java @@ -0,0 +1,395 @@ +/* + * 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.cache.store.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.store.jdbc.dialect.H2Dialect; +import org.apache.ignite.cache.store.jdbc.model.Person; +import org.apache.ignite.cache.store.jdbc.model.PersonKey; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.ConnectorConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.marshaller.Marshaller; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.h2.jdbcx.JdbcConnectionPool; + +import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; +import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; +import static org.apache.ignite.cache.CacheMode.PARTITIONED; + +/** + * Class for {@code PojoCacheStore} tests. + */ +public abstract class CacheJdbcPojoStoreAbstractSelfTest extends GridCommonAbstractTest { + /** IP finder. */ + protected static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** DB connection URL. */ + protected static final String DFLT_CONN_URL = "jdbc:h2:mem:TestDatabase;DB_CLOSE_DELAY=-1"; + + /** Organization count. */ + protected static final int ORGANIZATION_CNT = 1000; + + /** Person count. */ + protected static final int PERSON_CNT = 100000; + + /** Flag indicating that tests should use transactional cache. */ + protected static boolean transactional; + + /** Flag indicating that tests should use primitive classes like java.lang.Integer for keys. */ + protected static boolean builtinKeys; + + /** Flag indicating that classes for keys available on class path or not. */ + protected static boolean noKeyClasses; + + /** Flag indicating that classes for values available on class path or not. */ + protected static boolean noValClasses; + + /** + * @return Connection to test in-memory H2 database. + * @throws SQLException + */ + protected Connection getConnection() throws SQLException { + return DriverManager.getConnection(DFLT_CONN_URL, "sa", ""); + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + Connection conn = getConnection(); + + Statement stmt = conn.createStatement(); + + stmt.executeUpdate("DROP TABLE IF EXISTS Organization"); + stmt.executeUpdate("DROP TABLE IF EXISTS Person"); + + stmt.executeUpdate("CREATE TABLE Organization (" + + " id INTEGER PRIMARY KEY," + + " name VARCHAR(50)," + + " city VARCHAR(50))"); + + stmt.executeUpdate("CREATE TABLE Person (" + + " id INTEGER PRIMARY KEY," + + " org_id INTEGER," + + " name VARCHAR(50))"); + + conn.commit(); + + U.closeQuiet(stmt); + + fillSampleDatabase(conn); + + U.closeQuiet(conn); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + TcpDiscoverySpi disco = new TcpDiscoverySpi(); + + disco.setIpFinder(IP_FINDER); + + cfg.setDiscoverySpi(disco); + + cfg.setCacheConfiguration(cacheConfiguration()); + + cfg.setMarshaller(marshaller()); + + ConnectorConfiguration connCfg = new ConnectorConfiguration(); + cfg.setConnectorConfiguration(connCfg); + + return cfg; + } + + /** + * @return Marshaller to be used in test. + */ + protected abstract Marshaller marshaller(); + + /** + * @return Types to be used in test. + */ + protected JdbcType[] storeTypes() { + JdbcType[] storeTypes = new JdbcType[2]; + + storeTypes[0] = new JdbcType(); + storeTypes[0].setDatabaseSchema("PUBLIC"); + storeTypes[0].setDatabaseTable("ORGANIZATION"); + + if (builtinKeys) { + storeTypes[0].setKeyType("java.lang.Integer"); + storeTypes[0].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id")); + } + else { + storeTypes[0].setKeyType("org.apache.ignite.cache.store.jdbc.model.OrganizationKey" + (noKeyClasses ? "1" : "")); + storeTypes[0].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id")); + } + + storeTypes[0].setValueType("org.apache.ignite.cache.store.jdbc.model.Organization" + (noValClasses ? "1" : "")); + storeTypes[0].setValueFields( + new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"), + new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "name"), + new JdbcTypeField(Types.VARCHAR, "CITY", String.class, "city")); + + storeTypes[1] = new JdbcType(); + storeTypes[1].setDatabaseSchema("PUBLIC"); + storeTypes[1].setDatabaseTable("PERSON"); + + if (builtinKeys) { + storeTypes[1].setKeyType("java.lang.Long"); + storeTypes[1].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Long.class, "id")); + } + else { + storeTypes[1].setKeyType("org.apache.ignite.cache.store.jdbc.model.PersonKey" + (noKeyClasses ? "1" : "")); + storeTypes[1].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id")); + } + + storeTypes[1].setValueType("org.apache.ignite.cache.store.jdbc.model.Person" + (noValClasses ? "1" : "")); + storeTypes[1].setValueFields( + new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"), + new JdbcTypeField(Types.INTEGER, "ORG_ID", Integer.class, "orgId"), + new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "name")); + + return storeTypes; + } + + /** + * @return Cache configuration for test. + * @throws Exception In case when failed to create cache configuration. + */ + protected CacheConfiguration cacheConfiguration() throws Exception { + CacheConfiguration cc = defaultCacheConfiguration(); + + cc.setCacheMode(PARTITIONED); + cc.setAtomicityMode(transactional ? TRANSACTIONAL : ATOMIC); + cc.setSwapEnabled(false); + cc.setWriteBehindEnabled(false); + + CacheJdbcPojoStoreFactory<Object, Object> storeFactory = new CacheJdbcPojoStoreFactory<>(); + storeFactory.setDialect(new H2Dialect()); + storeFactory.setTypes(storeTypes()); + storeFactory.setDataSource(JdbcConnectionPool.create(DFLT_CONN_URL, "sa", "")); // H2 DataSource + + cc.setCacheStoreFactory(storeFactory); + cc.setReadThrough(true); + cc.setWriteThrough(true); + cc.setLoadPreviousValue(true); + + return cc; + } + + /** + * Fill in-memory database with sample data. + * + * @param conn Connection to database. + * @throws SQLException In case of filling database with sample data failed. + */ + protected void fillSampleDatabase(Connection conn) throws SQLException { + info("Start to fill sample database..."); + + PreparedStatement orgStmt = conn.prepareStatement("INSERT INTO Organization(id, name, city) VALUES (?, ?, ?)"); + + for (int i = 0; i < ORGANIZATION_CNT; i++) { + orgStmt.setInt(1, i); + orgStmt.setString(2, "name" + i); + orgStmt.setString(3, "city" + i % 10); + + orgStmt.addBatch(); + } + + orgStmt.executeBatch(); + + U.closeQuiet(orgStmt); + + conn.commit(); + + PreparedStatement prnStmt = conn.prepareStatement("INSERT INTO Person(id, org_id, name) VALUES (?, ?, ?)"); + + for (int i = 0; i < PERSON_CNT; i++) { + prnStmt.setInt(1, i); + prnStmt.setInt(2, i % 100); + prnStmt.setString(3, "name" + i); + + prnStmt.addBatch(); + } + + prnStmt.executeBatch(); + + conn.commit(); + + U.closeQuiet(prnStmt); + + info("Sample database prepared."); + } + + /** + * Start test grid with specified options. + * + * @param builtin {@code True} if keys are built in java types. + * @param noKeyCls {@code True} if keys classes are not on class path. + * @param noValCls {@code True} if values classes are not on class path. + * @param trn {@code True} if cache should be started in transactional mode. + * @throws Exception + */ + protected void startTestGrid(boolean builtin, boolean noKeyCls, boolean noValCls, boolean trn) throws Exception { + builtinKeys = builtin; + noKeyClasses = noKeyCls; + noValClasses = noValCls; + transactional = trn; + + startGrid(); + } + + /** + * Check that data was loaded correctly. + */ + protected void checkCacheContent() { + IgniteCache<Object, Object> c1 = grid().cache(null); + + c1.loadCache(null); + + assertEquals(ORGANIZATION_CNT + PERSON_CNT, c1.size()); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCache() throws Exception { + startTestGrid(false, false, false, false); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheTx() throws Exception { + startTestGrid(false, false, false, true); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCachePrimitiveKeys() throws Exception { + startTestGrid(true, false, false, false); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCachePrimitiveKeysTx() throws Exception { + startTestGrid(true, false, false, true); + + checkCacheContent(); + } + + /** + * Check put in cache and store it in db. + * + * @throws Exception If failed. + */ + private void checkPut() throws Exception { + IgniteCache<PersonKey, Person> c1 = grid().cache(null); + + Connection conn = getConnection(); + try { + PreparedStatement stmt = conn.prepareStatement("SELECT ID, ORG_ID, NAME FROM PERSON WHERE ID = ?"); + + stmt.setInt(1, -1); + + ResultSet rs = stmt.executeQuery(); + + assertFalse("Unexpected non empty result set", rs.next()); + + U.closeQuiet(rs); + + // Test put-insert. + PersonKey key = new PersonKey(-1); + + c1.put(key, new Person(-1, -2, "Person-to-test-put-insert", 999)); + + rs = stmt.executeQuery(); + + assertTrue("Unexpected empty result set", rs.next()); + + assertEquals(-1, rs.getInt(1)); + assertEquals(-2, rs.getInt(2)); + assertEquals("Person-to-test-put-insert", rs.getString(3)); + + assertFalse("Unexpected more data in result set", rs.next()); + + U.closeQuiet(rs); + + // Test put-update. + c1.put(key, new Person(-1, -3, "Person-to-test-put-update", 999)); + + rs = stmt.executeQuery(); + + assertTrue("Unexpected empty result set", rs.next()); + + assertEquals(-1, rs.getInt(1)); + assertEquals(-3, rs.getInt(2)); + assertEquals("Person-to-test-put-update", rs.getString(3)); + + assertFalse("Unexpected more data in result set", rs.next()); + + U.closeQuiet(rs); + } + finally { + U.closeQuiet(conn); + } + } + + /** + * @throws Exception If failed. + */ + public void testPut() throws Exception { + startTestGrid(false, false, false, false); + + checkPut(); + } + + /** + * @throws Exception If failed. + */ + public void testPutTx() throws Exception { + startTestGrid(false, false, false, true); + + checkPut(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreOptimizedMarshallerSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreOptimizedMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreOptimizedMarshallerSelfTest.java new file mode 100644 index 0000000..f40f7d7 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreOptimizedMarshallerSelfTest.java @@ -0,0 +1,31 @@ +/* + * 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.cache.store.jdbc; + +import org.apache.ignite.marshaller.Marshaller; +import org.apache.ignite.marshaller.optimized.OptimizedMarshaller; + +/** + * Class for {@code PojoCacheStore} tests. + */ +public class CacheJdbcPojoStoreOptimizedMarshallerSelfTest extends CacheJdbcPojoStoreAbstractSelfTest { + /** {@inheritDoc} */ + @Override protected Marshaller marshaller(){ + return new OptimizedMarshaller(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStorePortableMarshallerSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStorePortableMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStorePortableMarshallerSelfTest.java new file mode 100644 index 0000000..39504b1 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStorePortableMarshallerSelfTest.java @@ -0,0 +1,85 @@ +/* + * 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.cache.store.jdbc; + +import org.apache.ignite.marshaller.Marshaller; +import org.apache.ignite.marshaller.portable.BinaryMarshaller; + +/** + * Class for {@code PojoCacheStore} tests. + */ +public class CacheJdbcPojoStorePortableMarshallerSelfTest extends CacheJdbcPojoStoreAbstractSelfTest { + /** {@inheritDoc} */ + @Override protected Marshaller marshaller(){ + return new BinaryMarshaller(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoKeyClasses() throws Exception { + startTestGrid(false, true, false, false); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoKeyClassesTx() throws Exception { + startTestGrid(false, true, false, true); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoValueClasses() throws Exception { + startTestGrid(false, false, true, false); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoValueClassesTx() throws Exception { + startTestGrid(false, false, true, true); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoKeyAndValueClasses() throws Exception { + startTestGrid(false, true, true, false); + + checkCacheContent(); + } + + /** + * @throws Exception If failed. + */ + public void testLoadCacheNoKeyAndValueClassesTx() throws Exception { + startTestGrid(false, true, true, true); + + checkCacheContent(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java index 0ad2cad..d8f75d3 100644 --- a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java +++ b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java @@ -17,23 +17,19 @@ package org.apache.ignite.cache.store.jdbc; -import java.net.MalformedURLException; -import java.net.URL; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.sql.Types; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import javax.cache.integration.CacheWriterException; -import org.apache.ignite.IgniteException; -import org.apache.ignite.cache.CacheTypeMetadata; -import org.apache.ignite.cache.store.jdbc.dialect.BasicJdbcDialect; -import org.apache.ignite.cache.store.jdbc.dialect.JdbcDialect; + +import org.apache.ignite.cache.store.jdbc.dialect.H2Dialect; import org.apache.ignite.cache.store.jdbc.model.Organization; import org.apache.ignite.cache.store.jdbc.model.OrganizationKey; import org.apache.ignite.cache.store.jdbc.model.Person; @@ -41,16 +37,11 @@ import org.apache.ignite.cache.store.jdbc.model.PersonComplexKey; import org.apache.ignite.cache.store.jdbc.model.PersonKey; import org.apache.ignite.internal.processors.cache.CacheEntryImpl; import org.apache.ignite.internal.util.typedef.CI2; -import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiInClosure; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.cache.GridAbstractCacheStoreSelfTest; import org.h2.jdbcx.JdbcConnectionPool; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.UrlResource; /** * Class for {@code PojoCacheStore} tests. @@ -59,9 +50,6 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache /** DB connection URL. */ private static final String DFLT_CONN_URL = "jdbc:h2:mem:autoCacheStore;DB_CLOSE_DELAY=-1"; - /** Default config with mapping. */ - private static final String DFLT_MAPPING_CONFIG = "modules/core/src/test/config/store/jdbc/ignite-type-metadata.xml"; - /** Organization count. */ protected static final int ORGANIZATION_CNT = 1000; @@ -77,71 +65,86 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache /** {@inheritDoc} */ @Override protected CacheJdbcPojoStore<Object, Object> store() { - CacheJdbcPojoStore<Object, Object> store = new CacheJdbcPojoStore<>(); - -// PGPoolingDataSource ds = new PGPoolingDataSource(); -// ds.setUser("postgres"); -// ds.setPassword("postgres"); -// ds.setServerName("ip"); -// ds.setDatabaseName("postgres"); -// store.setDataSource(ds); - -// MysqlDataSource ds = new MysqlDataSource(); -// ds.setURL("jdbc:mysql://ip:port/dbname"); -// ds.setUser("mysql"); -// ds.setPassword("mysql"); - + CacheJdbcPojoStoreFactory<Object, Object> storeFactory = new CacheJdbcPojoStoreFactory<>(); + + JdbcType[] storeTypes = new JdbcType[6]; + + storeTypes[0] = new JdbcType(); + storeTypes[0].setDatabaseSchema("PUBLIC"); + storeTypes[0].setDatabaseTable("ORGANIZATION"); + storeTypes[0].setKeyType("org.apache.ignite.cache.store.jdbc.model.OrganizationKey"); + storeTypes[0].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id")); + + storeTypes[0].setValueType("org.apache.ignite.cache.store.jdbc.model.Organization"); + storeTypes[0].setValueFields( + new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"), + new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "name"), + new JdbcTypeField(Types.VARCHAR, "CITY", String.class, "city")); + + storeTypes[1] = new JdbcType(); + storeTypes[1].setDatabaseSchema("PUBLIC"); + storeTypes[1].setDatabaseTable("PERSON"); + storeTypes[1].setKeyType("org.apache.ignite.cache.store.jdbc.model.PersonKey"); + storeTypes[1].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id")); + + storeTypes[1].setValueType("org.apache.ignite.cache.store.jdbc.model.Person"); + storeTypes[1].setValueFields( + new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"), + new JdbcTypeField(Types.INTEGER, "ORG_ID", Integer.class, "orgId"), + new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "name")); + + storeTypes[2] = new JdbcType(); + storeTypes[2].setDatabaseSchema("PUBLIC"); + storeTypes[2].setDatabaseTable("PERSON_COMPLEX"); + storeTypes[2].setKeyType("org.apache.ignite.cache.store.jdbc.model.PersonComplexKey"); + storeTypes[2].setKeyFields( + new JdbcTypeField(Types.INTEGER, "ID", int.class, "id"), + new JdbcTypeField(Types.INTEGER, "ORG_ID", int.class, "orgId"), + new JdbcTypeField(Types.INTEGER, "CITY_ID", int.class, "cityId")); + + storeTypes[2].setValueType("org.apache.ignite.cache.store.jdbc.model.Person"); + storeTypes[2].setValueFields( + new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"), + new JdbcTypeField(Types.INTEGER, "ORG_ID", Integer.class, "orgId"), + new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "name"), + new JdbcTypeField(Types.INTEGER, "SALARY", Integer.class, "salary")); + + storeTypes[3] = new JdbcType(); + storeTypes[3].setDatabaseSchema("PUBLIC"); + storeTypes[3].setDatabaseTable("TIMESTAMP_ENTRIES"); + storeTypes[3].setKeyType("java.sql.Timestamp"); + storeTypes[3].setKeyFields(new JdbcTypeField(Types.TIMESTAMP, "KEY", Timestamp.class, null)); + + storeTypes[3].setValueType("java.lang.Integer"); + storeTypes[3].setValueFields(new JdbcTypeField(Types.INTEGER, "VAL", Integer.class, null)); + + storeTypes[4] = new JdbcType(); + storeTypes[4].setDatabaseSchema("PUBLIC"); + storeTypes[4].setDatabaseTable("STRING_ENTRIES"); + storeTypes[4].setKeyType("java.lang.String"); + storeTypes[4].setKeyFields(new JdbcTypeField(Types.VARCHAR, "KEY", String.class, null)); + + storeTypes[4].setValueType("java.lang.String"); + storeTypes[4].setValueFields(new JdbcTypeField(Types.VARCHAR, "VAL", Integer.class, null)); + + storeTypes[5] = new JdbcType(); + storeTypes[5].setDatabaseSchema("PUBLIC"); + storeTypes[5].setDatabaseTable("UUID_ENTRIES"); + storeTypes[5].setKeyType("java.util.UUID"); + storeTypes[5].setKeyFields(new JdbcTypeField(Types.BINARY, "KEY", UUID.class, null)); + + storeTypes[5].setValueType("java.util.UUID"); + storeTypes[5].setValueFields(new JdbcTypeField(Types.BINARY, "VAL", UUID.class, null)); + + storeFactory.setTypes(storeTypes); + + storeFactory.setDialect(new H2Dialect()); + + CacheJdbcPojoStore<Object, Object> store = storeFactory.create(); + + // H2 DataSource store.setDataSource(JdbcConnectionPool.create(DFLT_CONN_URL, "sa", "")); - URL cfgUrl; - - try { - cfgUrl = new URL(DFLT_MAPPING_CONFIG); - } - catch (MalformedURLException ignore) { - cfgUrl = U.resolveIgniteUrl(DFLT_MAPPING_CONFIG); - } - - if (cfgUrl == null) - throw new IgniteException("Failed to resolve metadata path: " + DFLT_MAPPING_CONFIG); - - try { - GenericApplicationContext springCtx = new GenericApplicationContext(); - - new XmlBeanDefinitionReader(springCtx).loadBeanDefinitions(new UrlResource(cfgUrl)); - - springCtx.refresh(); - - Collection<CacheTypeMetadata> typeMeta = springCtx.getBeansOfType(CacheTypeMetadata.class).values(); - - Map<Integer, Map<Object, CacheAbstractJdbcStore.EntryMapping>> cacheMappings = new HashMap<>(); - - JdbcDialect dialect = store.resolveDialect(); - - GridTestUtils.setFieldValue(store, CacheAbstractJdbcStore.class, "dialect", dialect); - - Map<Object, CacheAbstractJdbcStore.EntryMapping> entryMappings = U.newHashMap(typeMeta.size()); - - for (CacheTypeMetadata type : typeMeta) - entryMappings.put(store.keyTypeId(type.getKeyType()), - new CacheAbstractJdbcStore.EntryMapping(null, dialect, type)); - - store.prepareBuilders(null, typeMeta); - - cacheMappings.put(null, entryMappings); - - GridTestUtils.setFieldValue(store, CacheAbstractJdbcStore.class, "cacheMappings", cacheMappings); - } - catch (BeansException e) { - if (X.hasCause(e, ClassNotFoundException.class)) - throw new IgniteException("Failed to instantiate Spring XML application context " + - "(make sure all classes used in Spring configuration are present at CLASSPATH) " + - "[springUrl=" + cfgUrl + ']', e); - else - throw new IgniteException("Failed to instantiate Spring XML application context [springUrl=" + - cfgUrl + ", err=" + e.getMessage() + ']', e); - } - return store; } @@ -224,7 +227,6 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache super.beforeTest(); } - /** * @throws Exception If failed. */ @@ -274,7 +276,7 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache if (i > 0) prnComplexStmt.setInt(5, 1000 + i * 500); else // Add person with null salary - prnComplexStmt.setNull(5, java.sql.Types.INTEGER); + prnComplexStmt.setNull(5, Types.INTEGER); prnComplexStmt.addBatch(); } @@ -302,9 +304,9 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache Person val = (Person)v; - assert key.getId() == val.getId(); - assert key.getOrgId() == val.getOrgId(); - assertEquals("name" + key.getId(), val.getName()); + assertTrue("Key ID should be the same as value ID", key.getId() == val.getId()); + assertTrue("Key orgID should be the same as value orgID", key.getOrgId() == val.getOrgId()); + assertEquals("name" + key.getId(), val.getName()); prnComplexKeys.add((PersonComplexKey)k); } @@ -351,25 +353,23 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache * @throws Exception If failed. */ public void testWriteRetry() throws Exception { + CacheJdbcPojoStore<Object, Object> store = store(); + // Special dialect that will skip updates, to test write retry. - BasicJdbcDialect dialect = new BasicJdbcDialect() { + store.setDialect(new H2Dialect() { /** {@inheritDoc} */ - @Override public String updateQuery(String tblName, Collection<String> keyCols, Iterable<String> valCols) { - return super.updateQuery(tblName, keyCols, valCols) + " AND 1 = 0"; + @Override public boolean hasMerge() { + return false; } - }; - - store.setDialect(dialect); - - Map<String, Map<Object, CacheAbstractJdbcStore.EntryMapping>> cacheMappings = - GridTestUtils.getFieldValue(store, CacheAbstractJdbcStore.class, "cacheMappings"); - - CacheAbstractJdbcStore.EntryMapping em = cacheMappings.get(null).get(OrganizationKey.class); - CacheTypeMetadata typeMeta = GridTestUtils.getFieldValue(em, CacheAbstractJdbcStore.EntryMapping.class, "typeMeta"); + /** {@inheritDoc} */ + @Override public String updateQuery(String tblName, Collection<String> keyCols, + Iterable<String> valCols) { + return super.updateQuery(tblName, keyCols, valCols) + " AND 1 = 0"; + } + }); - cacheMappings.get(null).put(OrganizationKey.class, - new CacheAbstractJdbcStore.EntryMapping(null, dialect, typeMeta)); + inject(store); Connection conn = store.openConnection(false); @@ -392,6 +392,8 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache try { store.write(new CacheEntryImpl<>(k1, v1)); + + fail("CacheWriterException wasn't thrown."); } catch (CacheWriterException e) { if (!e.getMessage().startsWith("Failed insert entry in database, violate a unique index or primary key") || @@ -418,4 +420,4 @@ public class CacheJdbcPojoStoreTest extends GridAbstractCacheStoreSelfTest<Cache assertNull(store.load(k)); } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d71f6129/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcStoreAbstractMultithreadedSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcStoreAbstractMultithreadedSelfTest.java b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcStoreAbstractMultithreadedSelfTest.java index 757cedd..4c4bd58 100644 --- a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcStoreAbstractMultithreadedSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcStoreAbstractMultithreadedSelfTest.java @@ -308,4 +308,4 @@ public abstract class CacheJdbcStoreAbstractMultithreadedSelfTest<T extends Cach } }, 8, "tx"); } -} \ No newline at end of file +}
