From c8a84fd37d9077bed999d104751face7d39a01bb Mon Sep 17 00:00:00 2001 From: Heath Thomann Date: Thu, 20 Oct 2011 16:37:15 +0000 Subject: [PATCH] OPENJPA-2051: Change to ensure entities are properly cascaded after a flush. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1186903 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/openjpa/conf/Compatibility.java | 30 +++++ .../org/apache/openjpa/kernel/BrokerImpl.java | 5 +- .../openjpa/persistence/cascade/Edge.java | 59 +++++++++ .../cascade/TestMultiCascadePersist.java | 117 ++++++++++++++++++ .../openjpa/persistence/cascade/Vertex.java | 78 ++++++++++++ .../persistence/cascade/VertexType.java | 66 ++++++++++ 6 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Edge.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/TestMultiCascadePersist.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Vertex.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/VertexType.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index c851feb7b..7fec3957f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -73,6 +73,8 @@ public class Compatibility { private boolean _useListAttributeForArrays = false; private boolean _metaFactoriesAreStrict = false; + private boolean _resetFlushFlagForCascadePersist = true;//OPENJPA-2051 + /** * Whether to require exact identity value types when creating object @@ -666,4 +668,32 @@ public class Compatibility { public void setMetaFactoriesAreStrict(boolean metaFactoriesAreStrict) { _metaFactoriesAreStrict = metaFactoriesAreStrict; } + + /** + * Whether OpenJPA should reset the internal state (flush flag) when cascading a persist to another + * Entity. That is, when a flush is performed, OpenJPA keep state to indicate the flush has been + * performed. In certain cascade persist scenarios the fact that a flush has been performed prior to + * a cascade persist can cause certain entities to not be written to the database given the prior + * flush. This property, when set, will cause the flush flag to be reset in cascade scenarios. For more + * details see JIRA OPENJPA-2051 + * + * @since 2.0.x + */ + public boolean getResetFlushFlagForCascadePersist(){ + return _resetFlushFlagForCascadePersist; + } + + /** + * Whether OpenJPA should reset the internal state (flush flag) when cascading a persist to another + * Entity. That is, when a flush is performed, OpenJPA keep state to indicate the flush has been + * performed. In certain cascade persist scenarios the fact that a flush has been performed prior to + * a cascade persist can cause certain entities to not be written to the database given the prior + * flush. This property, when set, will cause the flush flag to be reset in cascade scenarios. For more + * details see JIRA OPENJPA-2051 + * + * @since 2.0.x + */ + public void setResetFlushFlagForCascadePersist(boolean b){ + _resetFlushFlagForCascadePersist = b; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index f5fdbe488..c34729015 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -4109,7 +4109,10 @@ public class BrokerImpl lock(); try { switch (status) { - case STATUS_INIT: + case STATUS_INIT: + if (_compat.getResetFlushFlagForCascadePersist()){//OPENJPA-2051 + _flags &= ~FLAG_FLUSHED; + } _cache.add(sm); break; case STATUS_TRANSIENT: diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Edge.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Edge.java new file mode 100644 index 000000000..46c3efc81 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Edge.java @@ -0,0 +1,59 @@ +/* + * 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.cascade; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class Edge { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long oid; + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "SOURCE_OID") + private Vertex source; + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "TARGET_OID") + private Vertex target; + + protected Edge() { + } + + Edge( Vertex src ) { + this(); + this.source = src; + } + + public void setTarget( Vertex node ) { + this.target = node; + } + + public long getOid() { + return oid; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/TestMultiCascadePersist.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/TestMultiCascadePersist.java new file mode 100644 index 000000000..3e6cc2876 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/TestMultiCascadePersist.java @@ -0,0 +1,117 @@ +/* + * 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.cascade; + +import java.util.Collections; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; + +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestMultiCascadePersist extends SingleEMFTestCase { + + @Override + public void setUp() throws Exception { + setUp(DROP_TABLES, Vertex.class, VertexType.class, Edge.class); + } + + public void testSingleTransaction() { + OpenJPAEntityManager em = emf.createEntityManager(); + EntityTransaction tx = em.getTransaction(); + + tx.begin(); + + //The flush is important to the rest of the test. If this + //is removed, the test works as expected. While the flush + //at this point in the test may seem odd/unnecessary, it + //is more clear to perform a flush directly rather than + //something (e.g. query) which would cause a flush under + //the covers. See OPENJPA-2051 for more details. + em.flush(); + + VertexType defaultType = new VertexType( "default" ); + VertexType specialType = new VertexType( "special" ); + + em.persist(defaultType); + em.persist(specialType); + + Vertex src = new Vertex( defaultType ); + Vertex target = new Vertex( specialType ); + + Edge t = src.newEdge( target ); + assertNotNull( t ); + + em.persist(src); + + tx.commit(); + + TypedQuery q = em.createQuery( "SELECT t FROM Edge t", Edge.class ); + List resultList = q.getResultList(); + + assertEquals( 1, resultList.size() ); + assertEquals( 2, findAllVertexType(em).size() ); + if (emf.getConfiguration().getCompatibilityInstance().getResetFlushFlagForCascadePersist()){ + assertEquals( 2, findAllVertex(em).size() ); + } + else{ + //There *should* be 2 Vertex....but by default we can not fix this without a + //compatibility flag. + assertEquals( 1, findAllVertex(em).size() ); + } + } + + public VertexType findVertexTypeByName(EntityManager em, String name ) { + try { + TypedQuery query = em.createNamedQuery( "VertexType.findByName", + VertexType.class ); + query.setParameter( 1, name ); + return query.getSingleResult(); + } catch ( NoResultException nre ) { + return null; + } + } + + public List findAllVertexType(EntityManager em) { + try { + TypedQuery query = em.createNamedQuery( "VertexType.findAll", + VertexType.class ); + return query.getResultList(); + } catch ( NoResultException nre ) { + return Collections.emptyList(); + } + } + + public List findAllVertex(EntityManager em) { + try { + TypedQuery query = em.createNamedQuery( "Vertex.findAll", + Vertex.class ); + return query.getResultList(); + } catch ( NoResultException nre ) { + return Collections.emptyList(); + } + } +} + + + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Vertex.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Vertex.java new file mode 100644 index 000000000..e9917fc84 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/Vertex.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.cascade; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; + + +@Entity +@NamedQueries({ + @NamedQuery(name = "Vertex.findByName", + query = "SELECT n FROM Vertex n where n.type.name=?1"), + @NamedQuery(name = "Vertex.findAll", query = "SELECT n FROM Vertex n") }) +public class Vertex { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long oid; + + @OneToMany(mappedBy = "source", cascade = CascadeType.ALL) + private List outgoing; + + @OneToMany(mappedBy = "target", cascade = CascadeType.ALL) + private List incoming; + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "TYPE_OID") + private VertexType type; + + protected Vertex() { + this.incoming = new ArrayList(); + this.outgoing = new ArrayList(); + } + + public Vertex( VertexType type ) { + this(); + this.type = type; + type.instances.add( this ); + } + + public Edge newEdge( Vertex target ) { + Edge t = new Edge( this ); + outgoing.add( t ); + t.setTarget( target ); + return t; + } + + public long getOid() { + return oid; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/VertexType.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/VertexType.java new file mode 100644 index 000000000..58362f4aa --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cascade/VertexType.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.cascade; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; + +@Entity +@NamedQueries({ + @NamedQuery(name = "VertexType.findByName", + query = "SELECT t FROM VertexType t where t.name=?1"), + @NamedQuery(name = "VertexType.findAll", + query = "SELECT t FROM VertexType t") }) +public class VertexType { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long oid; + + @OneToMany(mappedBy = "type", cascade = CascadeType.ALL) + List instances; + + private String name; + + protected VertexType() { + this.instances = new ArrayList(); + } + + public VertexType( String name ) { + this(); + this.name = name; + } + + public String getName() { + return name; + } + + public long getOid() { + return oid; + } +}