OPENJPA-2006: If flushing, also pre-flush non-provisional entities to allow sequence ID population - merged Jeremy's changes from trunk.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.0.x@1141193 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Heath Thomann 2011-06-29 18:45:09 +00:00
parent 2ede6e480c
commit 49f14ecdeb
8 changed files with 748 additions and 0 deletions

View File

@ -5075,4 +5075,8 @@ public class BrokerImpl
}
return _store.isCached(oids, loaded);
};
protected boolean isFlushing() {
return ((_flags & FLAG_FLUSHING) != 0);
}
}

View File

@ -782,6 +782,12 @@ class SingleFieldManager
// ensure generated IDs get assigned properly
if (!logical)
((StateManagerImpl)sm).assignObjectId(false, true);
// Call preFetch on this and any related persistent fields.
// This will ensure IDs get assigned to those that need them.
if (_broker.isFlushing()) {
((StateManagerImpl)sm).preFlush(logical, call);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.flush;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="FL_ASSIGN")
public class Assignment implements Serializable {
private static final long serialVersionUID = -7707299604883998179L;
@Id
@Column(name="ASSIGN_ID")
@SequenceGenerator(name="assignIdSeq", sequenceName="FL_ASSIGN_SEQ")
@GeneratedValue(generator="assignIdSeq", strategy=GenerationType.SEQUENCE)
protected Long assignId;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinColumn(name="TOPIC_ID")
protected Topic topic;
@Column(name="ASSIGN_TEXT")
protected String assignText;
public Long getAssignmentId() {
return assignId;
}
public void setAssignmentId(Long assignId) {
this.assignId = assignId;
}
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public String getAssignmentText() {
return assignText;
}
public void setAssignmentText(String assignText) {
this.assignText = assignText;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.flush;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.apache.openjpa.persistence.ElementDependent;
@Entity
@Table(name="FL_CLP")
public class ClassPeriod implements Serializable {
private static final long serialVersionUID = -5315185851562144594L;
@Id
@Column(name="CLP_ID")
@SequenceGenerator(name="clpIdSeq", sequenceName="FL_CLP_SEQ")
@GeneratedValue(generator="clpIdSeq", strategy=GenerationType.SEQUENCE)
protected Long clpId;
@Column(name="CLP_TEXT")
protected String clpText;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinColumn(name="COURSE_ID")
protected Course course;
@OneToMany(mappedBy="clp",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@ElementDependent(true)
protected Set<Topic> topics;
public Set<Topic> getTopics() {
return topics;
}
public void setTopics(Set<Topic> topics) {
this.topics = topics;
}
public Long getClassPeriodId() {
return clpId;
}
public void setClassPeriodId(Long clpId) {
this.clpId = clpId;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public String getClassPeriodText() {
return clpText;
}
public void setClassPeriodText(String clpText) {
this.clpText = clpText;
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.flush;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.apache.openjpa.persistence.ElementDependent;
@Entity
@Table(name="FL_COURSE")
public class Course implements Serializable {
private static final long serialVersionUID = -5351948190722744801L;
@Id
@Column(name="COURSE_ID")
@SequenceGenerator(name="courseIdSeq", sequenceName="FL_COURSE_SEQ")
@GeneratedValue(generator="courseIdSeq", strategy=GenerationType.SEQUENCE)
protected Long courseId;
@Column(name="COURSE_TEXT")
protected String courseText;
@OneToMany(mappedBy="course", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@ElementDependent(true)
protected Set<ClassPeriod> classPeriods;
public Long getCourseId() {
return courseId;
}
public void setCourseId(Long courseId) {
this.courseId = courseId;
}
public String getCourseText() {
return courseText;
}
public void setCourseText(String courseText) {
this.courseText = courseText;
}
public Set<ClassPeriod> getClassPeriods() {
return classPeriods;
}
public void setClassPeriods(Set<ClassPeriod> classPeriods) {
this.classPeriods = classPeriods;
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.flush;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="FL_SUBTOPIC")
public class SubTopic implements Serializable {
private static final long serialVersionUID = 1855479005964448251L;
@Id
@Column(name="SUBTOPIC_ID")
@SequenceGenerator(name="subtopicIdSeq", sequenceName="FL_SUBTOPIC_SEQ")
@GeneratedValue(generator="subtopicIdSeq", strategy=GenerationType.SEQUENCE)
protected Long subtopicId;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinColumn(name="TOPIC_ID")
protected Topic topic;
@Column(name="SUBTOPIC_TEXT")
protected String subtopicText;
public Long getSubtopicId() {
return subtopicId;
}
public void setSubtopicId(Long subtopicId) {
this.subtopicId = subtopicId;
}
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public String getSubtopicText() {
return subtopicText;
}
public void setSubtopicText(String subtopicText) {
this.subtopicText = subtopicText;
}
}

View File

@ -0,0 +1,307 @@
/*
* 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.flush;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.EntityManager;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestCascadingFlush extends SingleEMFTestCase {
boolean supportsNativeSequence = false;
public void setUp() {
setUp(Assignment.class, ClassPeriod.class, Course.class, SubTopic.class, Topic.class, CLEAR_TABLES);
try {
supportsNativeSequence = ((JDBCConfiguration) emf
.getConfiguration()).getDBDictionaryInstance()
.nextSequenceQuery != null;
} catch (Throwable t) {
supportsNativeSequence = false;
}
}
/**
* Verifies flushing a complex bidirectional domain model results in retrieval and population of all sequence-gen
* ID values.
*/
public void testCascadingFlushBasic() {
if (!supportsNativeSequence) {
return;
}
EntityManager em = emf.createEntityManager();
Long courseId = populate(em);
em.clear();
Course course = em.find(Course.class, courseId);
verifyCascadingFlush(em, course);
em.close();
}
/**
* Verifies flushing a complex bidirectional domain model results in retrieval and population of all sequence-gen
* ID values using an detached, then merged entity graph.
*/
public void testCascadingFlushDetach() {
if (!supportsNativeSequence) {
return;
}
EntityManager em = emf.createEntityManager();
Long courseId = populate(em);
em.clear();
Course tmpCourse = em.find(Course.class, courseId);
Course course = OpenJPAPersistence.cast(em).detachCopy(tmpCourse);
assertNotEquals(course, tmpCourse);
verifyCascadingFlush(em, course);
em.close();
}
/**
* Verifies flushing a complex bidirectional domain model results in retrieval of all sequence-gen ID values using
* an serialized (resulting in detach), then merged entity graph.
*/
public void testCascadingFlushSerialize() {
if (!supportsNativeSequence) {
return;
}
EntityManager em = emf.createEntityManager();
try {
Long courseId = populate(em);
em.clear();
Course tmpCourse = em.find(Course.class, courseId);
Course course = null;
try {
course = (Course)roundtrip(tmpCourse, false);
} catch (Throwable t) {
fail("Failed to serialize and deserialize persistent object.");
}
assertNotEquals(course, tmpCourse);
verifyCascadingFlush(em, course);
}
finally {
if (em != null) {
em.close();
}
}
}
private void verifyCascadingFlush(EntityManager em, Course course) {
try {
beginTx(em);
// Add a class period to the graph
addClassPeriod(course);
// Merge in the new entities
Course course2 = em.merge(course);
// Flush to the database. ID's should be assigned to all elements in
// the graph.
em.flush();
// Verify all id's are assigned
assertTrue(course2.getCourseId() > 0);
assertNotNull(course2.getClassPeriods());
Set<ClassPeriod> cps = course2.getClassPeriods();
assertTrue(cps.size() == 2);
for (ClassPeriod cp : cps) {
assertNotNull(cp);
assertTrue(cp.getClassPeriodId() > 0);
assertEquals(cp.getCourse(), course2);
Set<Topic> topics = cp.getTopics();
assertNotNull(topics);
assertTrue(topics.size() > 0);
for (Topic t : topics) {
assertNotNull(t);
assertTrue(t.getTopicId() > 0);
Set<Assignment> assignments = t.getAssignments();
assertNotNull(assignments);
assertTrue(assignments.size() == 1);
for (Assignment a : assignments) {
assertNotNull(a);
assertTrue(a.getAssignmentId() > 0);
}
Set<SubTopic> subTopics = t.getSubTopics();
assertNotNull(subTopics);
assertTrue(subTopics.size() == 1);
for (SubTopic s : subTopics) {
assertNotNull(s);
assertTrue(s.getSubtopicId() > 0);
}
}
}
commitTx(em);
} catch (Exception e) {
e.printStackTrace();
em.getTransaction().rollback();
fail();
}
}
public static Long populate(EntityManager em) {
beginTx(em);
Course course = createNewCourse();
em.persist(course);
em.flush();
commitTx(em);
return course.getCourseId();
}
public static Course createNewCourse() {
Course course = new Course();
course.setCourseText("Nuclear Physics");
Assignment assignment1 = new Assignment();
assignment1.setAssignmentText("Lab: Nuclear Fusion");
Set<Assignment> assignments = new HashSet<Assignment>();
assignments.add(assignment1);
SubTopic subtopic1 = new SubTopic();
subtopic1.setSubtopicText("Nuclear Fusion");
Set<SubTopic> subtopics = new HashSet<SubTopic>();
subtopics.add(subtopic1);
Topic topic1 = new Topic();
topic1.setTopicText("Fundamentals of Nuclear Energy");
topic1.setAssignments(assignments);
topic1.setSubTopics(subtopics);
assignment1.setTopic(topic1);
subtopic1.setTopic(topic1);
Set<Topic> topics = new HashSet<Topic>();
topics.add(topic1);
ClassPeriod cp1 = new ClassPeriod();
cp1.setClassPeriodText("8844: M,W,Th 8:00AM");
cp1.setTopics(topics);
cp1.setCourse(course);
topic1.setClassPeriod(cp1);
Set<ClassPeriod> cps = new HashSet<ClassPeriod>();
cps.add(cp1);
course.setClassPeriods(cps);
return course;
}
public static void addClassPeriod(Course course) {
Assignment assignment = new Assignment();
assignment.setAssignmentText("Read pages 442-645");
Set<Assignment> assignments = new HashSet<Assignment>();
assignments.add(assignment);
SubTopic subTopic = new SubTopic();
subTopic.setSubtopicText("Newton");
Set<SubTopic> subTopics = new HashSet<SubTopic>();
subTopics.add(subTopic);
Topic topic = new Topic();
topic.setTopicText("Gravity");
topic.setSubTopics(subTopics);
topic.setAssignments(assignments);
assignment.setTopic(topic);
subTopic.setTopic(topic);
Set<Topic> topics = new HashSet<Topic>();
topics.add(topic);
// Add another topic
Assignment assignment2 = new Assignment();
assignment2.setAssignmentText("Read pages 645-785");
Set<Assignment> assignments2 = new HashSet<Assignment>();
assignments2.add(assignment2);
SubTopic subTopic2 = new SubTopic();
subTopic2.setSubtopicText("Forces");
Set<SubTopic> subTopics2 = new HashSet<SubTopic>();
subTopics2.add(subTopic2);
Topic topic2 = new Topic();
topic2.setTopicText("Magnetism");
topic2.setSubTopics(subTopics2);
topic2.setAssignments(assignments2);
subTopic2.setTopic(topic);
subTopic2.setTopic(topic);
topics.add(topic2);
ClassPeriod cp2 = new ClassPeriod();
cp2.setClassPeriodText("8846: M,W,Th 11:00AM");
cp2.setTopics(topics);
cp2.setCourse(course);
topic.setClassPeriod(cp2);
topic2.setClassPeriod(cp2);
course.getClassPeriods().add(cp2);
}
private static void beginTx(EntityManager em) {
em.getTransaction().begin();
}
private static void commitTx(EntityManager em) {
em.getTransaction().commit();
}
public static Object roundtrip(Object orig, boolean validateEquality)
throws IOException, ClassNotFoundException {
assertNotNull(orig);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(orig);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin);
Object result = in.readObject();
if (validateEquality) {
assertEquals(orig.hashCode(), result.hashCode());
assertEquals(orig, result);
}
return result;
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.flush;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.apache.openjpa.persistence.ElementDependent;
@Entity
@Table(name="FL_TOPIC")
public class Topic implements Serializable {
private static final long serialVersionUID = -2570150606711529060L;
@Id
@Column(name="TOPIC_ID")
@SequenceGenerator(name="topicIdSeq", sequenceName="TOPIC_SEQ")
@GeneratedValue(generator="topicIdSeq", strategy=GenerationType.SEQUENCE)
protected Long topicId;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinColumn(name="CLP_ID")
protected ClassPeriod clp;
@Column(name="TOPIC_TEXT")
protected String topicText;
@OneToMany(mappedBy="topic",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@ElementDependent(true)
protected Set<Assignment> assignments;
@OneToMany(mappedBy="topic",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@ElementDependent(true)
protected Set<SubTopic> subTopics;
public Long getTopicId() {
return topicId;
}
public void setTopicId(Long topicId) {
this.topicId = topicId;
}
public ClassPeriod getClassPeriod() {
return clp;
}
public void setClassPeriod(ClassPeriod clp) {
this.clp = clp;
}
public String getTopicText() {
return topicText;
}
public void setTopicText(String topicText) {
this.topicText = topicText;
}
public Set<Assignment> getAssignments() {
return assignments;
}
public void setAssignments(Set<Assignment> assignments) {
this.assignments = assignments;
}
public Set<SubTopic> getSubTopics() {
return subTopics;
}
public void setSubTopics(Set<SubTopic> subTopics) {
this.subTopics = subTopics;
}
}