ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject incubator-ignite git commit: # IGNITE-32 WIP: Store implementation.
Date Tue, 13 Jan 2015 03:32:23 GMT
Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-32 a565ef4dc -> 1b2c8f56b


# IGNITE-32 WIP: Store implementation.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/1b2c8f56
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/1b2c8f56
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/1b2c8f56

Branch: refs/heads/ignite-32
Commit: 1b2c8f56bb4400f289c746d0e9d9207ffdd2034d
Parents: a565ef4
Author: AKuznetsov <akuznetsov@gridgain.com>
Authored: Tue Jan 13 10:32:36 2015 +0700
Committer: AKuznetsov <akuznetsov@gridgain.com>
Committed: Tue Jan 13 10:32:36 2015 +0700

----------------------------------------------------------------------
 .../impl/GridRouterCommandLineStartup.java      |   20 +-
 .../grid/cache/store/auto/AutoCacheStore.java   | 1080 ++++++++++++------
 .../grid/cache/store/auto/H2PojoCacheStore.java |   53 +
 .../grid/cache/store/auto/JdbcMapper.java       |   40 +
 .../grid/cache/store/auto/PojoJdbcMapper.java   |  157 +++
 .../processors/spring/GridSpringProcessor.java  |    6 +-
 .../auto/AbstractAutoCacheStoreSelfTest.java    |   74 +-
 .../store/auto/AutoCacheStoreSelfTest.java      |    6 +-
 .../spring/GridSpringProcessorImpl.java         |   21 +-
 9 files changed, 1028 insertions(+), 429 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/client/router/impl/GridRouterCommandLineStartup.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/client/router/impl/GridRouterCommandLineStartup.java b/modules/core/src/main/java/org/gridgain/client/router/impl/GridRouterCommandLineStartup.java
index 4a03f9c..b8e890a 100644
--- a/modules/core/src/main/java/org/gridgain/client/router/impl/GridRouterCommandLineStartup.java
+++ b/modules/core/src/main/java/org/gridgain/client/router/impl/GridRouterCommandLineStartup.java
@@ -13,7 +13,6 @@ import org.apache.ignite.*;
 import org.apache.ignite.lang.*;
 import org.apache.ignite.lifecycle.*;
 import org.gridgain.client.router.*;
-import org.gridgain.grid.*;
 import org.gridgain.grid.kernal.processors.spring.*;
 import org.gridgain.grid.util.typedef.*;
 import org.gridgain.grid.util.typedef.internal.*;
