This is an automated email from the ASF dual-hosted git repository.

ivandasch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new c569d8682c5 IGNITE-18849 Add LOB and BLOB fields support to 
CacheJdbcPojoStore (#10553)
c569d8682c5 is described below

commit c569d8682c59ef33590e35088c67bbefc5618195
Author: Ilhom <[email protected]>
AuthorDate: Wed Mar 1 11:16:59 2023 +0300

    IGNITE-18849 Add LOB and BLOB fields support to CacheJdbcPojoStore (#10553)
---
 .../cache/store/jdbc/CacheAbstractJdbcStore.java   |  29 ++++-
 .../store/jdbc/JdbcTypesDefaultTransformer.java    |   3 +
 .../cache/store/jdbc/CacheJdbcPojoStoreTest.java   | 138 ++++++++++++++++++++-
 .../apache/ignite/cache/store/jdbc/model/Logo.java | 138 +++++++++++++++++++++
 .../ignite/cache/store/jdbc/model/LogoKey.java     |  86 +++++++++++++
 5 files changed, 392 insertions(+), 2 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheAbstractJdbcStore.java
 
b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheAbstractJdbcStore.java
index ac99dbf745a..26c9c725b92 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheAbstractJdbcStore.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/CacheAbstractJdbcStore.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.cache.store.jdbc;
 
 import java.sql.BatchUpdateException;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -1384,7 +1386,32 @@ public abstract class CacheAbstractJdbcStore<K, V> 
implements CacheStore<K, V>,
                     }
                 }
 
-                stmt.setObject(idx, fieldVal);
+                switch (field.getDatabaseFieldType()) {
+                    case Types.CLOB:
+                        if (fieldVal instanceof String) {
+                            Clob clob = stmt.getConnection().createClob();
+                            clob.setString(1, (String)fieldVal);
+                            stmt.setClob(idx, clob);
+                        }
+                        else {
+                            throw new CacheException("Failed to set statement 
parameter name: " + field.getDatabaseFieldName() +
+                                ", only String is allowed for CLOB field.");
+                        }
+                        break;
+                    case Types.BLOB:
+                        if (fieldVal instanceof byte[]) {
+                            Blob blob = stmt.getConnection().createBlob();
+                            blob.setBytes(1, (byte[])fieldVal);
+                            stmt.setBlob(idx, blob);
+                        }
+                        else {
+                            throw new CacheException("Failed to set statement 
parameter name: " + field.getDatabaseFieldName() +
+                                ", only byte[] is allowed for BLOB field.");
+                        }
+                        break;
+                    default:
+                        stmt.setObject(idx, fieldVal);
+                }
             }
             else
                 stmt.setNull(idx, field.getDatabaseFieldType());
diff --git 
a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypesDefaultTransformer.java
 
b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypesDefaultTransformer.java
index 47e178854f4..3b5720c2e83 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypesDefaultTransformer.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/cache/store/jdbc/JdbcTypesDefaultTransformer.java
@@ -114,6 +114,9 @@ public class JdbcTypesDefaultTransformer implements 
JdbcTypesTransformer {
                 return UUID.fromString((String)res);
         }
 
