Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-32 5b01f7083 -> 76408a4c5


# IGNITE-32 WIP: AutoCacheStore implementation + test.


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

Branch: refs/heads/ignite-32
Commit: 76408a4c599f0da002b4fb9f436ff197799c2e15
Parents: 5b01f70
Author: AKuznetsov <akuznet...@gridgain.com>
Authored: Fri Dec 26 11:54:11 2014 +0700
Committer: AKuznetsov <akuznet...@gridgain.com>
Committed: Fri Dec 26 11:54:11 2014 +0700

----------------------------------------------------------------------
 .../grid/cache/store/auto/AutoCacheStore.java   | 659 +++++++++++++++++++
 .../cache/GridAbstractCacheStoreSelfTest.java   |   3 +-
 modules/schema-load/pom.xml                     |  38 ++
 .../ignite/schema/pojo/PojoCodeGenerator.java   |   4 +-
 .../src/test/config/ORGANIZATION.xml            |  94 +++
 modules/schema-load/src/test/config/PERSON.xml  |  92 +++
 modules/schema-load/src/test/config/all.xml     | 184 ++++++
 .../java/org/apache/ignite/Organization.java    |  64 ++
 .../java/org/apache/ignite/OrganizationKey.java |  34 +
 .../src/test/java/org/apache/ignite/Person.java |  64 ++
 .../test/java/org/apache/ignite/PersonKey.java  |  34 +
 .../auto/AbstractAutoCacheStoreSelfTest.java    | 153 +++++
 .../store/auto/AutoCacheStoreSelfTest.java      |  38 ++
 .../gridgain/grid/cache/store/auto/package.html |  15 +
 14 files changed, 1473 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/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