@@ -37,12 +36,23 @@ public class GridRouterCommandLineStartup {
     private LifecycleAware tcpRouter;
 
     /**
+     * Gets first bean configuration.
+     * @param beans a Map containing the bean names as keys and the corresponding bean instances as values
+     * @return Spring bean.
+     */
+    private static <T> T firstBean(Map<String, Object> beans) {
+        Map.Entry<String, Object> entry = F.firstEntry(beans);
+
+        return entry == null ? null : (T)entry.getValue();
+    }
+
+    /**
      * Search given context for required configuration and starts router.
      *
      * @param beans Beans loaded from spring configuration file.
      */
-    public void start(Map<Class<?>, Object> beans) {
-        log = (IgniteLogger)beans.get(IgniteLogger.class);
+    public void start(Map<Class<?>, Map<String, Object>> beans) {
+        log = firstBean(beans.get(IgniteLogger.class));
 
         if (log == null) {
             U.error(log, "Failed to find logger definition in application context. Stopping the router.");
@@ -50,7 +60,7 @@ public class GridRouterCommandLineStartup {
             return;
         }
 
-        GridTcpRouterConfiguration tcpCfg = (GridTcpRouterConfiguration)beans.get(GridTcpRouterConfiguration.class);
+        GridTcpRouterConfiguration tcpCfg = firstBean(beans.get(GridTcpRouterConfiguration.class));
 
         if (tcpCfg == null)
             U.warn(log, "TCP router startup skipped (configuration not found).");
@@ -131,7 +141,7 @@ public class GridRouterCommandLineStartup {
         else
             savedHnds = U.addJavaNoOpLogger();
 
-        Map<Class<?>, Object> beans;
+        Map<Class<?>, Map<String, Object>> beans;
 
         try {
             beans = spring.loadBeans(cfgUrl, IgniteLogger.class, GridTcpRouterConfiguration.class);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java
index 2c8ef73..aeff437 100644
--- a/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java
+++ b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java
@@ -31,152 +31,114 @@ import java.util.concurrent.atomic.*;
 import static org.gridgain.grid.kernal.GridComponentType.*;
 
 /**
- * {@link GridCacheStore} implementation backed by JDBC.
+ * Base {@link GridCacheStore} implementation backed by JDBC.
  * This implementation stores objects in underlying database using mapping description.
  */
-public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
+public abstract class AutoCacheStore<K, V> implements GridCacheStore<K, V> {
     /**
-     * Internal mapping description.
+     * Type mapping cache.
      */
-    private static class TypeMetadata {
+    protected class TypeCache {
         /** Select all items query. */
-        private final String loadCacheQry;
+        protected final String loadCacheQry;
 
-        /** Select one item query. */
+        /** Select item query. */
+        protected final String loadQrySingle;
+
+        /** Select items query. */
         private final String loadQry;
 
-        /** Insert item query. */
-        private final String insQry;
+        /** Put item(s) query. */
+        protected final String putQry;
 
-        /** Update item query. */
-        private final String updQry;
+        /** Remove item(s) query. */
+        protected final String remQry;
 
-        /** Delete one item query. */
-        private final String delQry;
+        /** Batch size for load query. */
+        protected final int loadBatchSize;
 
-        /** Key type. */
-        private final Class<?> keyType;
+        /** Database table name. */
+        private final String tblName;
 
-        /** Value type. */
-        private final Class<?> valType;
+        /** Database key columns. */
+        private final Collection<String> keyCols;
 
-        /** Key fields. */
-        private final Collection<GridCacheQueryTypeDescriptor> keyFields;
+        /** Database value columns. */
+        private final Collection<String> valCols;
 
-        /** Value fields. */
-        private final Collection<GridCacheQueryTypeDescriptor> valFields;
+        /** Database unique columns.  */
+        private final Set<String> uniqCols;
 
-        /** Unique fields from key and value. */
-        private final Collection<GridCacheQueryTypeDescriptor> uniqFields;
+        /** Mapper for key. */
+        protected JdbcMapper<K> keyMapper;
 
-        /** Value fields without key fields. */
-        private final Collection<GridCacheQueryTypeDescriptor> updFields;
+        /** Mapper for value. */
+        protected JdbcMapper<V> valMapper;
 
         /**
-         * @param dsc Cache type metadata.
+         *
+         * @param m Type metadata.
+         * @param keyMapper Mapper for key.
+         * @param valMapper Mapper for value.
          */
-        private TypeMetadata(GridCacheQueryTypeMetadata dsc) throws IgniteCheckedException {
-            try {
-                keyType = Class.forName(dsc.getKeyType());
-                valType = Class.forName(dsc.getType());
-            }
-            catch (ClassNotFoundException e) {
-                throw new IgniteCheckedException("Failed to initialize cache store (POJO classes is not provided).", e);
-            }
-
-            keyFields = dsc.getKeyDescriptors();
-
-            valFields = dsc.getValueDescriptors();
-
-            uniqFields = U.newLinkedHashSet(keyFields.size() + valFields.size());
-            uniqFields.addAll(keyFields);
-            uniqFields.addAll(valFields);
+        protected TypeCache(GridCacheQueryTypeMetadata m, JdbcMapper<K> keyMapper, JdbcMapper<V> valMapper) {
+            keyCols = databaseColumns(m.getKeyDescriptors());
 
-            updFields = new HashSet<>(valFields);
-            updFields.removeAll(keyFields);
+            valCols = databaseColumns(m.getValueDescriptors());
 
-            String uniqCols = databaseColumns(uniqFields);
+            uniqCols = U.newLinkedHashSet(keyCols.size() + valCols.size());
+            uniqCols.addAll(keyCols);
+            uniqCols.addAll(valCols);
 
-            String where = databaseParameters(keyFields, " OR ");
+            tblName = String.format("%s.%s", m.getSchema(), m.getTableName());
 
-            String values = concat(uniqFields, new C1<GridCacheQueryTypeDescriptor, String>() {
-                @Override public String apply(GridCacheQueryTypeDescriptor desc) {
-                    return "?";
-                }
-            }, ", ");
+            loadCacheQry = loadCacheQuery(tblName, uniqCols);
 
-            String updCols = databaseParameters(updFields, ", ");
+            loadQrySingle = loadQuery(tblName, keyCols, valCols, 1);
 
-            String tblName = String.format("%s.%s", dsc.getSchema(), dsc.getTableName());
+            loadBatchSize = MAX_QRY_PARAMETERS / keyCols.size();
 
-            loadCacheQry = String.format("SELECT %s FROM %s", uniqCols, tblName);
+            loadQry = loadQuery(tblName, keyCols, uniqCols, loadBatchSize);
 
-            loadQry = String.format("SELECT %s FROM %s WHERE %s", databaseColumns(valFields), tblName, where);
+            putQry = putQuery(tblName, keyCols, uniqCols);
 
-            insQry = String.format("INSERT INTO %s (%s) values (%s)", tblName, uniqCols, values);
+            remQry = removeQuery(tblName, keyCols);
 
-            updQry = String.format("UPDATE %s SET %s WHERE %s", tblName, updCols, where);
+            this.keyMapper = keyMapper;
 
-            delQry = String.format("DELETE FROM %s WHERE %s", tblName, where);
+            this.valMapper = valMapper;
         }
 
         /**
-         * Concatenates elements using provided delimiter.
-         *
-         * @param elems Concatenated elements.
-         * @param f closure used for transform element.
-         * @param delim Delimiter.
-         * @return Concatenated string.
+         * Construct query for select values with key count less or equal {@code loadBatchSize}
+         * @param keyCnt Key count.
          */
-        private static <T> String concat(Iterable<T> elems, C1<T, String> f, String delim) {
-            SB sb = new SB();
-
-            boolean first = true;
-
-            for (T elem : elems) {
-                if (!first)
-                    sb.a(delim);
+        protected String loadQueryLast(int keyCnt) {
+            assert keyCnt > loadBatchSize;
 
-                sb.a(f.apply(elem));
-
-                first = false;
-            }
-
-            return sb.toString();
-        }
+            if (keyCnt == loadBatchSize)
+                return loadQry;
 
-        /**
-         * @param fields Mapped fields.
-         * @param delim Delimiter
-         * @return Database parameters separated by delimiter.
-         */
-        private static String databaseParameters(Iterable<GridCacheQueryTypeDescriptor> fields, String delim) {
-            return concat(fields, new C1<GridCacheQueryTypeDescriptor, String>() {
-                @Override public String apply(GridCacheQueryTypeDescriptor desc) {
-                    return desc.getDbName() + "=?";
-                }
-            }, delim);
-        }
+            if (keyCnt == 1)
+                return loadQrySingle;
 
-        /**
-         * @param fields Mapped fields.
-         * @return Database columns separated by comma.
-         */
-        private static String databaseColumns(Iterable<GridCacheQueryTypeDescriptor> fields) {
-            return concat(fields, new C1<GridCacheQueryTypeDescriptor, String>() {
-                @Override public String apply(GridCacheQueryTypeDescriptor desc) {
-                    return desc.getDbName();
-                }
-            }, ", ");
+            return loadQuery(tblName, keyCols, uniqCols, keyCnt);
         }
     }
 
+    /** Max query parameters count. */
+    protected static final int MAX_QRY_PARAMETERS = 2000;
+
     /** Connection attribute name. */
-    private static final String ATTR_CONN = "AUTO_STORE_CONNECTION";
+    protected static final String ATTR_CONN = "JDBC_STORE_CONNECTION";
 
-    /** Log. */
+    /** Auto-injected grid instance. */
+    @IgniteInstanceResource
+    protected Ignite ignite;
+
+    /** Auto-injected logger instance. */
     @IgniteLoggerResource
-    private IgniteLogger log;
+    protected IgniteLogger log;
 
     /** Init guard. */
     @GridToStringExclude
@@ -190,272 +152,36 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
     private boolean initOk;
 
     /** Data source. */
-    private DataSource dataSrc;
+    protected DataSource dataSrc;
 
     /** Connection URL. */
-    private String connUrl;
+    protected String connUrl;
 
     /** User name for database access. */
-    private String user;
+    protected String user;
 
     /** Password for database access. */
     @GridToStringExclude
-    private String passwd;
-
-    /** Path to metadata xmls. */
-    private Collection<String> metaPaths;
-
-    /** Types cache. */
-    private Map<Class<?>, TypeMetadata> types;
-
-    /**
-     * @param stmt Query.
-     * @param fields1 Mapped fields1.
-     * @param obj1 Source object1.
-     * @param fields2 Mapped fields2.
-     * @param obj2 Source object2.
-     */
-    private static void fill(PreparedStatement stmt, Iterable<GridCacheQueryTypeDescriptor> fields1, Object obj1,
-        Iterable<GridCacheQueryTypeDescriptor> fields2, Object obj2) throws SQLException {
-        int i = 1;
-
-        for (GridCacheQueryTypeDescriptor field : fields1)
-            stmt.setObject(i++, U.field(obj1, field.getJavaName()));
-
-        for (GridCacheQueryTypeDescriptor field : fields2)
-            stmt.setObject(i++, U.field(obj2, field.getJavaName()));
-    }
-
-    /**
-     * @param stmt Query.
-     * @param fields Mapped fields.
-     * @param obj Source object.
-     */
-    private static void fill(PreparedStatement stmt, Iterable<GridCacheQueryTypeDescriptor> fields, Object obj)
-        throws SQLException {
-        int i = 1;
-
-        for (GridCacheQueryTypeDescriptor field : fields)
-            stmt.setObject(i++, U.field(obj, field.getJavaName()));
-    }
-
-    /**
-     * Capitalizes the first character of the given string.
-     *
-     * @param str String.
-     * @return String with capitalized first character.
-     */
-    private static String capitalFirst(@Nullable String str) {
-        return str == null ? null :
-            str.isEmpty() ? "" : Character.toUpperCase(str.charAt(0)) + str.substring(1);
-    }
-
-    /**
-     * Map table row to object.
-     *
-     * @param rs resultSet.
-     * @param type Type of target object.
-     * @param fields Mapped fields.
-     */
-    public static Object newInstance(ResultSet rs, Class<?> type,
-        Iterable<GridCacheQueryTypeDescriptor> fields) throws IgniteCheckedException, SQLException {
-        Object key = U.newInstance(type);
-
-        assert key != null;
-
-        for (GridCacheQueryTypeDescriptor field : fields) {
-            U.invoke(key.getClass(), key, "set" + capitalFirst(field.getJavaName()),
-                new Class[] {field.getJavaType()}, rs.getObject(field.getDbName()));
-        }
-
-        return key;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void loadCache(IgniteBiInClosure<Object, Object> clo, @Nullable Object... args)
-        throws IgniteCheckedException {
-        init();
-
-        if (log.isDebugEnabled())
-            log.debug("Loading all values from db");
-
-        Connection conn = null;
-
-        try {
-            for (TypeMetadata type : types.values()) {
-                PreparedStatement stmt = null;
-
-                try {
-                    conn = connection(null);
-
-                    stmt = conn.prepareStatement(type.loadCacheQry);
-
-                    ResultSet rs = stmt.executeQuery();
-
-                    while (rs.next()) {
-                        Object key = newInstance(rs, type.keyType, type.keyFields);
-                        Object val = newInstance(rs, type.valType, type.valFields);
-
-                        clo.apply(key, val);
-                    }
-                }
-                catch (SQLException e) {
-                    throw new IgniteCheckedException("Failed to load object: " + type.valType, e);
-                }
-                finally {
-                    U.closeQuiet(stmt);
-                }
-            }
-        }
-        finally {
-            closeConnection(conn);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Nullable @Override public Object load(@Nullable IgniteTx tx, Object key) throws IgniteCheckedException {
-        init();
-
-        TypeMetadata type = types.get(key.getClass());
-
-        if (type == null)
-            throw new IgniteCheckedException("Failed to find metadata for type: " + key.getClass());
-
-        if (log.isDebugEnabled())
-            log.debug("Loading value from db: " + type.valType);
-
-        Connection conn = null;
-
-        PreparedStatement stmt = null;
-
-        try {
-            conn = connection(tx);
-
-            stmt = conn.prepareStatement(type.loadQry);
-
-            fill(stmt, type.keyFields, key);
-
-            ResultSet rs = stmt.executeQuery();
-
-            if (rs.next())
-                return newInstance(rs, type.valType, type.valFields);
-        }
-        catch (SQLException e) {
-            throw new IgniteCheckedException("Failed to load object: " + type.valType, e);
-        }
-        finally {
-            end(tx, conn, stmt);
-        }
-
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void put(@Nullable IgniteTx tx, Object key, Object val) throws IgniteCheckedException {
-        init();
-
-        TypeMetadata type = types.get(key.getClass());
-
-        if (type == null)
-            throw new IgniteCheckedException("Failed to find metadata for type: " + key.getClass());
-
-        if (log.isDebugEnabled())
-            log.debug("Put value to db: " + type.valType);
-
-        Connection conn = null;
-
-        PreparedStatement stmt = null;
-
-        try {
-            conn = connection(tx);
-
-            stmt = conn.prepareStatement(type.updQry);
+    protected String passwd;
 
-            fill(stmt, type.updFields, val, type.keyFields, key);
+    /** Execute. */
+    protected ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
 
-            if (stmt.executeUpdate() == 0) {
-                stmt.close();
+    /** Paths to xml with type mapping description. */
+    protected Collection<String> typeMetadataPaths;
 
-                stmt = conn.prepareStatement(type.insQry);
+    /** Type mapping description. */
+    protected Collection<GridCacheQueryTypeMetadata> typeMetadata;
 
-                fill(stmt, type.keyFields, key, type.updFields, val);
-
-                stmt.executeUpdate();
-            }
-        }
-        catch (SQLException e) {
-            throw new IgniteCheckedException("Failed to load object: " + type.valType, e);
-        }
-        finally {
-            end(tx, conn, stmt);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override public void remove(@Nullable IgniteTx tx, Object key) throws IgniteCheckedException {
-        init();
-
-        TypeMetadata type = types.get(key.getClass());
-
-        if (type == null)
-            throw new IgniteCheckedException("Failed to find metadata for type: " + key.getClass());
-
-        if (log.isDebugEnabled())
-            log.debug("Removing value from db: " + type.valType);
-
-        Connection conn = null;
-
-        PreparedStatement stmt = null;
-
-        try {
-            conn = connection(tx);
-
-            stmt = conn.prepareStatement(type.delQry);
-
-            fill(stmt, type.keyFields, key);
-
-            stmt.executeUpdate();
-        }
-        catch (SQLException e) {
-            throw new IgniteCheckedException("Failed to load object: " + type.valType, e);
-        }
-        finally {
-            end(tx, conn, stmt);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override public void txEnd(IgniteTx tx, boolean commit) throws IgniteCheckedException {
-        init();
-
-        Connection conn = tx.removeMeta(ATTR_CONN);
-
-        if (conn != null) {
-            try {
-                if (commit)
-                    conn.commit();
-                else
-                    conn.rollback();
-            }
-            catch (SQLException e) {
-                throw new IgniteCheckedException(
-                    "Failed to end transaction [xid=" + tx.xid() + ", commit=" + commit + ']', e);
-            }
-            finally {
-                closeConnection(conn);
-            }
-        }
-
-        if (log.isDebugEnabled())
-            log.debug("Transaction ended [xid=" + tx.xid() + ", commit=" + commit + ']');
-    }
+    /** Type cache. */
+    protected Map<Object, TypeCache> typesCache;
 
     /**
      * Initializes store.
      *
      * @throws IgniteCheckedException If failed to initialize.
      */
-    private void init() throws IgniteCheckedException {
+    protected void init() throws IgniteCheckedException {
         if (initLatch.getCount() > 0) {
             if (initGuard.compareAndSet(false, true)) {
                 if (log.isDebugEnabled())
@@ -465,32 +191,36 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
                     throw new IgniteCheckedException("Failed to initialize cache store (connection is not provided).");
 
                 try {
-                    GridSpringProcessor spring = SPRING.create(false);
-
-                    if (types == null) {
-                        if (metaPaths == null)
+                    if (typeMetadata == null) {
+                        if (typeMetadataPaths == null)
                             throw new IgniteCheckedException(
                                 "Failed to initialize cache store (metadata paths is not provided).");
 
+                        GridSpringProcessor spring = SPRING.create(false);
+
                         Collection<GridCacheQueryTypeMetadata> typeMeta = new ArrayList<>();
 
-                        for (String path : metaPaths) {
+                        for (String path : typeMetadataPaths) {
                             URL url = U.resolveGridGainUrl(path);
 
                             if (url != null) {
-                                Map<Class<?>, Object> beans = spring.loadBeans(url, GridCacheQueryTypeMetadata.class);
+                                Map<String, Object> beans = spring.loadBeans(url, GridCacheQueryTypeMetadata.class).
+                                    get(GridCacheQueryTypeMetadata.class);
 
-                                for (Object bean : beans.values())
-                                    if (bean instanceof GridCacheQueryTypeMetadata)
-                                        typeMeta.add((GridCacheQueryTypeMetadata)bean);
+                                if (beans != null)
+                                    for (Object bean : beans.values())
+                                        if (bean instanceof GridCacheQueryTypeMetadata)
+                                            typeMeta.add((GridCacheQueryTypeMetadata)bean);
                             }
                             else
                                 log.warning("Failed to resolve metadata path: " + path);
                         }
 
-                        setTypes(typeMeta);
+                        setTypeMetadata(typeMeta);
                     }
 
+                    buildTypeCache();
+
                     initOk = true;
                 }
                 finally {
@@ -512,7 +242,7 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
      * @param conn Allocated connection.
      * @param st Created statement,
      */
-    private void end(@Nullable IgniteTx tx, Connection conn, Statement st) {
+    protected void end(@Nullable IgniteTx tx, Connection conn, Statement st) {
         U.closeQuiet(st);
 
         if (tx == null)
@@ -527,7 +257,7 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
      * @return Pooled connection.
      * @throws SQLException In case of error.
      */
-    private Connection openConnection(boolean autocommit) throws SQLException {
+    protected Connection openConnection(boolean autocommit) throws SQLException {
         Connection conn = dataSrc != null ? dataSrc.getConnection() :
             DriverManager.getConnection(connUrl, user, passwd);
 
@@ -541,7 +271,7 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
      *
      * @param conn Connection to close.
      */
-    private void closeConnection(Connection conn) {
+    protected void closeConnection(Connection conn) {
         U.closeQuiet(conn);
     }
 
@@ -550,7 +280,7 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
      * @return Connection.
      * @throws SQLException In case of error.
      */
-    private Connection connection(@Nullable IgniteTx tx) throws SQLException {
+    protected Connection connection(@Nullable IgniteTx tx) throws SQLException {
         if (tx != null) {
             Connection conn = tx.meta(ATTR_CONN);
 
@@ -570,8 +300,125 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
     }
 
     /** {@inheritDoc} */
-    @Override public String toString() {
-        return S.toString(AutoCacheStore.class, this, "passwd", passwd != null ? "*" : null);
+    @Override public void txEnd(IgniteTx tx, boolean commit) throws IgniteCheckedException {
+        init();
+
+        Connection conn = tx.removeMeta(ATTR_CONN);
+
+        if (conn != null) {
+            try {
+                if (commit)
+                    conn.commit();
+                else
+                    conn.rollback();
+            }
+            catch (SQLException e) {
+                throw new IgniteCheckedException(
+                    "Failed to end transaction [xid=" + tx.xid() + ", commit=" + commit + ']', e);
+            }
+            finally {
+                closeConnection(conn);
+            }
+        }
+
+        if (log.isDebugEnabled())
+            log.debug("Transaction ended [xid=" + tx.xid() + ", commit=" + commit + ']');
+    }
+
+    /**
+     * Concatenates elements using provided separator.
+     *
+     * @param elems Concatenated elements.
+     * @param f closure used for transform element.
+     * @param start Start string.
+     * @param sep Separator.
+     * @param end End string.
+     * @return Concatenated string.
+     */
+    private static <T> String mkString(Iterable<T> elems, C1<T, String> f, String start, String sep, String end) {
+        SB sb = new SB(start);
+
+        boolean first = true;
+
+        for (T elem : elems) {
+            if (!first)
+                sb.a(sep);
+
+            sb.a(f.apply(elem));
+
+            first = false;
+        }
+
+        return sb.a(end).toString();
+    }
+
+    /**
+     * Concatenates elements using provided separator.
+     *
+     * @param strs Concatenated string.
+     * @param start Start string.
+     * @param sep Delimiter.
+     * @param end End string.
+     * @return Concatenated string.
+     */
+    protected static String mkString(Iterable<String> strs, String start, String sep, String end) {
+        return mkString(strs, new C1<String, String>() {
+            @Override public String apply(String s) {
+                return s;
+            }
+        }, start, sep, end);
+    }
+
+    /**
+     * Concatenates strings using provided separator.
+     *
+     * @param strs Concatenated string.
+     * @param sep Separator.
+     * @return Concatenated string.
+     */
+    protected static String mkString(Iterable<String> strs, String sep) {
+        return mkString(strs, new C1<String, String>() {
+            @Override public String apply(String s) {
+                return s;
+            }
+        }, "", sep, "");
+    }
+
+    /**
+     * Concatenates elements using provided delimiter.
+     *
+     * @param str Repeated string.
+     * @param cnt Repeat count.
+     * @param start Start string.
+     * @param sep Separator.
+     * @param end End string.
+     */
+    protected static String repeat(String str, int cnt, String start, String sep, String end) {
+        SB sb = new SB(str.length() * cnt + sep.length() * (cnt - 1) + start.length() + end.length());
+
+        sb.a(start);
+
+        for (int i = 0; i < cnt; i++) {
+            if (i > 0)
+                sb.a(sep);
+
+            sb.a(str);
+        }
+
+        return sb.a(end).toString();
+    }
+
+    /**
+     * Extract database column names from {@link GridCacheQueryTypeDescriptor}.
+     * @param dsc collection of {@link GridCacheQueryTypeDescriptor}.
+     */
+    protected static Collection<String> databaseColumns(Collection<GridCacheQueryTypeDescriptor> dsc) {
+        return F.transform(dsc, new C1<GridCacheQueryTypeDescriptor, String>() {
+            /** {@inheritDoc} */
+            @Override public String apply(GridCacheQueryTypeDescriptor desc) {
+                return desc.getDbName();
+            }
+        });
     }
 
     /**
@@ -631,29 +478,502 @@ public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
     }
 
     /**
-     * @return Paths to xml with mapping description.
+     * @return Paths to xml with type mapping description.
      */
-    public Collection<String> getMetaPaths() {
-        return metaPaths;
+    public Collection<String> getTypeMetadataPaths() {
+        return typeMetadataPaths;
     }
 
     /**
-     * @param metaPaths Paths to xml with mapping description.
+     * Set paths to xml with type mapping description.
+     *
+     * @param typeMetadataPaths Paths to xml.
      */
-    public void setMetaPaths(Collection<String> metaPaths) {
-        this.metaPaths = metaPaths;
+    public void setTypeMetadataPaths(Collection<String> typeMetadataPaths) {
+        this.typeMetadataPaths = typeMetadataPaths;
     }
 
     /**
-     * @param tms Tms.
+     * Set type mapping description.
+     *
+     * @param typeMetadata Type mapping description.
      */
-    public void setTypes(Collection<GridCacheQueryTypeMetadata> tms) throws IgniteCheckedException {
-        types = U.newHashMap(tms.size());
+    public void setTypeMetadata(Collection<GridCacheQueryTypeMetadata> typeMetadata) {
+        this.typeMetadata = typeMetadata;
+    }
+
+    /**
+     * Construct load cache query.
+     * @param tblName Database table name.
+     * @param uniqCols Database unique columns.
+     * @return Load cache query.
+     */
+    protected String loadCacheQuery(String tblName, Iterable<String> uniqCols) {
+        return String.format("SELECT %s FROM %s", mkString(uniqCols, ","), tblName);
+    }
+
+    /**
+     * Construct load query.
+     *
+     * @param tblName Database table name.
+     * @param keyCols Database key columns.
+     * @param valCols Database value columns.
+     * @param keyCnt Key count.
+     * @return Load query.
+     */
+    protected String loadQuery(String tblName, Collection<String> keyCols, Iterable<String> valCols, int keyCnt) {
+        assert !keyCols.isEmpty();
 
-        for (GridCacheQueryTypeMetadata tm : tms) {
-            TypeMetadata m = new TypeMetadata(tm);
+        assert keyCols.size() * keyCnt <= MAX_QRY_PARAMETERS;
 
-            types.put(m.keyType, m);
+        SB sb = new SB(String.format("SELECT %s FROM %s WHERE ", mkString(valCols, ","), tblName));
+
+        if (keyCols.size() == 1) {
+            String keyCol = keyCols.iterator().next();
+
+            if (keyCnt == 1)
+                sb.a(keyCol+ "=?");
+            else
+                sb.a(repeat("?", keyCnt, keyCol + " IN (", ",", ")"));
         }
+        else {
+            String keyParams = mkString(keyCols, new C1<String, String>() {
+                @Override public String apply(String s) {
+                    return s + "=?";
+                }
+            }, "(", " AND ", ")");
+
+            sb.a(repeat(keyParams, keyCnt, "", " OR ", ""));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Construct put query.
+     *
+     * @param tblName Database table name.
+     * @param keyCols Database key columns.
+     * @param uniqCols Database unique columns.
+     * @return Put query.
+     */
+    protected abstract String putQuery(String tblName, Collection<String> keyCols, Collection<String> uniqCols);
+
+    /**
+     * Construct remove query.
+     *
+     * @param tblName Database table name.
+     * @param keyCols Database key columns.
+     * @return Remove query.
+     */
+    protected String removeQuery(String tblName, Iterable<String> keyCols) {
+        String whereParams = mkString(keyCols, new C1<String, String>() {
+            @Override public String apply(String s) {
+                return s + "=?";
+            }
+        }, "", " AND ", "");
+
+        return String.format("DELETE FROM %s WHERE %s", tblName, whereParams);
+    }
+
+    /**
+     * Extract type key from object.
+     *
+     * @param key Key object.
+     * @return Type key.
+     */
+    protected abstract Object typeKey(K key);
+
+    /**
+     * Build cache for mapped types.
+     *
+     * @throws IgniteCheckedException If failed to initialize.
+     */
+    protected abstract void buildTypeCache() throws IgniteCheckedException;
+
+
+    /** {@inheritDoc} */
+    @Override public void loadCache(final IgniteBiInClosure<K, V> clo, @Nullable Object... args)
+        throws IgniteCheckedException {
+        init();
+
+        if (log.isDebugEnabled())
+            log.debug("Loading all values from db");
+
+        Collection<Future<?>> futs = new ArrayList<>();
+
+        for (final TypeCache type : typesCache.values())
+            futs.add(exec.submit(new Callable<Void>() {
+                @Override public Void call() throws Exception {
+                    Connection conn = null;
+
+                    try {
+                        PreparedStatement stmt = null;
+
+                        try {
+                            conn = connection(null);
+
+                            stmt = conn.prepareStatement(type.loadCacheQry);
+
+                            ResultSet rs = stmt.executeQuery();
+
+                            while (rs.next()) {
+                                K key = type.keyMapper.readObject(ignite, rs);
+                                V val = type.valMapper.readObject(ignite, rs);
+
+                                clo.apply(key, val);
+                            }
+                        }
+                        catch (SQLException e) {
+                            throw new IgniteCheckedException("Failed to load cache", e);
+                        }
+                        finally {
+                            U.closeQuiet(stmt);
+                        }
+                    }
+                    finally {
+                        closeConnection(conn);
+                    }
+
+                    return null;
+                }
+            }));
+
+        for (Future<?> fut : futs)
+            U.get(fut);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public V load(@Nullable IgniteTx tx, K key) throws IgniteCheckedException {
+        init();
+
+        TypeCache type = typesCache.get(key.getClass());
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find mapping description for type: " + key.getClass());
+
+        if (log.isDebugEnabled())
+            log.debug("Start load value from db by key: " + key);
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.loadQrySingle);
+
+            type.keyMapper.setParameters(stmt, 1, key);
+
+            ResultSet rs = stmt.executeQuery();
+
+            if (rs.next())
+                return type.valMapper.readObject(ignite, rs);
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to load object by key: " + key, e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+
+        return null;
+    }
+
+    /**
+     * Loads all values for given keys with same type and passes every value to the provided closure.
+     *
+     * @param tx Cache transaction, if write-behind is not enabled, null otherwise.
+     * @param typeKey Type key.
+     * @param keys Collection of keys to load.
+     * @param c Closure to call for every loaded element.
+     * @throws IgniteCheckedException If load failed.
+     */
+    protected void loadAll(@Nullable IgniteTx tx, Object typeKey, Collection<? extends K> keys,
+        IgniteBiInClosure<K, V> c) throws IgniteCheckedException {
+        init();
+
+        TypeCache type = typesCache.get(typeKey);
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find metadata for type: " + typeKey);
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.loadQueryLast(keys.size()));
+
+            int startIdx = 1;
+
+            for (K key : keys)
+                startIdx = type.keyMapper.setParameters(stmt, startIdx, key);
+
+            stmt.executeQuery();
+
+            ResultSet rs = stmt.executeQuery();
+
+            while (rs.next()) {
+                K key = type.keyMapper.readObject(ignite, rs);
+                V val = type.valMapper.readObject(ignite, rs);
+
+                c.apply(key, val);
+            }
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to put objects", e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public final void loadAll(@Nullable final IgniteTx tx, Collection<? extends K> keys,
+        final IgniteBiInClosure<K, V> c)
+        throws IgniteCheckedException {
+        Map<Object, Collection<K>> splittedKeys = U.newHashMap(typesCache.size());
+
+        final Collection<Future<?>> futs = new ArrayList<>();
+
+        for (K key : keys) {
+            final Object typeKey = typeKey(key);
+
+            Collection<K> batch = splittedKeys.get(typeKey);
+
+            if (batch == null)
+                splittedKeys.put(typeKey, batch = new ArrayList<>());
+
+            batch.add(key);
+
+            if (batch.size() == typesCache.get(typeKey).loadBatchSize) {
+                final Collection<K> p = splittedKeys.remove(typeKey);
+
+                futs.add(exec.submit(new Callable<Void>() {
+                    @Override public Void call() throws Exception {
+                    loadAll(tx, typeKey, p, c);
+
+                    return null;
+                    }
+                }));
+            }
+        }
+
+        for (final Map.Entry<Object, Collection<K>> entry : splittedKeys.entrySet())
+            futs.add(exec.submit(new Callable<Void>() {
+                @Override public Void call() throws Exception {
+                    loadAll(tx, entry.getKey(), entry.getValue(), c);
+
+                    return null;
+                }
+            }));
+
+        for (Future<?> fut : futs)
+            U.get(fut);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void put(@Nullable IgniteTx tx, K key, V val) throws IgniteCheckedException {
+        init();
+
+        TypeCache type = typesCache.get(key.getClass());
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find metadata for type: " + key.getClass());
+
+        if (log.isDebugEnabled())
+            log.debug("Start put value in db: (" + key + ", " + val);
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.putQry);
+
+            int idx = type.keyMapper.setParameters(stmt, 1, key);
+            type.valMapper.setParameters(stmt, idx, val);
+
+            stmt.executeUpdate();
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to load object by key: " + key, e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+    }
+
+    /**
+     * Stores given key value pairs in persistent storage.
+     *
+     * @param tx Cache transaction, if write-behind is not enabled, null otherwise.
+     * @param typeKey Type key.
+     * @param map Values to store.
+     * @throws IgniteCheckedException If store failed.
+     */
+    /** {@inheritDoc} */
+    protected void putAll(@Nullable IgniteTx tx, Object typeKey, Iterable<Map.Entry<? extends K, ? extends V>> map)
+        throws IgniteCheckedException {
+        assert map != null;
+
+        init();
+
+        TypeCache type = typesCache.get(typeKey);
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find metadata for type: " + typeKey);
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.putQry);
+
+            for (Map.Entry<? extends K, ? extends V> entry : map) {
+                int startIdx = type.keyMapper.setParameters(stmt, 1, entry.getKey());
+
+                type.valMapper.setParameters(stmt, startIdx, entry.getValue());
+
+                stmt.addBatch();
+            }
+
+            stmt.executeBatch();
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to put objects", e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public final void putAll(@Nullable IgniteTx tx, Map<? extends K, ? extends V> map)
+        throws IgniteCheckedException {
+        Map<Object, Collection<Map.Entry<? extends K, ? extends V>>> keyByType = U.newHashMap(typesCache.size());
+
+        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+            Object typeKey = typeKey(entry.getKey());
+
+            Collection<Map.Entry<? extends K, ? extends V>> s = keyByType.get(typeKey);
+
+            if (s == null)
+                keyByType.put(typeKey, s = new ArrayList<>());
+
+            s.add(entry);
+        }
+
+        for (Map.Entry<Object, Collection<Map.Entry<? extends K, ? extends V>>> m : keyByType.entrySet())
+            putAll(tx, m.getKey(), m.getValue());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(@Nullable IgniteTx tx, K key) throws IgniteCheckedException {
+        init();
+
+        TypeCache type = typesCache.get(key.getClass());
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find metadata for type: " + key.getClass());
+
+        if (log.isDebugEnabled())
+            log.debug("Start remove value from db by key: " + key);
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.remQry);
+
+            type.keyMapper.setParameters(stmt, 1, key);
+
+            stmt.executeUpdate();
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to load object by key: " + key, e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+    }
+
+    /**
+     * Removes all vales identified by given keys from persistent storage.
+     *
+     * @param tx Cache transaction, if write-behind is not enabled, null otherwise.
+     * @param typeKey Type key.
+     * @param keys Collection of keys to remove.
+     * @throws IgniteCheckedException If remove failed.
+     */
+    protected void removeAll(@Nullable IgniteTx tx, Object typeKey, Collection<? extends K> keys) throws IgniteCheckedException {
+        assert keys != null;
+        assert keys.size() > 1;
+
+        init();
+
+        TypeCache type = typesCache.get(typeKey);
+
+        if (type == null)
+            throw new IgniteCheckedException("Failed to find metadata for type: " + typeKey);
+
+        if (log.isDebugEnabled())
+            log.debug("Start remove values by keys: " + Arrays.toString(keys.toArray()));
+
+        Connection conn = null;
+
+        PreparedStatement stmt = null;
+
+        try {
+            conn = connection(tx);
+
+            stmt = conn.prepareStatement(type.remQry);
+
+            for (K key : keys) {
+                type.keyMapper.setParameters(stmt, 1, key);
+
+                stmt.addBatch();
+            }
+
+            stmt.executeBatch();
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException("Failed to remove values by keys.", e);
+        }
+        finally {
+            end(tx, conn, stmt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public final void removeAll(@Nullable IgniteTx tx, Collection<? extends K> keys)
+        throws IgniteCheckedException {
+        Map<Object, Collection<K>> keyByType = U.newHashMap(typesCache.size());
+
+        for (K key : keys) {
+            Object typeKey = typeKey(key);
+
+            Collection<K> s = keyByType.get(typeKey);
+
+            if (s == null)
+                keyByType.put(typeKey, s = new ArrayList<>());
+
+            s.add(key);
+        }
+
+        for (Map.Entry<?, Collection<K>> entry : keyByType.entrySet())
+            removeAll(tx, entry.getKey(), entry.getValue());
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/H2PojoCacheStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/H2PojoCacheStore.java b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/H2PojoCacheStore.java
new file mode 100644
index 0000000..e35467b
--- /dev/null
+++ b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/H2PojoCacheStore.java
@@ -0,0 +1,53 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.apache.ignite.*;
+import org.gridgain.grid.cache.query.*;
+import org.gridgain.grid.cache.store.*;
+import org.gridgain.grid.util.typedef.internal.*;
+
+import java.util.*;
+
+/**
+ * {@link GridCacheStore} implementation stores objects in H2 database using mapping description.
+ */
+public class H2PojoCacheStore extends AutoCacheStore<Object, Object> {
+    /** {@inheritDoc} */
+    @Override protected String putQuery(String tblName, Collection<String> keyCols, Collection<String> uniqCols) {
+        return String.format("MERGE INTO %s (%s) KEY (%s) VALUES(%s)", tblName, mkString(uniqCols, ","),
+            mkString(keyCols, ","), repeat("?", uniqCols.size(), "",", ",""));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void buildTypeCache() throws IgniteCheckedException {
+        typesCache = U.newHashMap(typeMetadata.size());
+
+        for (GridCacheQueryTypeMetadata type : typeMetadata) {
+            Set<String> paramNames = new LinkedHashSet<>(databaseColumns(type.getValueDescriptors()));
+            paramNames.removeAll(databaseColumns(type.getKeyDescriptors()));
+
+            PojoJdbcMapper keyMapper = new PojoJdbcMapper(type.getKeyType(), type.getKeyDescriptors(), null);
+            PojoJdbcMapper valMapper = new PojoJdbcMapper(type.getType(), type.getValueDescriptors(), paramNames);
+
+            typesCache.put(keyMapper.cls, new TypeCache(type, keyMapper, valMapper));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Object typeKey(Object key) {
+        return key.getClass();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(H2PojoCacheStore.class, this, "passwd", passwd != null ? "*" : null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/JdbcMapper.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/JdbcMapper.java b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/JdbcMapper.java
new file mode 100644
index 0000000..2d0c98e
--- /dev/null
+++ b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/JdbcMapper.java
@@ -0,0 +1,40 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.apache.ignite.*;
+
+import java.sql.*;
+
+/**
+ * Mapper between JDBC objects and cache objects.
+ */
+public interface JdbcMapper<T> {
+    /**
+     * Set parameters in prepare statement from cache object.
+     *
+     * @param stmt Prepare statement.
+     * @param startIdx Start index for set parameters in prepare statement.
+     * @param obj Cache object.
+     * @return Last parameter index.
+     * @throws IgniteCheckedException If failed.
+     */
+    public int setParameters(PreparedStatement stmt, int startIdx, T obj) throws IgniteCheckedException;
+
+    /**
+     * Read cache object from result set.
+     *
+     * @param ignite Grid.
+     * @param rs Result set.
+     * @return cache object.
+     * @throws IgniteCheckedException If failed.
+     */
+    public T readObject(Ignite ignite, ResultSet rs) throws IgniteCheckedException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/PojoJdbcMapper.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/PojoJdbcMapper.java b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/PojoJdbcMapper.java
new file mode 100644
index 0000000..b5b4e06
--- /dev/null
+++ b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/PojoJdbcMapper.java
@@ -0,0 +1,157 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.apache.ignite.*;
+import org.gridgain.grid.cache.query.*;
+import org.jetbrains.annotations.*;
+
+import java.lang.reflect.*;
+import java.sql.*;
+import java.util.*;
+
+/**
+ * Mapper between JDBC objects and POJO.
+ */
+public class PojoJdbcMapper implements JdbcMapper<Object> {
+    /** POJO class. */
+    protected final Class<?> cls;
+
+    /** Constructor for POJO object. */
+    private final Constructor ctor;
+
+    /** Database column names. */
+    private final String[] colNames;
+
+    /** Cached getters for POJO object. */
+    private final Method[] getters;
+
+    /** Cached setters for POJO object. */
+    private final Method[] setters;
+
+    /**
+     * @param clsName POJO class name.
+     * @param descs Fields descriptors.
+     * @param paramNames Parameter for set from this object.
+     */
+    protected PojoJdbcMapper(String clsName, Collection<GridCacheQueryTypeDescriptor> descs,
+        @Nullable Collection<String> paramNames) throws IgniteCheckedException {
+        try {
+            cls = Class.forName(clsName);
+
+            ctor = cls.getDeclaredConstructor();
+
+            if (!ctor.isAccessible())
+                ctor.setAccessible(true);
+        }
+        catch (ClassNotFoundException e) {
+            throw new IgniteCheckedException("Failed to find class: " + clsName, e);
+        }
+        catch (NoSuchMethodException e) {
+            throw new IgniteCheckedException("Failed to find empty constructor for class: " + clsName, e);
+        }
+
+        colNames = new String[descs.size()];
+
+        List<Method> getters = new ArrayList<>(descs.size());
+
+        setters = new Method[descs.size()];
+
+        int i = 0;
+
+        for (GridCacheQueryTypeDescriptor desc : descs) {
+            colNames[i] = desc.getDbName();
+
+            String prop = capitalFirst(desc.getJavaName());
+
+            try {
+                setters[i] = cls.getMethod("set" + prop, desc.getJavaType());
+            }
+            catch (NoSuchMethodException e) {
+                throw new IgniteCheckedException("Failed to find setter for property " + desc.getJavaName() +
+                    " of class: " + clsName, e);
+            }
+
+            if (paramNames == null || paramNames.contains(colNames[i])) {
+                try {
+                    getters.add(cls.getMethod("get" + prop));
+                }
+                catch (NoSuchMethodException ignored) {
+                    try {
+                        getters.add(cls.getMethod("is" + prop));
+                    }
+                    catch (NoSuchMethodException e) {
+                        throw new IgniteCheckedException("Failed to find getter for property " + desc.getJavaName() +
+                            " of class: " + cls.getName(), e);
+                    }
+                }
+            }
+
+            i++;
+        }
+
+        this.getters = getters.toArray(new Method[getters.size()]);
+    }
+
+    /**
+     * Capitalizes the first character of the given string.
+     *
+     * @param str String.
+     * @return String with capitalized first character.
+     */
+    private String capitalFirst(@Nullable String str) {
+        return str == null ? null :
+            str.isEmpty() ? "" : Character.toUpperCase(str.charAt(0)) + str.substring(1);
+    }
+
+    /**
+     * Construct new instance of pojo object.
+     *
+     * @return pojo object.
+     * @throws IgniteCheckedException If construct new instance failed.
+     */
+    private Object newInstance() throws IgniteCheckedException {
+        try {
+            return ctor.newInstance();
+        }
+        catch (Exception e) {
+            throw new IgniteCheckedException("Failed to create new instance for class: " + cls, e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public int setParameters(PreparedStatement stmt, int startIdx, Object obj)
+        throws IgniteCheckedException {
+        try {
+            for (int i = 0; i < getters.length; i++)
+                stmt.setObject(startIdx  + i, getters[i].invoke(obj));
+
+            return startIdx + getters.length;
+        }
+        catch (Exception e) {
+            throw new IgniteCheckedException("Failed to set parameters for query.", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object readObject(Ignite ignite, ResultSet rs) throws IgniteCheckedException {
+        Object obj = newInstance();
+
+        try {
+            for (int i = 0; i < setters.length; i++)
+                setters[i].invoke(obj, rs.getObject(colNames[i]));
+
+            return obj;
+        }
+        catch (Exception e) {
+            throw new IgniteCheckedException("Failed to read object of class: " + cls, e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/core/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessor.java b/modules/core/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessor.java
index 8742eb2..8baf4a7 100644
--- a/modules/core/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessor.java
+++ b/modules/core/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessor.java
@@ -45,11 +45,11 @@ public interface GridSpringProcessor {
      *
      * @param cfgUrl Configuration file path or URL. This cannot be {@code null}.
      * @param beanClasses Beans classes.
-     * @return Bean class -> loaded bean instance map, if configuration does not contain bean with required type the
-     *       map value is {@code null}.
+     * @return Bean class -> (bean name -> loaded bean) instance map,
+     *       if configuration does not contain beans with required type the map value is {@code null}.
      * @throws IgniteCheckedException If failed to load configuration.
      */
-    public Map<Class<?>, Object> loadBeans(URL cfgUrl, Class<?>... beanClasses) throws IgniteCheckedException;
+    public Map<Class<?>, Map<String, Object>> loadBeans(URL cfgUrl, Class<?>... beanClasses) throws IgniteCheckedException;
 
     /**
      * Gets user version for given class loader by checking

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java
index a964151..168daac 100644
--- a/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java
+++ b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java
@@ -11,12 +11,17 @@ import org.gridgain.testframework.junits.cache.*;
 import org.gridgain.testframework.junits.common.*;
 
 import java.sql.*;
-import java.util.*;
 
 /**
  * Base class for {@code AutoCacheStore} tests.
  */
 abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
+    /** Organization count. */
+    protected static final int ORGANIZATION_CNT = 1000;
+
+    /** Person count. */
+    protected static final int PERSON_CNT = 100000;
+
     /** */
     protected final AutoCacheStore store;
 
@@ -49,19 +54,20 @@ abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
      * @throws Exception If failed.
      */
     public void testLoadCache() throws Exception {
-        final Collection<Object> keys = new ArrayList<>();
+        final T2<Integer, Integer> keys = new T2<>(0, 0);
 
         IgniteBiInClosure<Object,Object> c = new CI2<Object, Object>() {
             @Override public void apply(Object k, Object v) {
-                keys.add(k);
+                if (k instanceof OrganizationKey && v instanceof Organization)
+                    keys.set1(keys.get1() + 1);
+                else if (k instanceof PersonKey && v instanceof Person)
+                    keys.set2(keys.get2() + 1);
             }
         };
 
-        assertTrue(keys.isEmpty());
-
         store.loadCache(c);
 
-        assertEquals(2, keys.size());
+        assertEquals(new T2<>(ORGANIZATION_CNT, PERSON_CNT), keys);
     }
 
     /**
@@ -72,18 +78,18 @@ abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
         IgniteTx tx = new GridAbstractCacheStoreSelfTest.DummyTx();
 
         OrganizationKey k3 = new OrganizationKey();
-        k3.setId(3);
+        k3.setId(-3);
 
         OrganizationKey k4 = new OrganizationKey();
-        k4.setId(4);
+        k4.setId(-4);
 
         Organization v3 = new Organization();
-        v3.setId(3);
+        v3.setId(-3);
         v3.setName("Test1");
         v3.setCity("Test2");
 
         Organization v4 = new Organization();
-        v4.setId(4);
+        v4.setId(-4);
         v4.setName("Test3");
         v4.setCity("Test4");
 
@@ -96,17 +102,25 @@ abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
         assertEquals(v4, store.load(null, k4));
 
         OrganizationKey k5 = new OrganizationKey();
-        k5.setId(5);
+        k5.setId(-5);
 
-        assertNull(store.load( null, k5));
+        assertNull(store.load(null, k5));
 
         store.remove(tx, k3);
 
-        store.txEnd(tx, true);
+        assertNull(store.load(tx, k3));
+        assertEquals(v4, store.load(tx, k4));
 
-        assertNull(store.load(null, k3));
-        assertEquals(v4, store.load(null, k4));
-        assertNull(store.load(null, k3));
+        store.remove(tx, k4);
+
+        assertNull(store.load(tx, k4));
+
+        store.putAll(tx, F.asMap(k3, v3, k4, v4));
+
+        assertEquals(v3, store.load(tx, k3));
+        assertEquals(v4, store.load(tx, k4));
+
+        store.txEnd(tx, true);
     }
 
     /** {@inheritDoc} */
@@ -131,11 +145,31 @@ abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
 
         conn.commit();
 
-        stmt.executeUpdate("INSERT INTO Organization(id, name, city) VALUES (1, 'Test1', 'Test2')" );
-        stmt.executeUpdate("INSERT INTO Organization(id, name, city) VALUES (2, 'Test3', 'Test4')" );
+        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();
+
+        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();
+        }
 
-        stmt.executeUpdate("INSERT INTO Person(id, org_id, name) VALUES (1, 1, 'Test5')");
-        stmt.executeUpdate("INSERT INTO Person(id, org_id, name) VALUES (2, 2, 'Test6')");
+        prnStmt.executeBatch();
 
         conn.commit();
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java
index 5930986..d62ad74 100644
--- a/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java
+++ b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java
@@ -17,14 +17,14 @@ public class AutoCacheStoreSelfTest extends AbstractAutoCacheStoreSelfTest {
     /**
      * @return Store.
      */
-    @Override protected AutoCacheStore store() {
-        AutoCacheStore store = new AutoCacheStore();
+    @Override protected H2PojoCacheStore store() {
+        H2PojoCacheStore store = new H2PojoCacheStore();
 
         store.setConnUrl("jdbc:h2:mem:test");
         store.setUser("sa");
         store.setPassword("");
 
-        store.setMetaPaths(F.asList("modules/schema-load/src/test/config/all.xml"));
+        store.setTypeMetadataPaths(F.asList("modules/schema-load/src/test/config/all.xml"));
 
         return store;
     }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b2c8f56/modules/spring/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessorImpl.java b/modules/spring/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessorImpl.java
index 66b931c..f0a5855 100644
--- a/modules/spring/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessorImpl.java
+++ b/modules/spring/src/main/java/org/gridgain/grid/kernal/processors/spring/GridSpringProcessorImpl.java
@@ -12,11 +12,9 @@ package org.gridgain.grid.kernal.processors.spring;
 import org.apache.ignite.*;
 import org.apache.ignite.configuration.*;
 import org.apache.ignite.lang.*;
-import org.gridgain.grid.*;
 import org.gridgain.grid.kernal.processors.resource.*;
 import org.gridgain.grid.util.typedef.*;
 import org.gridgain.grid.util.typedef.internal.*;
-import org.jetbrains.annotations.*;
 import org.springframework.beans.*;
 import org.springframework.beans.factory.*;
 import org.springframework.beans.factory.config.*;
@@ -103,7 +101,7 @@ public class GridSpringProcessorImpl implements GridSpringProcessor {
     }
 
     /** {@inheritDoc} */
-    @Override public Map<Class<?>, Object> loadBeans(URL cfgUrl, Class<?>... beanClasses) throws IgniteCheckedException {
+    @Override public Map<Class<?>, Map<String, Object>> loadBeans(URL cfgUrl, Class<?>... beanClasses) throws IgniteCheckedException {
         assert beanClasses.length > 0;
 
         GenericApplicationContext springCtx;
@@ -125,10 +123,10 @@ public class GridSpringProcessorImpl implements GridSpringProcessor {
                     cfgUrl + ", err=" + e.getMessage() + ']', e);
         }
 
-        Map<Class<?>, Object> beans = new HashMap<>();
+        Map<Class<?>, Map<String, Object>> beans = new HashMap<>();
 
         for (Class<?> cls : beanClasses)
-            beans.put(cls, bean(springCtx, cls));
+            beans.put(cls, (Map<String, Object>)springCtx.getBeansOfType(cls));
 
         return beans;
     }
@@ -196,19 +194,6 @@ public class GridSpringProcessorImpl implements GridSpringProcessor {
     }
 
     /**
-     * Gets bean configuration.
-     *
-     * @param ctx Spring context.
-     * @param beanCls Bean class.
-     * @return Spring bean.
-     */
-    @Nullable private static <T> T bean(ListableBeanFactory ctx, Class<T> beanCls) {
-        Map.Entry<String, T> entry = F.firstEntry(ctx.getBeansOfType(beanCls));
-
-        return entry == null ? null : entry.getValue();
-    }
-
-    /**
      * Creates Spring application context. Optionally excluded properties can be specified,
      * it means that if such a property is found in {@link org.apache.ignite.configuration.IgniteConfiguration}
      * then it is removed before the bean is instantiated.


Mime
View raw message