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();
+ }
+}