+        if (type == byte[].class)
+            return rs.getBytes(colIdx);
+
         if (type.isEnum()) {
             if 
(NUMERIC_TYPES.contains(rs.getMetaData().getColumnType(colIdx))) {
                 int ordinal = rs.getInt(colIdx);
diff --git 
a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java
 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java
index 00a12dbcd5a..a1b02a75126 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/CacheJdbcPojoStoreTest.java
@@ -19,8 +19,11 @@ package org.apache.ignite.cache.store.jdbc;
 
 import java.io.ByteArrayInputStream;
 import java.lang.reflect.Field;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
@@ -31,12 +34,15 @@ import java.util.Collection;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import javax.cache.integration.CacheWriterException;
+import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjectBuilder;
 import org.apache.ignite.cache.store.jdbc.dialect.H2Dialect;
 import org.apache.ignite.cache.store.jdbc.model.BinaryTest;
 import org.apache.ignite.cache.store.jdbc.model.BinaryTestKey;
+import org.apache.ignite.cache.store.jdbc.model.Logo;
+import org.apache.ignite.cache.store.jdbc.model.LogoKey;
 import org.apache.ignite.cache.store.jdbc.model.Organization;
 import org.apache.ignite.cache.store.jdbc.model.OrganizationKey;
 import org.apache.ignite.cache.store.jdbc.model.Person;
@@ -82,7 +88,7 @@ public class CacheJdbcPojoStoreTest extends 
GridAbstractCacheStoreSelfTest<Cache
     @Override protected CacheJdbcPojoStore<Object, Object> store() {
         CacheJdbcPojoStoreFactory<Object, Object> storeFactory = new 
CacheJdbcPojoStoreFactory<>();
 
-        JdbcType[] storeTypes = new JdbcType[7];
+        JdbcType[] storeTypes = new JdbcType[8];
 
         storeTypes[0] = new JdbcType();
         storeTypes[0].setDatabaseSchema("PUBLIC");
@@ -160,6 +166,18 @@ public class CacheJdbcPojoStoreTest extends 
GridAbstractCacheStoreSelfTest<Cache
         
storeTypes[6].setValueType("org.apache.ignite.cache.store.jdbc.model.BinaryTest");
         storeTypes[6].setValueFields(new JdbcTypeField(Types.BINARY, "VAL", 
byte[].class, "bytes"));
 
+        storeTypes[7] = new JdbcType();
+        storeTypes[7].setDatabaseSchema("PUBLIC");
+        storeTypes[7].setDatabaseTable("LOGO");
+        
storeTypes[7].setKeyType("org.apache.ignite.cache.store.jdbc.model.LogoKey");
+        storeTypes[7].setKeyFields(new JdbcTypeField(Types.INTEGER, "ID", 
Integer.class, "id"));
+
+        
storeTypes[7].setValueType("org.apache.ignite.cache.store.jdbc.model.Logo");
+        storeTypes[7].setValueFields(
+            new JdbcTypeField(Types.INTEGER, "ID", Integer.class, "id"),
+            new JdbcTypeField(Types.BLOB, "PICTURE", byte[].class, "picture"),
+            new JdbcTypeField(Types.CLOB, "DESCRIPTION", String.class, 
"description"));
+
         storeFactory.setTypes(storeTypes);
 
         storeFactory.setDialect(new H2Dialect());
@@ -230,6 +248,13 @@ public class CacheJdbcPojoStoreTest extends 
GridAbstractCacheStoreSelfTest<Cache
             // No-op.
         }
 
+        try {
+            stmt.executeUpdate("delete from Logo");
+        }
+        catch (SQLException ignore) {
+            // No-op.
+        }
+
         stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " +
             "String_Entries (key varchar(100) not null, val varchar(100), 
PRIMARY KEY(key))");
 
@@ -252,6 +277,9 @@ public class CacheJdbcPojoStoreTest extends 
GridAbstractCacheStoreSelfTest<Cache
             "Person_Complex (id integer not null, org_id integer not null, 
city_id integer not null, " +
             "name varchar(50), salary integer, PRIMARY KEY(id, org_id, 
city_id))");
 
+        stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " +
+            "Logo (id integer not null, picture blob not null, description 
clob not null, PRIMARY KEY(id))");
+
         conn.commit();
 
         U.closeQuiet(stmt);
