From 02608cc658571514e87a57f1b477330dfdef782e Mon Sep 17 00:00:00 2001 From: Michael Dick Date: Tue, 11 Nov 2008 03:44:43 +0000 Subject: [PATCH] OPENJPA-765 Check for isInsertable and isUpdatable in HandlerFieldStrategy git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@712928 13f79535-47bb-0310-9956-ffa450edef68 --- .../jdbc/kernel/AbstractUpdateManager.java | 6 +- .../meta/strats/HandlerFieldStrategy.java | 38 ++++---- .../nullity/AbstractNullityTestCase.java | 66 +++++++++++++ .../persistence/nullity/BlobValue.java | 25 +++++ .../persistence/nullity/NullValues.java | 4 + .../nullity/TestBasicFieldNullity.java | 44 +-------- .../nullity/TestReadOnlyNullity.java | 78 +++++++++++++++ .../nullity/TimestampedEntity.java | 95 +++++++++++++++++++ 8 files changed, 294 insertions(+), 62 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/AbstractNullityTestCase.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestReadOnlyNullity.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TimestampedEntity.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java index db3f34345..2be2fbcb6 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java @@ -193,8 +193,9 @@ public abstract class AbstractUpdateManager BitSet dirty = sm.getDirty(); for (int i = 0; i < fields.length; i++) { if (dirty.get(fields[i].getIndex()) - && !bufferCustomInsert(fields[i], sm, store, customs)) + && !bufferCustomInsert(fields[i], sm, store, customs)) { fields[i].insert(sm, store, rowMgr); + } } if (sup == null) { Version vers = mapping.getVersion(); @@ -279,8 +280,9 @@ public abstract class AbstractUpdateManager FieldMapping[] fields = mapping.getDefinedFieldMappings(); for (int i = 0; i < fields.length; i++) { if (dirty.get(fields[i].getIndex()) - && !bufferCustomUpdate(fields[i], sm, store, customs)) + && !bufferCustomUpdate(fields[i], sm, store, customs)) { fields[i].update(sm, store, rowMgr); + } } ClassMapping sup = mapping.getJoinablePCSuperclassMapping(); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerFieldStrategy.java index 1b8246390..ef1aceeba 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerFieldStrategy.java @@ -122,27 +122,31 @@ public class HandlerFieldStrategy public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm) throws SQLException { - Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT); - if (row != null) { - Object value = sm.fetch(field.getIndex()); - if (!HandlerStrategies.set(field, value, store, row, _cols, _io, - field.getNullValue() == FieldMapping.NULL_NONE)) - if (field.getValueStrategy() != ValueStrategies.AUTOASSIGN) - throw new UserException(_loc.get("cant-set-value", - row.getFailedObject(), field, value)); + if (field.getColumnIO().isInsertable(0, false)) { + Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT); + if (row != null) { + Object value = sm.fetch(field.getIndex()); + if (!HandlerStrategies.set(field, value, store, row, _cols, + _io, field.getNullValue() == FieldMapping.NULL_NONE)) + if (field.getValueStrategy() != ValueStrategies.AUTOASSIGN) + throw new UserException(_loc.get("cant-set-value", row + .getFailedObject(), field, value)); + } } } public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm) throws SQLException { - Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE); - if (row != null){ - Object value = sm.fetch(field.getIndex()); - if (!HandlerStrategies.set(field, value, store, row, _cols, _io, - field.getNullValue() == FieldMapping.NULL_NONE)) - if (field.getValueStrategy() != ValueStrategies.AUTOASSIGN) - throw new UserException(_loc.get("cant-set-value", - row.getFailedObject(), field, value)); + if (field.getColumnIO().isUpdatable(0, false)) { + Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE); + if (row != null) { + Object value = sm.fetch(field.getIndex()); + if (!HandlerStrategies.set(field, value, store, row, _cols, + _io, field.getNullValue() == FieldMapping.NULL_NONE)) + if (field.getValueStrategy() != ValueStrategies.AUTOASSIGN) + throw new UserException(_loc.get("cant-set-value", row + .getFailedObject(), field, value)); + } } } @@ -154,7 +158,7 @@ public class HandlerFieldStrategy public int supportsSelect(Select sel, int type, OpenJPAStateManager sm, JDBCStore store, JDBCFetchConfiguration fetch) { if ((type == Select.TYPE_JOINLESS && sel.isSelected(field.getTable())) - || (_load && type == sel.TYPE_TWO_PART)) + || (_load && type == Select.TYPE_TWO_PART)) return 1; return 0; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/AbstractNullityTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/AbstractNullityTestCase.java new file mode 100644 index 000000000..91ef92011 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/AbstractNullityTestCase.java @@ -0,0 +1,66 @@ +/* + * 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.openjpa.persistence.nullity; + +import javax.persistence.EntityManager; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public abstract class AbstractNullityTestCase extends SingleEMFTestCase { + protected static boolean NEW = true; + + /** + * Asserts that the given instance can not be committed. + */ + void assertCommitFails(Object pc, boolean isNew, + Class expected) { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + if (isNew) { + em.persist(pc); + } + else { + em.merge(pc); + } + try { + em.getTransaction().commit(); + fail(); + } catch (Exception e) { + if (!expected.isAssignableFrom(e.getClass())) { + e.printStackTrace(); + fail("Expected " + expected.getName()); + } + } + } + + void assertCommitSucceeds(Object pc, boolean isNew) { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + if (isNew) + em.persist(pc); + else + em.merge(pc); + try { + em.getTransaction().commit(); + } catch (RuntimeException e) { + e.printStackTrace(); + fail(); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/BlobValue.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/BlobValue.java index cc9b2dc75..251646f51 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/BlobValue.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/BlobValue.java @@ -27,7 +27,32 @@ import java.io.Serializable; * */ public class BlobValue implements Serializable { + private static final long serialVersionUID = 833553627832894685L; private String strVal; private int intVal; private byte[] bytes; + + public String getStrVal() { + return strVal; + } + + public void setStrVal(String strVal) { + this.strVal = strVal; + } + + public int getIntVal() { + return intVal; + } + + public void setIntVal(int intVal) { + this.intVal = intVal; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java index 0c3979915..4487c8071 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java @@ -147,4 +147,8 @@ public class NullValues { public void setNotOptionalBlob(BlobValue notOptionalBlob) { this.notOptionalBlob = notOptionalBlob; } + + public int getVersion() { + return version; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java index d8bf5cbbd..f1ef9da4a 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java @@ -18,13 +18,10 @@ */ package org.apache.openjpa.persistence.nullity; -import javax.persistence.EntityManager; import javax.persistence.RollbackException; import org.apache.openjpa.persistence.InvalidStateException; import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - /** * Test @Basic(optional=true|false) and @Column(nullable=true|false) @@ -34,8 +31,7 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase; * * @author Pinaki Poddar */ -public class TestBasicFieldNullity extends SingleEMFTestCase { - private static boolean NEW = true; +public class TestBasicFieldNullity extends AbstractNullityTestCase { public void setUp() { setUp(CLEAR_TABLES, NullValues.class); @@ -116,44 +112,6 @@ public class TestBasicFieldNullity extends SingleEMFTestCase { pc.setNotNullableBlob(null); assertCommitFails(pc, !NEW, RollbackException.class); - - } - - /** - * Asserts that the given instance can not be committed. - */ - void assertCommitFails(Object pc, boolean isNew, Class expected) { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - if (isNew) - em.persist(pc); - else { - Object merged = em.merge(pc); - } - try { - em.getTransaction().commit(); - fail(); - } catch (Exception e) { - if (!expected.isAssignableFrom(e.getClass())) { - e.printStackTrace(); - fail("Expected " + expected.getName()); - } - } - } - - void assertCommitSucceeds(Object pc, boolean isNew) { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - if (isNew) - em.persist(pc); - else - em.merge(pc); - try { - em.getTransaction().commit(); - } catch (RuntimeException e) { - e.printStackTrace(); - fail(); - } } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestReadOnlyNullity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestReadOnlyNullity.java new file mode 100644 index 000000000..2623fa148 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestReadOnlyNullity.java @@ -0,0 +1,78 @@ +/* + * 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.openjpa.persistence.nullity; + +import javax.persistence.EntityManager; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.sql.AbstractDB2Dictionary; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; + +/** + * Testcase that verifies that null checking is omitted for "read only" fields. + * An example use case is a Date field that is set by the database (although in + * that case the field would be updateable=false and insertable=false). + */ +public class TestReadOnlyNullity extends AbstractNullityTestCase { + + private boolean skip = false; + + public void setUp() { + OpenJPAEntityManagerFactorySPI emf = createEMF(new Object[] {}); + if (((JDBCConfiguration) emf.getConfiguration()) + .getDBDictionaryInstance() instanceof AbstractDB2Dictionary) { + + setUp(CLEAR_TABLES, TimestampedEntity.class); + } else { + skip = true; + } + } + + /** + * Test that a non-insertable field may be set to null. This test is skipped + * for non-db2 databases. + */ + public void testNonInsertableBlobDoesNotFail() { + if (!skip) { + TimestampedEntity pc = new TimestampedEntity(); + pc.setNonInsertableNonNullableDate(null); + assertCommitSucceeds(pc, NEW); + } + // else no-op + } + + /** + * Test that a non-updatable field may be set to null. This test is skipped + * for non-db2 databases. + */ + public void testNonUpdatableBlobDoesNotFail() { + if (!skip) { + TimestampedEntity pc = new TimestampedEntity(); + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(pc); + em.getTransaction().commit(); + em.clear(); + + pc.setNonUpdatableNonNullableDate(null); + assertCommitSucceeds(pc, !NEW); + } + // else no-op + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TimestampedEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TimestampedEntity.java new file mode 100644 index 000000000..84d9a2f77 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TimestampedEntity.java @@ -0,0 +1,95 @@ +/* + * 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.openjpa.persistence.nullity; + +import java.sql.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Version; + +/** + * This Entity contains two date fields which are updated by the database. The + * fields are non-insertable and non-updatable respectively making them read + * only in many situations. The fields should be ignored when persisting or + * updating an entity (again respectively). + * + *

+ * The syntax used for the database to generate the date column is specific + * to Derby and DB2. Any testcase which uses this entity must ensure that one of + * those databases is used, or use pre-existing tables + *

+ */ +@Entity +public class TimestampedEntity { + @Id + @GeneratedValue + private int id; + + @Version + private int version; + + @Column(nullable = false, insertable = false, + columnDefinition = "DATE default '2008-01-01'") + private Date nonInsertableNonNullableDate; + + @Column(nullable = false, updatable = false, + columnDefinition = "DATE default '2008-01-01'") + private Date nonUpdatableNonNullableDate; + + public TimestampedEntity() { + setNonUpdatableNonNullableDate(new Date(52349606)); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public Date getNonInsertableNonNullableDate() { + return nonInsertableNonNullableDate; + } + + public void setNonInsertableNonNullableDate( + Date nonInsertableNonNullableDate) { + this.nonInsertableNonNullableDate = nonInsertableNonNullableDate; + } + + public Date getNonUpdatableNonNullableDate() { + return nonUpdatableNonNullableDate; + } + + public void setNonUpdatableNonNullableDate( + Date nonUpdatableNonNullableDate) { + this.nonUpdatableNonNullableDate = nonUpdatableNonNullableDate; + } +}