Bug 529602: Add CLOB compare for Oracle WHERE clause
Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
Reviewed-by: Lukas Jungmann <lukas.jungmann@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
index c7337aa..301a018 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2017 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -24,6 +24,8 @@
* - 458877 : Add national character support
* 02/23/2015-2.6 Dalia Abo Sheasha
* - 460607: Change DatabasePlatform StoredProcedureTerminationToken to be configurable
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
******************************************************************************/
package org.eclipse.persistence.internal.databaseaccess;
@@ -3517,4 +3519,14 @@ public String getConnectionUserName() {
throw new UnsupportedOperationException("Connection user name is not supported.");
}
+ /**
+ * INTERNAL:
+ * Override this method if the platform needs to use a custom function based on the DatabaseField
+ * @return An expression for the given field set equal to a parameter matching the field
+ */
+ public Expression createExpressionFor(DatabaseField field, Expression builder) {
+ Expression subExp1 = builder.getField(field);
+ Expression subExp2 = builder.getParameter(field);
+ return subExp1.equal(subExp2);
+ }
}
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
index 413914f..c8e6c1a 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -21,6 +21,8 @@
* - 485984: Retrieve FetchGroup info along with getReference() from cache
* 08/07/2016-2.7 Dalia Abo Sheasha
* - 499335: Multiple embeddable fields can't reference same object
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
******************************************************************************/
package org.eclipse.persistence.internal.descriptors;
@@ -2916,9 +2918,7 @@ public void createPrimaryKeyExpression(AbstractSession session) {
if(null != primaryKeyFields) {
for (int index = 0; index < primaryKeyFields.size(); index++) {
DatabaseField primaryKeyField = (DatabaseField)primaryKeyFields.get(index);
- subExp1 = builder.getField(primaryKeyField);
- subExp2 = builder.getParameter(primaryKeyField);
- subExpression = subExp1.equal(subExp2);
+ subExpression = session.getPlatform().createExpressionFor(primaryKeyField, builder);
if (expression == null) {
expression = subExpression;
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
index c4d4df8..38ed64c 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -20,6 +20,8 @@
* - 458877 : Add national character support
* 02/23/2015-2.6 Dalia Abo Sheasha
* - 460607: Change DatabasePlatform StoredProcedureTerminationToken to be configurable
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
*****************************************************************************/
package org.eclipse.persistence.platform.database;
@@ -40,6 +42,7 @@
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionOperator;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
@@ -1167,4 +1170,15 @@ public boolean shouldPrintForUpdateClause() {
return shouldPrintForUpdateClause;
}
+ @Override
+ public Expression createExpressionFor(DatabaseField field, Expression builder) {
+ if (field.getType() == java.sql.Clob.class ||
+ field.getType() == java.sql.Blob.class) {
+ Expression subExp1 = builder.getField(field);
+ Expression subExp2 = builder.getParameter(field);
+ subExp1 = subExp1.getFunction("dbms_lob.compare", subExp2);
+ return subExp1.equal(0);
+ }
+ return super.createExpressionFor(field, builder);
+ }
}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java
new file mode 100644
index 0000000..df17c1b
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob;
+
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.lob.model.CollectedEntity;
+import org.eclipse.persistence.jpa.test.lob.model.ParentEntity;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(EmfRunner.class)
+public class TestLobMerge {
+
+ @Emf(createTables = DDLGen.DROP_CREATE, classes = { CollectedEntity.class, ParentEntity.class })
+ private EntityManagerFactory emf;
+
+ /**
+ * Merging ElementCollections on Oracle fails when EclipseLink generates
+ * a DELETE SQL statement with a WHERE clause containing a CLOB.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testLobMerge() throws Exception {
+ EntityManager em = null;
+ try {
+ em = emf.createEntityManager();
+
+ final Set<CollectedEntity> col1 = new HashSet<CollectedEntity>(
+ Arrays.asList(new CollectedEntity[] {
+ new CollectedEntity("label1", "content1"),
+ new CollectedEntity("label2", "content2"),
+ new CollectedEntity("label3", "content3") }));
+
+ final ParentEntity pdo = new ParentEntity(9, Collections.unmodifiableSet(col1));
+ System.out.println("EM CREATED!!");
+ em.getTransaction().begin();
+ em.persist(pdo);
+ em.getTransaction().commit();
+
+ final Set<CollectedEntity> col2 = new HashSet<CollectedEntity>(
+ Arrays.asList(new CollectedEntity[] {
+ new CollectedEntity("label1", "content1"),
+ new CollectedEntity("label2", "content2") }));
+ final ParentEntity newEntity = new ParentEntity(pdo.getId(), col2);
+
+ try {
+ em.getTransaction().begin();
+ em.merge(newEntity);
+ //Failure would occur on merge, if it passed merge, test passed
+ em.getTransaction().commit();
+ } catch (final Exception e) {
+ System.err.println("Exception: " + e);
+ em.getTransaction().rollback();
+ }
+ } catch (Exception e) {
+ Assert.fail(e.getLocalizedMessage());
+ } finally {
+ if (em != null) {
+ em.close();
+ }
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java
new file mode 100644
index 0000000..b740000
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob.model;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import javax.persistence.Lob;
+
+@Embeddable
+public class CollectedEntity {
+
+ @Column(name = "label", nullable = false, unique = true)
+ private String label;
+
+ @Lob
+ @Column(name = "content", columnDefinition = "CLOB", nullable = false)
+ private String content;
+
+ public CollectedEntity() {}
+
+ public CollectedEntity(final String label, final String content) {
+ this.label = label;
+ this.content = content;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(final String label) {
+ this.label = label;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(final String content) {
+ this.content = content;
+ }
+
+ @Override
+ public int hashCode() {
+ return label.hashCode() + content.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object instanceof CollectedEntity) {
+ final CollectedEntity that = (CollectedEntity) object;
+ return this.label.equals(that.label)
+ && this.content.equals(that.content);
+ }
+ return false;
+ }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java
new file mode 100644
index 0000000..068e1ca
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * 02/14/2018-2.7 Will Dazey
+ * - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob.model;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+
+@Entity
+public class ParentEntity {
+
+ @Id private int id;
+
+ @ElementCollection
+ @CollectionTable(
+ joinColumns = {
+ @JoinColumn(name = "parent_id", referencedColumnName = "id")
+ })
+ private Set<CollectedEntity> subs;
+
+ public ParentEntity() {}
+
+ public ParentEntity(final int id, final Set<CollectedEntity> subs) {
+ this.id = id;
+ this.subs = Collections.unmodifiableSet(subs);
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(final int id) {
+ this.id = id;
+ }
+
+ public Set<CollectedEntity> getSubs() {
+ return subs;
+ }
+
+ public void setSubs(final Set<CollectedEntity> subs) {
+ this.subs = subs;
+ }
+
+ @Override
+ public int hashCode() {
+ return id;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object instanceof ParentEntity) {
+ final ParentEntity that = (ParentEntity) object;
+ return (this.id == that.id);
+ }
+ return false;
+ }
+}