crud example
diff --git a/benchmark/.classpath b/benchmark/.classpath
new file mode 100644
index 0000000..90cc704
--- /dev/null
+++ b/benchmark/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="META-INF/" kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="eclipselink.jar"/>
+	<classpathentry kind="lib" path="persistence.jar"/>
+	<classpathentry kind="lib" path="derby.jar"/>
+	<classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/benchmark/.project b/benchmark/.project
new file mode 100644
index 0000000..66a5239
--- /dev/null
+++ b/benchmark/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.persistence.example.jpa.benchmark</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+</projectDescription>
diff --git a/benchmark/benchmark.zip b/benchmark/benchmark.zip
new file mode 100644
index 0000000..4adf1d2
--- /dev/null
+++ b/benchmark/benchmark.zip
Binary files differ
diff --git a/benchmark/build.xml b/benchmark/build.xml
new file mode 100644
index 0000000..4756e05
--- /dev/null
+++ b/benchmark/build.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project basedir="." default="build" name="org.eclipse.persistence.example.jpa.benchmark">
+    <property environment="env"/>
+    <!--property name="JDBC_LIB" value="./mysql-connector-java-5.0.7-bin.jar"/-->
+    <property name="JDBC_LIB" value="./ojdbc6.jar"/>
+    <property name="JPA_LIB" value="./persistence.jar"/>
+    <property name="ECLIPSELINK_LIB" value="./eclipselink.jar"/>
+    <property name="AGENT" value="-javaagent:${ECLIPSELINK_LIB}"/>
+    <property name="debuglevel" value="source,lines,vars"/>
+    <path id="classpath">
+        <pathelement location="classes"/>
+        <pathelement location="${JDBC_LIB}"/>
+        <pathelement location="${JPA_LIB}"/>
+        <pathelement location="${ECLIPSELINK_LIB}"/>
+    </path>
+    <target name="init" depends="clean">
+        <mkdir dir="classes"/>
+    </target>
+    <target name="clean">
+        <delete dir="classes"/>
+    </target>
+
+    <target depends="init" name="build">
+        <echo message="${ant.project.name}: ${ant.file}"/>
+        <javac debug="true" debuglevel="${debuglevel}" destdir="classes">
+            <src path="src"/>
+            <exclude name="**/.svn/**"/>
+            <classpath refid="classpath"/>
+        </javac>
+        <copy todir="classes/META-INF">
+			<fileset dir="src/META-INF">
+				<include name="*.xml"/>
+			</fileset>
+		</copy>
+    </target>
+
+    <target name="test" depends="build">
+        <java classname="example.Test"
+                maxmemory="512m"
+                fork="true">
+            <jvmarg value="${AGENT}"/>
+            <classpath refid="classpath"/>
+        </java>
+    </target>
+    
+</project>
diff --git a/benchmark/src/META-INF/persistence.xml b/benchmark/src/META-INF/persistence.xml
new file mode 100644
index 0000000..d118444
--- /dev/null
+++ b/benchmark/src/META-INF/persistence.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_2_0.xsd"
+                version="1.0">
+    <persistence-unit name="benchmark" transaction-type="RESOURCE_LOCAL">
+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+        <class>model.Order</class>
+        <class>model.OrderLine</class>
+        <class>model.Customer</class>
+        <!--shared-cache-mode>NONE</shared-cache-mode-->
+        <properties>
+            <!-- Change this to access your own database. -->
+            <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver" />
+            <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:orcl" />
+            <property name="javax.persistence.jdbc.user" value="scott" />
+            <property name="javax.persistence.jdbc.password" value="tiger" />
+            <!--property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
+            <property name="javax.persistence.jdbc.url" value="jdbc:derby:test;create=true" />
+            <property name="javax.persistence.jdbc.user" value="" />
+            <property name="javax.persistence.jdbc.password" value="" /-->
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
+            <!--property name="eclipselink.cache.type.default" value="Full" /-->
+            <!--property name="eclipselink.jdbc.batch-writing" value="jdbc" /-->
+            <!--property name="eclipselink.logging.level" value="finest" /-->
+            <!--property name="eclipselink.profiler" value="PerformanceMonitor" /-->
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/benchmark/src/example/PerformanceResult.java b/benchmark/src/example/PerformanceResult.java
new file mode 100644
index 0000000..52b2b83
--- /dev/null
+++ b/benchmark/src/example/PerformanceResult.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 1998, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package example;
+
+import java.io.StringWriter;
+
+/**
+ * Generic performance result.
+ */
+public class PerformanceResult {
+    String testName;
+    int runRepeats;
+    int threads;
+    long runTime;
+    double average;
+    long min;
+    long max;
+    double standardDeviation;
+    long averageOperationTime;
+    long maxOperationTime;
+    long minOperationTime;
+    
+    public String toString() {
+        StringWriter writer = new StringWriter();
+        writer.write(this.testName + " Results");
+        writer.write("\nThreads:" + this.threads);
+        writer.write("\nRun repeats:" + this.runRepeats);
+        writer.write("\nRun time:" + (this.runTime / 1000000) + "ms");
+        writer.write("\nAverage result:" + this.average);
+        writer.write("\nMax result:" + this.max);
+        writer.write("\nMin result:" + this.min);
+        writer.write("\n% standard deviation:" + this.standardDeviation);
+        writer.write("\nMin operation time:" + this.minOperationTime + "ns");
+        writer.write("\nMax operation time:" + this.maxOperationTime + "ns");
+        writer.write("\nAverage operation time:" + this.averageOperationTime + "ns");
+        return writer.toString();
+    }
+}
diff --git a/benchmark/src/example/PerformanceTest.java b/benchmark/src/example/PerformanceTest.java
new file mode 100644
index 0000000..e0bafaa
--- /dev/null
+++ b/benchmark/src/example/PerformanceTest.java
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ * Copyright (c) 1998, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package example;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Generic performance test.
+ * This allows executing a timed run and measures and logs the performance.
+ * Logs the executions per run (minute), logs average and % stdev.
+ */
+public class PerformanceTest {
+
+    public static int REPEATS = 5;
+    public static long RUN_TIME = 10 * 10000L * 1000000L; //10 seconds (in nanos).
+    public static int THREADS = 1;
+
+    static WorkerThread[] threads;
+
+    /**
+     * Defines the work thread.
+     * A worker thread calls the run method in a loop,
+     * until it is suspended.
+     */
+    protected static class WorkerThread extends Thread {
+        volatile boolean isSuspended = true;
+        volatile boolean isDead = false;
+        Runnable runnable;
+        long min;
+        long max;
+        long total;
+        int count;
+
+        public void stopExecution() {
+            isDead = true;
+        }
+
+        /**
+         * After the next completion of the run method, suspend execution.
+         * The thread is still alive, but waiting to be signaled to resumed.
+         */
+        public void suspendExecution() {
+            isSuspended = true;
+        }
+
+        /**
+         * Wait for the thread to suspend.
+         * Synchronize will wait for wait to release synchronization.
+         */
+        public synchronized void joinExecution() {
+            if (!isSuspended) {
+                throw new RuntimeException("Must suspend first");
+            }
+        }
+
+        /**
+         * Continue running the run method.
+         */
+        public synchronized void resumeExecution() {
+            isSuspended = false;
+            min = Long.MAX_VALUE;
+            max = 0;
+            total = 0;
+            count = 0;
+            try {
+                notify();
+            } catch (Exception exception) {
+                throw new RuntimeException(exception.getMessage());
+            }
+        }
+
+        /**
+         * Run the test run method in a loop until killed.
+         */
+        public synchronized void run() {
+            try {
+                while (!isDead) {
+                    // Allows the thread to suspend itself when the current test is done.
+                    if (isSuspended) {
+                        wait();
+                    }
+                    long start = System.nanoTime();
+                    this.runnable.run();
+                    long end = System.nanoTime();
+                    long time = end - start;
+                    total = total + time;
+                    if (time > max) {
+                        max = time;
+                    }
+                    if (time < min) {
+                        min = time;
+                    }
+                    count++;
+                }
+            } catch (Exception exception) {
+                exception.printStackTrace();
+                throw new RuntimeException(exception.getMessage());
+            }
+        }
+    }
+    
+    /**
+     * Measure the performance of the run.
+     * Repeat the run REPEATS (5) times,
+     * and measure the number of execution in RUN_TIME (60s).
+     */
+    public PerformanceResult executeRun(String name, Runnable runnable) {
+        if (THREADS > 1) {
+            return executeMultiThreadedRun(name, runnable);
+        }
+        System.out.println("Starting run: " + name);
+                
+        List<Integer> results = new ArrayList<Integer>();
+        long max = 0;
+        long min = Long.MAX_VALUE;
+        // Repeat the test and baseline for the number of repeats.
+        for (int index = 0; index < REPEATS; index++) {
+            int executions = 0;
+            System.gc();
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ignore) {}
+            long startTime = System.nanoTime();
+            long endTime = startTime;
+            long lastEndTime = endTime;
+            // Count how many times the test can be invoked in the run time.
+            // This allows for the test run time to be easily changed.
+            while ((startTime + (RUN_TIME)) >= endTime) {
+                runnable.run();
+                executions++;
+                endTime = System.nanoTime();
+                long time = endTime - lastEndTime;
+                lastEndTime = endTime;
+                if (time > max) {
+                    max = time;
+                }
+                if (time < min) {
+                    min = time;
+                }
+            }
+            results.add(executions);
+            System.out.println("Done run: " +  index + " for: " + name + " - " + executions);
+            reset();
+        }
+        
+        System.out.println("Completed run: " + name);
+
+        PerformanceResult result = new PerformanceResult();
+        result.testName = name;
+        result.runRepeats = REPEATS;
+        result.runTime = RUN_TIME;
+        result.average = averageResults(results);
+        result.max = maxResults(results);
+        result.min = minResults(results);
+        result.standardDeviation = standardDeviationResults(results);
+        result.maxOperationTime = max;
+        result.minOperationTime = min;
+        result.averageOperationTime = (long) (RUN_TIME / result.average);
+        
+        System.out.println("");
+        System.out.println(result);
+        System.out.println("");
+        System.out.println("");
+        
+        return result;
+    }
+    
+    /**
+     * Allow the test to reset after a run.
+     */
+    public void reset() {
+        return;
+    }
+    
+    /**
+     * Measure the performance of the run.
+     * Repeat the run REPEATS (5) times,
+     * and measure the number of execution in RUN_TIME (60s).
+     */
+    public static PerformanceResult executeMultiThreadedRun(String name, Runnable runnable) {
+        System.out.println("Starting run: " + name);
+        
+        threads = new WorkerThread[THREADS];
+        for (int index = 0; index < THREADS; index++) {
+            threads[index] = new WorkerThread();
+        }
+        for (int index = 0; index < THREADS; index++) {
+            threads[index].runnable = runnable;
+        }
+        for (int index = 0; index < THREADS; index++) {
+            threads[index].start();
+        }
+        
+        long min = Long.MAX_VALUE;
+        long max = 0;
+        long totalTime = 0;
+        long totalExecutions = 0;
+        List<Integer> results = new ArrayList<Integer>();
+        // Repeat the test and baseline for the number of repeats.
+        for (int repeat = 0; repeat < REPEATS; repeat++) {
+            System.gc();
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ignore) {}
+            long startTime = System.nanoTime();
+            long endTime = startTime;
+            for (int index = 0; index < THREADS; index++) {
+                threads[index].resumeExecution();
+            }
+            while ((startTime + RUN_TIME) >= endTime) {
+                try {
+                    Thread.sleep(1);
+                } catch (Exception ignore) {}
+                endTime = System.nanoTime();
+            }
+            for (int index = 0; index < THREADS; index++) {
+                threads[index].suspendExecution();
+            }
+            Thread.yield();
+            for (int index = 0; index < THREADS; index++) {
+                threads[index].joinExecution();
+            }
+            int executions = 0;
+            for (int index = 0; index < THREADS; index++) {
+                WorkerThread thread = threads[index];
+                executions = executions + thread.count;
+                totalTime = totalTime + thread.total;
+                if (thread.max > max) {
+                    max = thread.max;
+                }
+                if (thread.min < min) {
+                    min = thread.min;
+                }
+            }
+            totalExecutions = totalExecutions + executions;
+            results.add(executions);
+            System.out.println("Done run: " +  repeat + " for: " + name);
+            
+        }
+
+        for (int index = 0; index < THREADS; index++) {
+            threads[index].stopExecution();
+        }
+        Thread.yield();
+        threads = null;
+        
+        System.out.println("Completed run: " + name);
+
+        PerformanceResult result = new PerformanceResult();
+        result.testName = name;
+        result.threads = THREADS;
+        result.runRepeats = REPEATS;
+        result.runTime = RUN_TIME;
+        result.average = averageResults(results);
+        result.max = maxResults(results);
+        result.min = minResults(results);
+        result.standardDeviation = standardDeviationResults(results);
+        result.maxOperationTime = max;
+        result.minOperationTime = min;
+        result.averageOperationTime = (totalTime / totalExecutions);
+        
+        System.out.println("");
+        System.out.println(result);
+        System.out.println("");
+        System.out.println("");
+        
+        return result;
+    }
+
+    /**
+     * Compute the max of the results.
+     */
+    public static int maxResults(List<Integer> times) {
+        int testMax = 0;
+        for (int index = 0; index < times.size(); index++) {
+            int time = (int)times.get(index);
+            if (time > testMax) {
+                testMax = time;
+            }
+        }
+        return testMax;
+    }
+
+    /**
+     * Compute the min of the results.
+     */
+    public static int minResults(List<Integer> times) {
+        int testMin = 0;
+        for (int index = 0; index < times.size(); index++) {
+            int time = (int)times.get(index);
+            if ((testMin == 0) || (time < testMin)) {
+                testMin = time;
+            }
+        }
+        return testMin;
+    }
+
+    /**
+     * Filter max and min from results.
+     */
+    public static List<Integer> filterMaxMinResults(List<Integer> times) {
+        List filteredTimes = new ArrayList(times);
+        if (filteredTimes.size() > 3) {
+            filteredTimes.remove((Integer)maxResults(times));
+            filteredTimes.remove((Integer)minResults(times));
+        }
+        return filteredTimes;
+    }
+
+    /**
+     * Compute the average of the results rejecting the min and max.
+     */
+    public static double averageResults(List<Integer> allTimes) {
+        // Compute the average reject the min and max to improve consistency.
+        List<Integer> times = filterMaxMinResults(allTimes);
+        double testAverage = 0;
+        for (int index = 0; index < times.size(); index++) {
+            int time = (int)times.get(index);
+            testAverage = testAverage + time;
+        }
+        testAverage = testAverage / times.size();
+        return testAverage;
+    }
+
+    /**
+     * Compute the standard deviation of the results rejecting the min and max.
+     */
+    public static double standardDeviationResults(List<Integer> allTimes) {
+        // Compute the average reject the min and max to improve consistency.
+        double testAverage = averageResults(allTimes);
+
+        // Compute the standard deviation reject the min and max to improve consistency.
+        List<Integer> times = filterMaxMinResults(allTimes);
+        double testStandardDeviation = 0;
+        for (int index = 0; index < times.size(); index++) {
+            int time = (int)times.get(index);
+            testStandardDeviation = testStandardDeviation + Math.pow(time - testAverage, 2);
+        }
+        testStandardDeviation = testStandardDeviation / times.size();
+        testStandardDeviation = Math.sqrt(testStandardDeviation);
+        // As percent of average
+        testStandardDeviation = (testStandardDeviation / testAverage) * 100;
+        return testStandardDeviation;
+    }
+}
diff --git a/benchmark/src/example/Test.java b/benchmark/src/example/Test.java
new file mode 100644
index 0000000..c0a1760
--- /dev/null
+++ b/benchmark/src/example/Test.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 1998, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package example;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Random;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+
+import model.Customer;
+import model.Order;
+import model.OrderLine;
+
+/**
+ * Test the performance of a mix of crud+q operations on an Order object model.
+ * @author James Sutherland
+ */
+public class Test extends PerformanceTest {
+    public static double INSERT = 0.2;
+    public static double UPDATE = 0.1;
+    public static double FIND = 0.4;
+    public static double QUERY = 0.3;
+    
+    public static int SIZE = 100;
+    public static int ORDER_PER_CUSTOMER_SIZE = 10;
+    public static int ORDER_LINES_SIZE = 5;
+    
+    long firstOrderId;
+    long firstCustomerId;
+    Random random = new Random();
+    EntityManagerFactory factory;
+
+    //Map junk = new HashMap();
+
+    public static void main(String[] args) throws Exception {
+        try {
+            Test test = new Test();
+            test.setup();
+            test.test();
+        } catch (Exception error) {
+            error.printStackTrace();
+        }
+        System.exit(0);
+    }
+    
+    public void setup() {
+        /*for (int count = 0; count < 10; count++) {
+            junk.put(new XMLEntityMappingsMappingProject("orm", "xsd"), new XMLEntityMappingsMappingProject("orm", "xsd"));
+        }*/
+        
+        System.out.println("Begin setup");
+        this.factory = Persistence.createEntityManagerFactory("benchmark");
+        EntityManager em = factory.createEntityManager();
+        em.getTransaction().begin();
+        Order order = null;
+        em.createQuery("Delete from OrderLine").executeUpdate();
+        em.createQuery("Delete from Order").executeUpdate();
+        em.createQuery("Delete from Customer").executeUpdate();
+        em.getTransaction().commit();
+        em.close();
+        em = factory.createEntityManager();
+        em.getTransaction().begin(); 
+        int index = 0;
+        int batchIndex = 0;
+        for (int customerIndex = 0; customerIndex < (SIZE / ORDER_PER_CUSTOMER_SIZE); customerIndex++) {
+            Customer customer = new Customer();
+            customer.setName("Customer-" + index);
+            em.persist(customer);
+            if (this.firstCustomerId == 0) {
+                this.firstCustomerId = customer.getId();
+            }
+            for (int orderIndex = 0; orderIndex < ORDER_PER_CUSTOMER_SIZE; orderIndex++) {
+                order = new Order();
+                order.setDescription("Order-" + index);
+                order.setCustomer(customer);
+                for (int orderLineIndex = 0; orderLineIndex < ORDER_LINES_SIZE; orderLineIndex++) {
+                    order.addOrderLine(new OrderLine("line" + orderLineIndex, new BigDecimal(10)));
+                }
+                em.persist(order);
+                if (this.firstOrderId == 0) {
+                    this.firstOrderId = order.getId();
+                }
+                index++;
+            }
+            batchIndex++;
+            if (batchIndex > 10) {
+                em.getTransaction().commit();
+                em.close();
+                em = factory.createEntityManager();
+                em.getTransaction().begin();
+                batchIndex = 0;
+            }
+        }
+        em.getTransaction().commit();
+        em.close();
+    }
+    
+    public void test() {
+        System.out.println("Begin benchmark");
+        this.executeRun("benchmark", new Runnable() {
+            public void run() {
+                if (random.nextFloat() <= INSERT) {
+                    runInsert(factory);
+                }
+                if (random.nextFloat() <= FIND) {
+                    runFind(factory);
+                }
+                if (random.nextFloat() <= UPDATE) {
+                    runUpdate(factory);
+                }
+                if (random.nextFloat() <= QUERY) {
+                    runQuery(factory);
+                }
+            }
+        });
+        System.out.println("End benchmark");
+    }
+    
+    public void reset() {
+        //this.factory.createEntityManager().unwrap(Session.class).setProfiler(new PerformanceMonitor());
+    }
+    
+    /** Query for n orders by customer id. */
+    public void runQuery(EntityManagerFactory factory) {
+        EntityManager em = factory.createEntityManager();
+        Query query = em.createNamedQuery("findOrdersByCustomer");
+        long id = this.firstCustomerId + this.random.nextInt(SIZE/ORDER_PER_CUSTOMER_SIZE);
+        query.setParameter("customerId", id);
+        List<Order> result = query.getResultList();
+        for (Order order : result) {
+            order.getCustomer().toString();
+            order.getOrderLines().size();
+        }
+        em.close();
+    }
+
+    /** Perform a find by id. */
+    public void runFind(EntityManagerFactory factory) {
+        EntityManager em = factory.createEntityManager();
+        long id = this.firstOrderId + this.random.nextInt(SIZE);
+        Order order = em.find(Order.class, id);
+        order.getCustomer().toString();
+        order.getOrderLines().size();
+        em.close();
+    }
+
+    /** Perform an insert. */
+    public void runInsert(EntityManagerFactory factory) {
+        EntityManager em = factory.createEntityManager();
+        em.getTransaction().begin();
+        Customer customer = new Customer();
+        customer.setName("Customer" + customer.hashCode());
+        em.persist(customer);
+        Order order = new Order();
+        order.setDescription("Order-" + order.hashCode());
+        order.setCustomer(customer);
+        for (int orderLineIndex = 0; orderLineIndex < ORDER_LINES_SIZE; orderLineIndex++) {
+            order.addOrderLine(new OrderLine("line" + orderLineIndex, new BigDecimal(10)));
+        }
+        em.persist(order);
+        em.getTransaction().commit();
+        em.close();
+    }
+
+    /** Perform an update. */
+    public void runUpdate(EntityManagerFactory factory) {
+        EntityManager em = factory.createEntityManager();
+        em.getTransaction().begin();
+        long id = this.firstOrderId + this.random.nextInt(SIZE);
+        Order order = em.find(Order.class, id);
+        order.getCustomer().toString();
+        int size = order.getOrderLines().size();
+        if (size > ORDER_LINES_SIZE) {
+            order.removeOrderLine(order.getOrderLines().get(size - 1));
+        } else {
+            order.addOrderLine(new OrderLine("line" + (size + 1), new BigDecimal(5)));
+        }
+        em.getTransaction().commit();
+        em.close();
+    }
+}
diff --git a/benchmark/src/model/Customer.java b/benchmark/src/model/Customer.java
new file mode 100644
index 0000000..0daaa29
--- /dev/null
+++ b/benchmark/src/model/Customer.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package model;
+
+import java.io.Serializable;
+
+import javax.persistence.*;
+
+/**
+ * Customer
+ * @author James Sutherland
+ */
+@Entity
+@Table(name="PERF_CUSTOMER")
+public class Customer implements Serializable {
+    @Id
+    @GeneratedValue(generator="CUST_SEQ", strategy=GenerationType.TABLE)
+    private long id;
+    @Basic
+    private String name;
+
+    public Customer() {
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
diff --git a/benchmark/src/model/Order.java b/benchmark/src/model/Order.java
new file mode 100644
index 0000000..8ac9d29
--- /dev/null
+++ b/benchmark/src/model/Order.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package model;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.*;
+
+/**
+ * Order
+ * @author James Sutherland
+ */
+@Entity(name="Order")
+@Table(name="PERF_ORDER")
+@NamedQueries({
+    @NamedQuery(name="findOrdersByCustomer",
+            query="Select o from Order o where o.customer.id = :customerId",
+            hints=@QueryHint(name="eclipselink.read-only", value="true"))
+})
+public class Order implements Serializable {
+    @Id
+    @GeneratedValue(generator="ORDER_SEQ", strategy=GenerationType.TABLE)
+    @Column(name="ORDER_ID")
+    private long id;
+    
+    @Basic
+    private String description;
+    
+    @Basic
+    private BigDecimal totalCost = BigDecimal.valueOf(0);
+    
+    @OneToMany(mappedBy="order", cascade=CascadeType.ALL, orphanRemoval=true)
+    @OrderBy("lineNumber")
+    private List<OrderLine> orderLines = new ArrayList<OrderLine>();
+    
+    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
+    private Customer customer;
+
+    public Order() {
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public BigDecimal getTotalCost() {
+        return totalCost;
+    }
+
+    public void setTotalCost(BigDecimal totalCost) {
+        this.totalCost = totalCost;
+    }
+
+    public List<OrderLine> getOrderLines() {
+        return orderLines;
+    }
+
+    public void setOrderLines(List<OrderLine> orderLines) {
+        this.orderLines = orderLines;
+    }
+
+    public Customer getCustomer() {
+        return customer;
+    }
+
+    public void setCustomer(Customer customer) {
+        this.customer = customer;
+    }
+    
+    /**
+     * Add the order line to the order, and set the back reference and update the order cost.
+     */
+    public void addOrderLine(OrderLine orderLine) {
+        orderLine.setOrder(this);
+        getOrderLines().add(orderLine);
+        orderLine.setLineNumber(getOrderLines().size());
+        setTotalCost(getTotalCost().add(orderLine.getCost()));
+    }
+    
+    /**
+     * Add the order line to the order, and set the back reference and update the order cost.
+     */
+    public void removeOrderLine(OrderLine orderLine) {
+        getOrderLines().remove(orderLine);
+        setTotalCost(getTotalCost().subtract(orderLine.getCost()));
+    }
+}
diff --git a/benchmark/src/model/OrderLine.java b/benchmark/src/model/OrderLine.java
new file mode 100644
index 0000000..8ff5799
--- /dev/null
+++ b/benchmark/src/model/OrderLine.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package model;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import javax.persistence.*;
+
+/**
+ * OrderLine, help by Order.
+ * @author James Sutherland
+ */
+@Entity
+@Table(name="PERF_ORDERLINE")
+@IdClass(OrderLineID.class)
+public class OrderLine implements Serializable {
+    @Id
+    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
+    @JoinColumn(name="ORDER_ID")
+    private Order order;
+    
+    @Id
+    private int lineNumber;
+    
+    @Basic
+    private String description;
+    
+    @Basic
+    private BigDecimal cost = BigDecimal.valueOf(0);
+
+    public OrderLine() {
+    }
+    
+    public OrderLine(String description, BigDecimal cost) {
+        this.description = description;
+        this.cost = cost;
+    }
+
+    public Order getOrder() {
+        return order;
+    }
+
+    public void setOrder(Order order) {
+        this.order = order;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public void setLineNumber(int lineNumber) {
+        this.lineNumber = lineNumber;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public BigDecimal getCost() {
+        return cost;
+    }
+
+    public void setCost(BigDecimal cost) {
+        this.cost = cost;
+    }
+    
+}
diff --git a/benchmark/src/model/OrderLineID.java b/benchmark/src/model/OrderLineID.java
new file mode 100644
index 0000000..1521dcc
--- /dev/null
+++ b/benchmark/src/model/OrderLineID.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Oracle and/or its affiliates. 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:
+ *      Oracle - initial impl
+ ******************************************************************************/
+package model;
+
+import java.io.Serializable;
+
+/**
+ * OrderLine Id class.
+ * @author James Sutherland
+ */
+public class OrderLineID implements Serializable {
+
+    private long order;
+    private int lineNumber;
+    
+    public long getOrder() {
+        return order;
+    }
+    public void setOrder(long order) {
+        this.order = order;
+    }
+    public int getLineNumber() {
+        return lineNumber;
+    }
+    public void setLineNumber(int lineNumber) {
+        this.lineNumber = lineNumber;
+    }    
+}