@@ -580,6 +608,114 @@ public class CacheJdbcPojoStoreTest extends 
GridAbstractCacheStoreSelfTest<Cache
         assertNull(store.load(k));
     }
 
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testLoadCacheLobFields() throws Exception {
+        String longDescription = RandomStringUtils.randomAlphabetic(10000);
+        byte[] picture = new byte[64];
+
+        for (byte i = 0; i < 64; i++)
+            picture[i] = i;
+
+        Connection conn = null;
+        PreparedStatement logoStmt = null;
+
+        try {
+            conn = store.openConnection(false);
+
+            logoStmt = conn.prepareStatement("INSERT INTO Logo(id, picture, 
description) VALUES (?, ?, ?)");
+
+            logoStmt.setInt(1, 1);
+
+            Blob blob = logoStmt.getConnection().createBlob();
+            blob.setBytes(1, picture);
+            logoStmt.setBlob(2, blob);
+
+            Clob clob = logoStmt.getConnection().createClob();
+            clob.setString(1, longDescription);
+            logoStmt.setClob(3, clob);
+
+            logoStmt.executeUpdate();
+
+            conn.commit();
+        }
+        finally {
+            U.closeQuiet(logoStmt);
+            U.closeQuiet(conn);
+        }
+
+        IgniteBiInClosure<Object, Object> c = new CI2<Object, Object>() {
+            @Override public void apply(Object k, Object v) {
+                if (binaryEnable) {
+                    assertTrue(k instanceof BinaryObject);
+                    assertTrue(v instanceof BinaryObject);
+
+                    BinaryObject val = (BinaryObject)v;
+
+                    assertTrue(Arrays.equals(picture, val.field("picture")));
+                    assertEquals(longDescription, val.field("description"));
+                }
+                else {
+                    assertTrue(k instanceof LogoKey);
+                    assertTrue(v instanceof Logo);
+
+                    Logo val = (Logo)v;
+
+                    assertTrue(Arrays.equals(picture, val.getPicture()));
+                    assertEquals(longDescription, val.getDescription());
+                }
+            }
+        };
+
+        store.loadCache(c);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testLobWrite() throws Exception {
+        Integer id = 1;
+        LogoKey key = new LogoKey(id);
+
+        String longDescription = RandomStringUtils.randomAlphabetic(10000);
+        byte[] picture = new byte[64];
+
+        for (byte i = 0; i < 64; i++)
+            picture[i] = i;
+
+        Logo val = new Logo();
+
+        val.setId(id);
+        val.setPicture(picture);
+        val.setDescription(longDescription);
+
+        ses.newSession(null);
+
+        store.write(new CacheEntryImpl<>(wrap(key), wrap(val)));
+
+        Statement stmt = null;
+        Connection conn = null;
+
+        try {
+            conn = store.openConnection(false);
+            stmt = conn.createStatement();
+
+            ResultSet rs = stmt.executeQuery("SELECT picture, description FROM 
Logo");
+
+            assertTrue(rs.next());
+            assertTrue(Arrays.equals(picture, rs.getBytes(1)));
+            assertEquals(longDescription, rs.getString(2));
+            assertFalse(rs.next());
+        }
+        finally {
+            U.closeQuiet(stmt);
+            U.closeQuiet(conn);
+        }
+    }
+
     /**
      * @param obj Object.
      */
diff --git 
a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/Logo.java 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/Logo.java
new file mode 100644
index 00000000000..55416bee502
--- /dev/null
+++ 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/Logo.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.store.jdbc.model;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.StringJoiner;
+
+/** Logo definition. */
+public class Logo implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Value for id. */
+    private Integer id;
+
+    /** Logo as byte array. */
+    private byte[] picture;
+
+    /** Description as value. */
+    private String description;
+
+    /**
+     * Empty constructor.
+     */
+    public Logo() {
+        // No-op.
+    }
+
+    /** */
+    public Logo(Integer id, byte[] picture, String description) {
+        this.id = id;
+        this.picture = picture;
+        this.description = description;
+    }
+
+    /**
+     * Gets id.
+     *
+     * @return Value for id.
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id New value for id.
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets picture.
+     *
+     * @return Value for picture.
+     */
+    public byte[] getPicture() {
+        return picture;
+    }
+
+    /**
+     * Sets new picture.
+     *
+     * @param picture New value for picture.
+     */
+    public void setPicture(byte[] picture) {
+        this.picture = picture;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return Value for description.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description New value for description.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        Logo logo = (Logo)o;
+
+        if (id != null ? !id.equals(logo.id) : logo.id != null)
+            return false;
+        if (!Arrays.equals(picture, logo.picture))
+            return false;
+        return description != null ? description.equals(logo.description) : 
logo.description == null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + Arrays.hashCode(picture);
+        result = 31 * result + (description != null ? description.hashCode() : 
0);
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return new StringJoiner(", ", Logo.class.getSimpleName() + "[", "]")
+            .add("id=" + id)
+            .add("picture=" + Arrays.toString(picture))
+            .add("description='" + description + "'")
+            .toString();
+    }
+}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/LogoKey.java
 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/LogoKey.java
new file mode 100644
index 00000000000..de380562cb8
--- /dev/null
+++ 
b/modules/core/src/test/java/org/apache/ignite/cache/store/jdbc/model/LogoKey.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.store.jdbc.model;
+
+import java.io.Serializable;
+import java.util.StringJoiner;
+
+/** LogoKey definition. */
+public class LogoKey implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Value for id. */
+    private Integer id;
+
+    /**
+     * Empty constructor.
+     */
+    public LogoKey() {
+        // No-op.
+    }
+
+    /** */
+    public LogoKey(
+        Integer id
+    ) {
+        this.id = id;
+    }
+
+    /**
+     * Gets id.
+     *
+     * @return Value for id.
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id New value for id.
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        LogoKey key = (LogoKey)o;
+
+        return id != null ? id.equals(key.id) : key.id == null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return id != null ? id.hashCode() : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return new StringJoiner(", ", LogoKey.class.getSimpleName() + "[", "]")
+            .add("id=" + id)
+            .toString();
+    }
+}

Reply via email to