new file mode 100644
index 0000000..2c8ef73
--- /dev/null
+++ 
b/modules/core/src/main/java/org/gridgain/grid/cache/store/auto/AutoCacheStore.java
@@ -0,0 +1,659 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.apache.ignite.*;
+import org.apache.ignite.lang.*;
+import org.apache.ignite.resources.*;
+import org.apache.ignite.transactions.*;
+import org.gridgain.grid.cache.query.*;
+import org.gridgain.grid.cache.store.*;
+import org.gridgain.grid.kernal.processors.spring.*;
+import org.gridgain.grid.util.tostring.*;
+import org.gridgain.grid.util.typedef.*;
+import org.gridgain.grid.util.typedef.internal.*;
+import org.jetbrains.annotations.*;
+
+import javax.sql.*;
+import java.net.*;
+import java.sql.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import static org.gridgain.grid.kernal.GridComponentType.*;
+
+/**
+ * {@link GridCacheStore} implementation backed by JDBC.
+ * This implementation stores objects in underlying database using mapping 
description.
+ */
+public class AutoCacheStore extends GridCacheStoreAdapter<Object, Object> {
+    /**
+     * Internal mapping description.
+     */
+    private static class TypeMetadata {
+        /** Select all items query. */
+        private final String loadCacheQry;
+
+        /** Select one item query. */
+        private final String loadQry;
+
+        /** Insert item query. */
+        private final String insQry;
+
+        /** Update item query. */
+        private final String updQry;
+
+        /** Delete one item query. */
+        private final String delQry;
+
+        /** Key type. */
+        private final Class<?> keyType;
+
+        /** Value type. */
+        private final Class<?> valType;
+
+        /** Key fields. */
+        private final Collection<GridCacheQueryTypeDescriptor> keyFields;
+
+        /** Value fields. */
+        private final Collection<GridCacheQueryTypeDescriptor> valFields;
+
+        /** Unique fields from key and value. */
+        private final Collection<GridCacheQueryTypeDescriptor> uniqFields;
+
+        /** Value fields without key fields. */
+        private final Collection<GridCacheQueryTypeDescriptor> updFields;
+
+        /**
+         * @param dsc Cache type metadata.
+         */
+        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);
+
+            updFields = new HashSet<>(valFields);
+            updFields.removeAll(keyFields);
+
+            String uniqCols = databaseColumns(uniqFields);
+
+            String where = databaseParameters(keyFields, " OR ");
+
+            String values = concat(uniqFields, new 
C1<GridCacheQueryTypeDescriptor, String>() {
+                @Override public String apply(GridCacheQueryTypeDescriptor 
desc) {
+                    return "?";
+                }
+            }, ", ");
+
+            String updCols = databaseParameters(updFields, ", ");
+
+            String tblName = String.format("%s.%s", dsc.getSchema(), 
dsc.getTableName());
+
+            loadCacheQry = String.format("SELECT %s FROM %s", uniqCols, 
tblName);
+
+            loadQry = String.format("SELECT %s FROM %s WHERE %s", 
databaseColumns(valFields), tblName, where);
+
+            insQry = String.format("INSERT INTO %s (%s) values (%s)", tblName, 
uniqCols, values);
+
+            updQry = String.format("UPDATE %s SET %s WHERE %s", tblName, 
updCols, where);
+
+            delQry = String.format("DELETE FROM %s WHERE %s", tblName, where);
+        }
+
+        /**
+         * Concatenates elements using provided delimiter.
+         *
+         * @param elems Concatenated elements.
+         * @param f closure used for transform element.
+         * @param delim Delimiter.
+         * @return Concatenated string.
+         */
+        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);
+
+                sb.a(f.apply(elem));
+
+                first = false;
+            }
+
+            return sb.toString();
+        }
+
+        /**
+         * @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);
+        }
+
+        /**
+         * @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();
+                }
+            }, ", ");
+        }
+    }
+
+    /** Connection attribute name. */
+    private static final String ATTR_CONN = "AUTO_STORE_CONNECTION";
+
+    /** Log. */
+    @IgniteLoggerResource
+    private IgniteLogger log;
+
+    /** Init guard. */
+    @GridToStringExclude
+    private final AtomicBoolean initGuard = new AtomicBoolean();
+
+    /** Init latch. */
+    @GridToStringExclude
+    private final CountDownLatch initLatch = new CountDownLatch(1);
+
+    /** Successful initialization flag. */
+    private boolean initOk;
+
+    /** Data source. */
+    private DataSource dataSrc;
+
+    /** Connection URL. */
+    private String connUrl;
+
+    /** User name for database access. */
+    private 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);
+
+            fill(stmt, type.updFields, val, type.keyFields, key);
+
+            if (stmt.executeUpdate() == 0) {
+                stmt.close();
+
+                stmt = conn.prepareStatement(type.insQry);
+
+                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 + ']');
+    }
+
+    /**
+     * Initializes store.
+     *
+     * @throws IgniteCheckedException If failed to initialize.
+     */
+    private void init() throws IgniteCheckedException {
+        if (initLatch.getCount() > 0) {
+            if (initGuard.compareAndSet(false, true)) {
+                if (log.isDebugEnabled())
+                    log.debug("Initializing cache store.");
+
+                if (dataSrc == null && F.isEmpty(connUrl))
+                    throw new IgniteCheckedException("Failed to initialize 
cache store (connection is not provided).");
+
+                try {
+                    GridSpringProcessor spring = SPRING.create(false);
+
+                    if (types == null) {
+                        if (metaPaths == null)
+                            throw new IgniteCheckedException(
+                                "Failed to initialize cache store (metadata 
paths is not provided).");
+
+                        Collection<GridCacheQueryTypeMetadata> typeMeta = new 
ArrayList<>();
+
+                        for (String path : metaPaths) {
+                            URL url = U.resolveGridGainUrl(path);
+
+                            if (url != null) {
+                                Map<Class<?>, Object> beans = 
spring.loadBeans(url, GridCacheQueryTypeMetadata.class);
+
+                                for (Object bean : beans.values())
+                                    if (bean instanceof 
GridCacheQueryTypeMetadata)
+                                        
typeMeta.add((GridCacheQueryTypeMetadata)bean);
+                            }
+                            else
+                                log.warning("Failed to resolve metadata path: 
" + path);
+                        }
+
+                        setTypes(typeMeta);
+                    }
+
+                    initOk = true;
+                }
+                finally {
+                    initLatch.countDown();
+                }
+            }
+            else
+                U.await(initLatch);
+        }
+
+        if (!initOk)
+            throw new IgniteCheckedException("Cache store was not properly 
initialized.");
+    }
+
+    /**
+     * Closes allocated resources depending on transaction status.
+     *
+     * @param tx Active transaction, if any.
+     * @param conn Allocated connection.
+     * @param st Created statement,
+     */
+    private void end(@Nullable IgniteTx tx, Connection conn, Statement st) {
+        U.closeQuiet(st);
+
+        if (tx == null)
+            // Close connection right away if there is no transaction.
+            closeConnection(conn);
+    }
+
+    /**
+     * Gets connection from a pool.
+     *
+     * @param autocommit {@code true} If connection should use autocommit mode.
+     * @return Pooled connection.
+     * @throws SQLException In case of error.
+     */
+    private Connection openConnection(boolean autocommit) throws SQLException {
+        Connection conn = dataSrc != null ? dataSrc.getConnection() :
+            DriverManager.getConnection(connUrl, user, passwd);
+
+        conn.setAutoCommit(autocommit);
+
+        return conn;
+    }
+
+    /**
+     * Closes connection.
+     *
+     * @param conn Connection to close.
+     */
+    private void closeConnection(Connection conn) {
+        U.closeQuiet(conn);
+    }
+
+    /**
+     * @param tx Cache transaction.
+     * @return Connection.
+     * @throws SQLException In case of error.
+     */
+    private Connection connection(@Nullable IgniteTx tx) throws SQLException {
+        if (tx != null) {
+            Connection conn = tx.meta(ATTR_CONN);
+
+            if (conn == null) {
+                conn = openConnection(false);
+
+                // Store connection in transaction metadata, so it can be 
accessed
+                // for other operations on the same transaction.
+                tx.addMeta(ATTR_CONN, conn);
+            }
+
+            return conn;
+        }
+        // Transaction can be null in case of simple load operation.
+        else
+            return openConnection(true);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(AutoCacheStore.class, this, "passwd", passwd != null 
? "*" : null);
+    }
+
+    /**
+     * @return Data source.
+     */
+    public DataSource getDataSource() {
+        return dataSrc;
+    }
+
+    /**
+     * @param dataSrc Data source.
+     */
+    public void setDataSource(DataSource dataSrc) {
+        this.dataSrc = dataSrc;
+    }
+
+    /**
+     * @return Connection URL.
+     */
+    public String getConnUrl() {
+        return connUrl;
+    }
+
+    /**
+     * @param connUrl Connection URL.
+     */
+    public void setConnUrl(String connUrl) {
+        this.connUrl = connUrl;
+    }
+
+    /**
+     * @return Password for database access.
+     */
+    public String getPassword() {
+        return passwd;
+    }
+
+    /**
+     * @param passwd Password for database access.
+     */
+    public void setPassword(String passwd) {
+        this.passwd = passwd;
+    }
+
+    /**
+     * @return User name for database access.
+     */
+    public String getUser() {
+        return user;
+    }
+
+    /**
+     * @param user User name for database access.
+     */
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    /**
+     * @return Paths to xml with mapping description.
+     */
+    public Collection<String> getMetaPaths() {
+        return metaPaths;
+    }
+
+    /**
+     * @param metaPaths Paths to xml with mapping description.
+     */
+    public void setMetaPaths(Collection<String> metaPaths) {
+        this.metaPaths = metaPaths;
+    }
+
+    /**
+     * @param tms Tms.
+     */
+    public void setTypes(Collection<GridCacheQueryTypeMetadata> tms) throws 
IgniteCheckedException {
+        types = U.newHashMap(tms.size());
+
+        for (GridCacheQueryTypeMetadata tm : tms) {
+            TypeMetadata m = new TypeMetadata(tm);
+
+            types.put(m.keyType, m);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/core/src/test/java/org/gridgain/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/test/java/org/gridgain/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java
 
b/modules/core/src/test/java/org/gridgain/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java
index bcc8799..d30da22 100644
--- 
a/modules/core/src/test/java/org/gridgain/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java
+++ 
b/modules/core/src/test/java/org/gridgain/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java
@@ -366,8 +366,7 @@ public abstract class GridAbstractCacheStoreSelfTest<T 
extends GridCacheStore<Ob
         final Collection<Object> keys = new ArrayList<>();
 
         CI2<Object, Object> c = new CI2<Object, Object>() {
-            @Override
-            public void apply(Object k, Object v) {
+            @Override public void apply(Object k, Object v) {
                 keys.add(k);
             }
         };

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/pom.xml
----------------------------------------------------------------------
diff --git a/modules/schema-load/pom.xml b/modules/schema-load/pom.xml
index bca95b9..22962d7 100644
--- a/modules/schema-load/pom.xml
+++ b/modules/schema-load/pom.xml
@@ -35,6 +35,21 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${ignite.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${ignite.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
             <version>1.3.175</version>
@@ -50,5 +65,28 @@
                 </excludes>
             </resource>
         </resources>
+
+        <testResources>
+            <testResource>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            
<mainClass>org.apache.ignite.schema.ui.SchemaLoadApp</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
     </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/main/java/org/apache/ignite/schema/pojo/PojoCodeGenerator.java
----------------------------------------------------------------------
diff --git 
a/modules/schema-load/src/main/java/org/apache/ignite/schema/pojo/PojoCodeGenerator.java
 
b/modules/schema-load/src/main/java/org/apache/ignite/schema/pojo/PojoCodeGenerator.java
index 33ca6c1..79dff5a 100644
--- 
a/modules/schema-load/src/main/java/org/apache/ignite/schema/pojo/PojoCodeGenerator.java
+++ 
b/modules/schema-load/src/main/java/org/apache/ignite/schema/pojo/PojoCodeGenerator.java
@@ -75,7 +75,7 @@ public class PojoCodeGenerator {
 
         src.add("");
 
-        src.add(TAB2 + "if (o == null || getClass() != o.getClass())");
+        src.add(TAB2 + "if (!(o instanceof " + type + "))");
         src.add(TAB3 + "return false;");
 
         src.add("");
@@ -115,6 +115,8 @@ public class PojoCodeGenerator {
         src.add(TAB2 + "return result;");
         src.add(TAB + "}");
 
+        src.add("}");
+
         try (Writer writer = new BufferedWriter(new FileWriter(new 
File(pkgFolder, type + ".java")))) {
             for (String line : src)
                 writer.write(line + '\n');

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/config/ORGANIZATION.xml
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/config/ORGANIZATION.xml 
b/modules/schema-load/src/test/config/ORGANIZATION.xml
new file mode 100644
index 0000000..466070f
--- /dev/null
+++ b/modules/schema-load/src/test/config/ORGANIZATION.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?><beans 
xmlns="http://www.springframework.org/schema/beans"; 
xmlns:util="http://www.springframework.org/schema/util"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util.xsd";>
+    <bean class="org.gridgain.grid.cache.query.GridCacheQueryTypeMetadata">
+        <property name="type" value="org.apache.ignite.Organization"/>
+        <property name="keyType" value="org.apache.ignite.OrganizationKey"/>
+        <property name="schema" value="PUBLIC"/>
+        <property name="tableName" value="ORGANIZATION"/>
+        <property name="keyDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+            </list>
+        </property>
+        <property name="valueDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="name"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="NAME"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="city"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="CITY"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+            </list>
+        </property>
+        <property name="queryFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+                <entry key="city" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="ascendingFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+                <entry key="city" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="groups">
+            <map>
+                <entry key="primaryKeyD">
+                    <map>
+                        <entry key="id">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.Integer"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="orgNameCityIdx">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.String"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                        <entry key="city">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.String"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="orgNameIdx">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.String"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+            </map>
+        </property>
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/config/PERSON.xml
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/config/PERSON.xml 
b/modules/schema-load/src/test/config/PERSON.xml
new file mode 100644
index 0000000..6b7a6cc
--- /dev/null
+++ b/modules/schema-load/src/test/config/PERSON.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?><beans 
xmlns="http://www.springframework.org/schema/beans"; 
xmlns:util="http://www.springframework.org/schema/util"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util.xsd";>
+    <bean class="org.gridgain.grid.cache.query.GridCacheQueryTypeMetadata">
+        <property name="type" value="org.apache.ignite.Person"/>
+        <property name="keyType" value="org.apache.ignite.PersonKey"/>
+        <property name="schema" value="PUBLIC"/>
+        <property name="tableName" value="PERSON"/>
+        <property name="keyDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+            </list>
+        </property>
+        <property name="valueDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="orgId"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ORG_ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="name"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="NAME"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+            </list>
+        </property>
+        <property name="queryFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="orgId" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="ascendingFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="descendingFields">
+            <map>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="groups">
+            <map>
+                <entry key="primaryKey8">
+                    <map>
+                        <entry key="id">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.Integer"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="personNameIdx1">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.String"/>
+                                <constructor-arg type="java.lang.Object" 
value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="personNameIdx2">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg type="java.lang.Object" 
value="java.lang.String"/>
+                                <constructor-arg type="java.lang.Object" 
value="true"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+            </map>
+        </property>
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/config/all.xml
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/config/all.xml 
b/modules/schema-load/src/test/config/all.xml
new file mode 100644
index 0000000..14ebdc8
--- /dev/null
+++ b/modules/schema-load/src/test/config/all.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?><beans 
xmlns="http://www.springframework.org/schema/beans"; 
xmlns:util="http://www.springframework.org/schema/util"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util.xsd";>
+    <bean class="org.gridgain.grid.cache.query.GridCacheQueryTypeMetadata">
+        <property name="type" value="org.apache.ignite.Organization"/>
+        <property name="keyType" value="org.apache.ignite.OrganizationKey"/>
+        <property name="schema" value="PUBLIC"/>
+        <property name="tableName" value="ORGANIZATION"/>
+        <property name="keyDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+            </list>
+        </property>
+        <property name="valueDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="name"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="NAME"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="city"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="CITY"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+            </list>
+        </property>
+        <property name="queryFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+                <entry key="city" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="ascendingFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+                <entry key="city" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="groups">
+            <map>
+                <entry key="primaryKeyD">
+                    <map>
+                        <entry key="id">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.Integer"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="orgNameCityIdx">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.String"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                        <entry key="city">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.String"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="orgNameIdx">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.String"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+            </map>
+        </property>
+    </bean>
+    <bean class="org.gridgain.grid.cache.query.GridCacheQueryTypeMetadata">
+        <property name="type" value="org.apache.ignite.Person"/>
+        <property name="keyType" value="org.apache.ignite.PersonKey"/>
+        <property name="schema" value="PUBLIC"/>
+        <property name="tableName" value="PERSON"/>
+        <property name="keyDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+            </list>
+        </property>
+        <property name="valueDescriptors">
+            <list>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="id"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="orgId"/>
+                    <property name="javaType" value="java.lang.Integer"/>
+                    <property name="dbName" value="ORG_ID"/>
+                    <property name="dbType" value="4"/>
+                </bean>
+                <bean 
class="org.gridgain.grid.cache.query.GridCacheQueryTypeDescriptor">
+                    <property name="javaName" value="name"/>
+                    <property name="javaType" value="java.lang.String"/>
+                    <property name="dbName" value="NAME"/>
+                    <property name="dbType" value="12"/>
+                </bean>
+            </list>
+        </property>
+        <property name="queryFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="orgId" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="ascendingFields">
+            <map>
+                <entry key="id" value="java.lang.Integer"/>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="descendingFields">
+            <map>
+                <entry key="name" value="java.lang.String"/>
+            </map>
+        </property>
+        <property name="groups">
+            <map>
+                <entry key="primaryKey8">
+                    <map>
+                        <entry key="id">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.Integer"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="personNameIdx1">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.String"/>
+                                <constructor-arg value="false"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+                <entry key="personNameIdx2">
+                    <map>
+                        <entry key="name">
+                            <bean class="org.apache.ignite.lang.IgniteBiTuple">
+                                <constructor-arg value="java.lang.String"/>
+                                <constructor-arg value="true"/>
+                            </bean>
+                        </entry>
+                    </map>
+                </entry>
+            </map>
+        </property>
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/java/org/apache/ignite/Organization.java
----------------------------------------------------------------------
diff --git 
a/modules/schema-load/src/test/java/org/apache/ignite/Organization.java 
b/modules/schema-load/src/test/java/org/apache/ignite/Organization.java
new file mode 100644
index 0000000..865f69c
--- /dev/null
+++ b/modules/schema-load/src/test/java/org/apache/ignite/Organization.java
@@ -0,0 +1,64 @@
+package org.apache.ignite;
+
+public class Organization {
+    private Integer id;
+
+    private String name;
+
+    private String city;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof Organization))
+            return false;
+
+        Organization that = (Organization)o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null)
+            return false;
+
+        if (name != null ? !name.equals(that.name) : that.name != null)
+            return false;
+
+        if (city != null ? !city.equals(that.city) : that.city != null)
+            return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+
+        result = 31 * result + (city != null ? city.hashCode() : 0);
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/java/org/apache/ignite/OrganizationKey.java
----------------------------------------------------------------------
diff --git 
a/modules/schema-load/src/test/java/org/apache/ignite/OrganizationKey.java 
b/modules/schema-load/src/test/java/org/apache/ignite/OrganizationKey.java
new file mode 100644
index 0000000..b5700b6
--- /dev/null
+++ b/modules/schema-load/src/test/java/org/apache/ignite/OrganizationKey.java
@@ -0,0 +1,34 @@
+package org.apache.ignite;
+
+public class OrganizationKey {
+    private Integer id;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof OrganizationKey))
+            return false;
+
+        OrganizationKey that = (OrganizationKey)o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null)
+            return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/java/org/apache/ignite/Person.java
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/java/org/apache/ignite/Person.java 
b/modules/schema-load/src/test/java/org/apache/ignite/Person.java
new file mode 100644
index 0000000..e6f77a8
--- /dev/null
+++ b/modules/schema-load/src/test/java/org/apache/ignite/Person.java
@@ -0,0 +1,64 @@
+package org.apache.ignite;
+
+public class Person {
+    private Integer id;
+
+    private Integer orgId;
+
+    private String name;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getOrgid() {
+        return orgId;
+    }
+
+    public void setOrgid(Integer orgId) {
+        this.orgId = orgId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof Person))
+            return false;
+
+        Person that = (Person)o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null)
+            return false;
+
+        if (orgId != null ? !orgId.equals(that.orgId) : that.orgId != null)
+            return false;
+
+        if (name != null ? !name.equals(that.name) : that.name != null)
+            return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+
+        result = 31 * result + (orgId != null ? orgId.hashCode() : 0);
+
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/java/org/apache/ignite/PersonKey.java
----------------------------------------------------------------------
diff --git a/modules/schema-load/src/test/java/org/apache/ignite/PersonKey.java 
b/modules/schema-load/src/test/java/org/apache/ignite/PersonKey.java
new file mode 100644
index 0000000..ac32b8f
--- /dev/null
+++ b/modules/schema-load/src/test/java/org/apache/ignite/PersonKey.java
@@ -0,0 +1,34 @@
+package org.apache.ignite;
+
+public class PersonKey {
+    private Integer id;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof PersonKey))
+            return false;
+
+        PersonKey that = (PersonKey)o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null)
+            return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/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
new file mode 100644
index 0000000..ef1cd28
--- /dev/null
+++ 
b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AbstractAutoCacheStoreSelfTest.java
@@ -0,0 +1,153 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.apache.ignite.*;
+import org.apache.ignite.lang.*;
+import org.apache.ignite.transactions.*;
+import org.gridgain.grid.util.typedef.*;
+import org.gridgain.grid.util.typedef.internal.*;
+import org.gridgain.testframework.junits.cache.*;
+import org.gridgain.testframework.junits.common.*;
+
+import java.sql.*;
+import java.util.*;
+
+/**
+ * Test for {@code AutoCacheStore}.
+ */
+abstract class AbstractAutoCacheStoreSelfTest extends GridCommonAbstractTest {
+    /** */
+    protected final AutoCacheStore store;
+
+    /**
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings({"AbstractMethodCallInConstructor", 
"OverriddenMethodCallDuringObjectConstruction"})
+    protected AbstractAutoCacheStoreSelfTest() throws Exception {
+        super(false);
+
+        store = store();
+
+        inject(store);
+    }
+
+    /**
+     * @return Store.
+     */
+    protected abstract AutoCacheStore store();
+
+    /**
+     * @param store Store.
+     * @throws Exception If failed.
+     */
+    protected void inject(AutoCacheStore store) throws Exception {
+        getTestResources().inject(store);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testLoadCache() throws Exception {
+        final Collection<Object> keys = new ArrayList<>();
+
+        IgniteBiInClosure<Object,Object> c = new CI2<Object, Object>() {
+            @Override public void apply(Object k, Object v) {
+                keys.add(k);
+            }
+        };
+
+        assertTrue(keys.isEmpty());
+
+        store.loadCache(c);
+
+        assertEquals(2, keys.size());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testStore() throws Exception {
+        // Create dummy transaction
+        IgniteTx tx = new GridAbstractCacheStoreSelfTest.DummyTx();
+
+        OrganizationKey k3 = new OrganizationKey();
+        k3.setId(3);
+
+        OrganizationKey k4 = new OrganizationKey();
+        k4.setId(4);
+
+        Organization v3 = new Organization();
+        v3.setId(3);
+        v3.setName("Test1");
+        v3.setCity("Test2");
+
+        Organization v4 = new Organization();
+        v4.setId(4);
+        v4.setName("Test3");
+        v4.setCity("Test4");
+
+        store.put(tx, k3, v3);
+        store.put(tx, k4, v4);
+
+        store.txEnd(tx, true);
+
+        assertEquals(v3, store.load(null, k3));
+        assertEquals(v4, store.load(null, k4));
+
+        OrganizationKey k5 = new OrganizationKey();
+        k5.setId(5);
+
+        assertNull(store.load( null, k5));
+
+        store.remove(tx, k3);
+
+        store.txEnd(tx, true);
+
+        assertNull(store.load(null, k3));
+        assertEquals(v4, store.load(null, k4));
+        assertNull(store.load(null, k3));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        Class.forName("org.h2.Driver");
+        Connection conn = 
DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", "");
+
+        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))");
+
+        stmt.executeUpdate("CREATE INDEX Org_Name_IDX On Organization (name)");
+        stmt.executeUpdate("CREATE INDEX Org_Name_City_IDX On Organization 
(name, city)");
+        stmt.executeUpdate("CREATE INDEX Person_Name_IDX1 On Person (name)");
+        stmt.executeUpdate("CREATE INDEX Person_Name_IDX2 On Person (name 
desc)");
+
+        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')" );
+
+        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')");
+
+        conn.commit();
+
+        U.closeQuiet(stmt);
+
+        U.closeQuiet(conn);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/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
new file mode 100644
index 0000000..fa1ac66
--- /dev/null
+++ 
b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/AutoCacheStoreSelfTest.java
@@ -0,0 +1,38 @@
+/* @java.file.header */
+
+/*  _________        _____ __________________        _____
+ *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
+ *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+ *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+ *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+ */
+
+package org.gridgain.grid.cache.store.auto;
+
+import org.gridgain.grid.util.typedef.*;
+
+/**
+ * TODO: Add class description.
+ */
+public class AutoCacheStoreSelfTest extends AbstractAutoCacheStoreSelfTest {
+    /**
+     * @throws Exception If failed.
+     */
+    public AutoCacheStoreSelfTest() throws Exception {
+    }
+
+    /**
+     * @return Store.
+     */
+    @Override protected AutoCacheStore store() {
+        AutoCacheStore store = new AutoCacheStore();
+
+        store.setConnUrl("jdbc:h2:mem:test");
+        store.setUser("sa");
+        store.setPassword("");
+
+        
store.setMetaPaths(F.asList("modules/schema-load/src/test/config/all.xml"));
+
+        return store;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/76408a4c/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/package.html
----------------------------------------------------------------------
diff --git 
a/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/package.html
 
b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/package.html
new file mode 100644
index 0000000..5cad80a
--- /dev/null
+++ 
b/modules/schema-load/src/test/java/org/gridgain/grid/cache/store/auto/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<!--
+    @html.file.header
+    _________        _____ __________________        _____
+    __  ____/___________(_)______  /__  ____/______ ____(_)_______
+    _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
+    / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
+    \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
+-->
+<html>
+<body>
+    <!-- Package description. -->
+    Contains internal tests or test related classes and interfaces.
+</body>
+</html>

Reply via email to