You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by dw...@apache.org on 2010/03/18 14:34:27 UTC
svn commit: r924766 - in /openjpa/branches/1.3.x:
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/
openjpa-kernel/src/main/java/org/apache/openjpa/kernel/
openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/
openjpa-persistence-jdbc/src/tes...
Author: dwoods
Date: Thu Mar 18 13:34:26 2010
New Revision: 924766
URL: http://svn.apache.org/viewvc?rev=924766&view=rev
Log:
OPENJPA-1550 Updated patch and new junits contributed by Heath Thomann.
Added:
openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/
openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/
openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java (with props)
openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java (with props)
Modified:
openjpa/branches/1.3.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/BatchingPreparedStatementManagerImpl.java
openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
openjpa/branches/1.3.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java
Modified: openjpa/branches/1.3.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/BatchingPreparedStatementManagerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/BatchingPreparedStatementManagerImpl.java?rev=924766&r1=924765&r2=924766&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/BatchingPreparedStatementManagerImpl.java (original)
+++ openjpa/branches/1.3.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/BatchingPreparedStatementManagerImpl.java Thu Mar 18 13:34:26 2010
@@ -192,12 +192,11 @@ public class BatchingPreparedStatementMa
//similar to this path, or I should say, the path which is taken instead of this path when
//we aren't using batching), we see that the catch block doesn't do a 'se.getNextException'.
//When we do a 'getNextException', the 'next exception' doesn't contain the same message as se.
- //That is, 'next exception' contains a subset msg which is contained in se. For legacy, should
- //we continute to use 'sqex' in the 'old path' and use 'se' in the next path/code?????
-// SQLException sqex = se.getNextException();
- // if (sqex == null)
- // sqex = se;
- SQLException sqex = se;
+ //That is, 'next exception' contains a subset msg which is contained in se.
+ SQLException sqex = se.getNextException();
+ if (sqex == null){
+ sqex = se;
+ }
if (se instanceof ReportingSQLException){
int index = ((ReportingSQLException) se).getIndexOfFirstFailedObject();
@@ -209,25 +208,27 @@ public class BatchingPreparedStatementMa
index = 0;
}
- //index should not be less than 0 this path, but if for some reason it is, lets
+ //index should not be less than 0 in this path, but if for some reason it is, lets
//resort to the 'old way' and simply pass the 'ps' as the failed object.
if (index < 0){
- throw SQLExceptions.getStore(sqex, ps, _dict);
+ throw SQLExceptions.getStore(se, ps, _dict);
}
else{
- throw SQLExceptions.getStore(sqex, ((RowImpl)(_batchedRows.get(index))).getFailedObject(), _dict);
+ throw SQLExceptions.getStore(se, ((RowImpl)(_batchedRows.get(index))).getFailedObject(), _dict);
}
}
else{
+ //per comments above, use 'sqex' rather than 'se'.
throw SQLExceptions.getStore(sqex, ps, _dict);
}
} finally {
_batchedSql = null;
batchedRows.clear();
- //Clear the Params now....should this be done above?
- ps.clearParameters();
+
+
if (ps != null) {
try {
+ ps.clearParameters();
ps.close();
} catch (SQLException sqex) {
throw SQLExceptions.getStore(sqex, ps, _dict);
Modified: openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=924766&r1=924765&r2=924766&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Thu Mar 18 13:34:26 2010
@@ -2186,8 +2186,14 @@ public class BrokerImpl
}
if (opt)
return new OptimisticException(t);
+
+ Object failedObject = null;
+ if (t[0] instanceof OpenJPAException){
+ failedObject = ((OpenJPAException)t[0]).getFailedObject();
+ }
+
return new StoreException(_loc.get("rolled-back")).
- setNestedThrowables(t).setFatal(true);
+ setNestedThrowables(t).setFatal(true).setFailedObject(failedObject);
}
/**
Modified: openjpa/branches/1.3.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java?rev=924766&r1=924765&r2=924766&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java (original)
+++ openjpa/branches/1.3.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java Thu Mar 18 13:34:26 2010
@@ -1045,7 +1045,7 @@ public class LoggingConnectionDecorator
// we are tracking parameters, then set the current
// parameter set to be the index of the failed
// statement so that the ReportingSQLException will
- // show the correct param
+ // show the correct param(s)
if (se instanceof BatchUpdateException
&& _paramBatch != null && shouldTrackParameters()) {
int[] count = ((BatchUpdateException) se).
@@ -1055,7 +1055,7 @@ public class LoggingConnectionDecorator
for (int i = 0; i < count.length; i++) {
// -3 is Statement.STATEMENT_FAILED, but is
// only available in JDK 1.4+
- if (count[i] == Statement.EXECUTE_FAILED) {
+ if (count[i] == -3) {
indexOfFirstFailedObject = i;
break;
}
@@ -1092,7 +1092,6 @@ public class LoggingConnectionDecorator
throw err;
} finally {
logTime(start);
- clearLogParameters(true);
handleSQLErrors(LoggingPreparedStatement.this, err);
}
}
@@ -1335,16 +1334,12 @@ public class LoggingConnectionDecorator
}
private void clearLogParameters(boolean batch) {
- //Made !batch...we only want to clear if
- //we are NOT using batching. If we clear now,
- //the _params will not be displayed in the resultant
- //exception message. But when should we 'clear' them???
- if (!batch){
- if (_params != null)
- _params.clear();
-
- if (_paramBatch != null)
- _paramBatch.clear();
+ if (_params != null) {
+ _params.clear();
+ }
+
+ if (batch && _paramBatch != null) {
+ _paramBatch.clear();
}
}
@@ -1531,6 +1526,9 @@ public class LoggingConnectionDecorator
private final String _sql;
private List _params = null;
private List _paramBatch = null;
+ //When batching is used, this variable contains the index into the last
+ //successfully executed batched statement.
+ int batchedRowsBaseIndex = 0;
public LoggingCallableStatement(CallableStatement stmt, String sql)
throws SQLException {
@@ -1626,11 +1624,29 @@ public class LoggingConnectionDecorator
}
public int[] executeBatch() throws SQLException {
+ int indexOfFirstFailedObject = -1;
+
logBatchSQL(this);
long start = System.currentTimeMillis();
SQLException err = null;
try {
- return super.executeBatch();
+ int[] toReturn = super.executeBatch();
+ //executeBatch is called any time the number of batched statements
+ //is equal to, or less than, batchLimit. In the 'catch' block below,
+ //the logic seeks to find an index based on the current executeBatch
+ //results. This is fine when executeBatch is only called once, but
+ //if executeBatch is called many times, the _paramsBatch will continue
+ //to grow, as such, to index into _paramsBatch, we need to take into
+ //account the number of times executeBatch is called in order to
+ //correctly index into _paramsBatch. To that end, each time executeBatch
+ //is called, lets get the size of _paramBatch. This will effectively
+ //tell us the index of the last successfully executed batch statement.
+ //If an exception is caused, then we know that _paramBatch.size was
+ //the index of the LAST row to successfully execute.
+ if (_paramBatch != null){
+ batchedRowsBaseIndex = _paramBatch.size();
+ }
+ return toReturn;
} catch (SQLException se) {
// if the exception is a BatchUpdateException, and
// we are tracking parameters, then set the current
@@ -1643,12 +1659,11 @@ public class LoggingConnectionDecorator
getUpdateCounts();
if (count != null && count.length <= _paramBatch.size())
{
- int index = -1;
for (int i = 0; i < count.length; i++) {
// -3 is Statement.STATEMENT_FAILED, but is
// only available in JDK 1.4+
if (count[i] == Statement.EXECUTE_FAILED) {
- index = i;
+ indexOfFirstFailedObject = i;
break;
}
}
@@ -1656,19 +1671,35 @@ public class LoggingConnectionDecorator
// no -3 element: it may be that the server stopped
// processing, so the size of the count will be
// the index
- if (index == -1)
- index = count.length + 1;
+ //See the Javadoc for 'getUpdateCounts'; a provider
+ //may stop processing when the first failure occurs,
+ //as such, it may only return 'UpdateCounts' for the
+ //first few which pass. As such, the failed
+ //index is 'count.length', NOT count.length+1. That
+ //is, if the provider ONLY returns the first few that
+ //passes (i.e. say an array of [1,1] is returned) then
+ //length is 2, and since _paramBatch starts at 0, we
+ //don't want to use length+1 as that will give us the
+ //wrong index.
+ if (indexOfFirstFailedObject == -1){
+ indexOfFirstFailedObject = count.length;
+ }
+
+ //Finally, whatever the index is at this point, add batchedRowsBaseIndex
+ //to it to get the final index. Recall, we need to start our index from the
+ //last batch which successfully executed.
+ indexOfFirstFailedObject += batchedRowsBaseIndex;
// set the current params to the saved values
- if (index < _paramBatch.size())
- _params = (List) _paramBatch.get(index);
+ if (indexOfFirstFailedObject < _paramBatch.size()){
+ _params = (List) _paramBatch.get(indexOfFirstFailedObject);
+ }
}
}
- err = wrap(se, LoggingCallableStatement.this);
+ err = wrap(se, LoggingCallableStatement.this, indexOfFirstFailedObject);
throw err;
} finally {
logTime(start);
- clearLogParameters(true);
handleSQLErrors(LoggingCallableStatement.this, err);
}
}
Added: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java?rev=924766&view=auto
==============================================================================
--- openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java (added)
+++ openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java Thu Mar 18 13:34:26 2010
@@ -0,0 +1,73 @@
+/*
+ * 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.batch.exception;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Ent1 {
+ // primary key:
+ @Id
+ private int pk;
+ public int getPk() {return pk;}
+ public void setPk(int pk) {this.pk = pk;}
+
+ private String name;
+ public String getName(){return name;}
+ public void setName(String str){
+ name = str;
+ }
+
+ public Ent1() {}
+ public Ent1(int pk, String str) {this.pk = pk;name=str;}
+
+ public String toString(){
+ return "Ent1 [pk = " + pk + ", " + name +"]";
+ }
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + pk;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Ent1 other = (Ent1) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (pk != other.pk)
+ return false;
+ return true;
+ }
+}
+
+
Propchange: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/Ent1.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java?rev=924766&view=auto
==============================================================================
--- openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java (added)
+++ openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java Thu Mar 18 13:34:26 2010
@@ -0,0 +1,325 @@
+/*
+ * 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.batch.exception;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.apache.openjpa.persistence.test.PersistenceTestCase;
+import org.apache.openjpa.util.ExceptionInfo;
+
+//This test was created for OPENJPA-1550. In this issue the user was
+//not able to get the 'failed object' (the object causing the failure) when
+//batch limit was -1 or a value greater than 1. Also, they found that the
+//'params' listed in the prepared statement were missing. This test will set
+//various batch limits and verify that with the fix to 1550, the correct
+//'failed object' and prepared statement is returned.
+public class TestBatchLimitException extends PersistenceTestCase {
+
+ static Ent1 expectedFailedObject;
+ final String expectedFailureMsg =
+ "INSERT INTO Ent1 (pk, name) VALUES (?, ?) [params=(int) 200, (String) twohundred]";
+
+ public EntityManagerFactory newEmf(String batchLimit) {
+ EntityManagerFactory emf =
+ createEMF(Ent1.class,
+ "openjpa.jdbc.SynchronizeMappings",
+ "buildSchema(ForeignKeys=true)",
+ "openjpa.jdbc.DBDictionary", batchLimit,
+ CLEAR_TABLES);
+
+ assertNotNull("Unable to create EntityManagerFactory", emf);
+ return emf;
+ }
+
+ public void setUp() {
+ expectedFailedObject = null;
+ }
+
+ // Test that we get the correct 'failed object' when we have a batchLimt
+ // of X and Y rows, where X>Y. A duplicate row will be inserted
+ // sometime within the Y rows. This will verify that we get the right
+ // 'failed object' and message.
+ public void testExceptionInFirstBatch() throws Throwable {
+ EntityManagerFactory emf = newEmf("batchLimit=-1");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ em.persist(new Ent1(1, "one"));
+ expectedFailedObject = new Ent1(200, "twohundred");
+ em.persist(expectedFailedObject);
+ em.persist(new Ent1(5, "five"));
+ em.getTransaction().commit();
+ em.close();
+
+ EntityManager em2 = emf.createEntityManager();
+
+ em2.getTransaction().begin();
+ em2.persist(new Ent1(0, "zero"));
+ em2.persist(new Ent1(2, "two"));
+ em2.persist(new Ent1(200, "twohundred"));
+ em2.persist(new Ent1(3, "three"));
+ em2.persist(new Ent1(1, "one"));
+ em2.persist(new Ent1(5, "five"));
+
+ try {
+ em2.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em2.getTransaction().isActive()) {
+ em2.getTransaction().rollback();
+ }
+ em2.close();
+ emf.close();
+ }
+ }
+
+ // Test that we get the correct 'failed object' when there is only one
+ // row in the batch. The 'batching' logic executes a different
+ // statement when only one row is to be updated/inserted.
+ public void testExceptionSingleBatchedRow() throws Throwable {
+ EntityManagerFactory emf = newEmf("batchLimit=-1");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ expectedFailedObject = new Ent1(200, "twohundred");
+ em.persist(expectedFailedObject);
+ em.getTransaction().commit();
+ em.close();
+
+ EntityManager em2 = emf.createEntityManager();
+
+ em2.getTransaction().begin();
+ em2.persist(new Ent1(200, "twohundred"));
+
+ try {
+ em2.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em2.getTransaction().isActive()) {
+ em2.getTransaction().rollback();
+ }
+ em2.close();
+ emf.close();
+ }
+ }
+
+ // Test that we get the correct 'failed object' and message when we
+ // have a batchLimt of X and Y rows, where Y>X. In this case, the
+ // batch is executed every time the batchLimt is hit. A duplicate
+ // row will be inserted sometime after X (X+1, i.e right at the
+ // boundary of the batch) to verify that we get the right
+ // 'failed object' and msg no matter which batch a duplicate is
+ // contained in. This test is important because as part of the
+ // fix to OPENJPA-1510 we had to add extra logic to keep track
+ // of which batch the 'failed object' was in, along with the
+ // index into that batch.
+ public void testExceptionInSecondBatch() throws Throwable {
+ EntityManagerFactory emf = newEmf("batchLimit=9");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ expectedFailedObject = new Ent1(200, "twohundred");
+ em.persist(expectedFailedObject);
+ em.getTransaction().commit();
+ em.close();
+
+ EntityManager em2 = emf.createEntityManager();
+
+ em2.getTransaction().begin();
+
+ // Put 9 objects/rows into the batch
+ for (int i = 0; i < 9; i++) {
+ em2.persist(new Ent1(i, "name" + i));
+ }
+
+ // Put the duplicate object/row as the first element in the second batch.
+ em2.persist(new Ent1(200, "twohundred"));
+
+ try {
+ em2.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em2.getTransaction().isActive()) {
+ em2.getTransaction().rollback();
+ }
+ em2.close();
+ emf.close();
+ }
+ }
+
+ // Same as testRowsGreaterThanBatchLimit_boundaryCase, but the object to cause the failure
+ // is in the middle of the second batch. testExceptioninSecondBatch puts
+ // the failing object as the first element in the second batch, this test puts
+ // it somewhere in the middle of the third batch. Again, we want to make sure our
+ // indexing into the batch containing the 'failed object' is correct.
+ public void testExceptionInThirdBatch() throws Throwable {
+ EntityManagerFactory emf = newEmf("batchLimit=9");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ expectedFailedObject = new Ent1(200, "twohundred");
+ em.persist(expectedFailedObject);
+ em.getTransaction().commit();
+ em.close();
+
+ EntityManager em2 = emf.createEntityManager();
+
+ em2.getTransaction().begin();
+
+ // Persist 21 objects/rows....as such we will have two 'full'
+ // batches (9*2=18) and 3 (21-18=3) objects/rows in the 3rd batch.
+ for (int i = 0; i < 22; i++) {
+ em2.persist(new Ent1(i, "name" + i));
+ }
+
+ // Put the duplicate row in the 3rd batch.
+ em2.persist(new Ent1(200, "twohundred"));
+
+ // Put a few more objects into the batch.
+ for (int i = 22; i < 40; i++) {
+ em2.persist(new Ent1(i, "name" + i));
+ }
+
+ try {
+ em2.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em2.getTransaction().isActive()) {
+ em2.getTransaction().rollback();
+ }
+ em2.close();
+ emf.close();
+ }
+ }
+
+ // Similar to the previous two tests, but lets run the test with a large
+ // batch with a failure, and then commit, then run large batches
+ // again with failures again.....just want to make sure things are not in
+ // some way 're-used' between the two commits as far as the indexes go.
+ public void testSecondExceptionHasRightIndex() throws Throwable {
+ testExceptionInThirdBatch();
+
+ EntityManagerFactory emf = newEmf("batchLimit=9");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+
+ for (int i = 40; i < 55; i++) {
+ em.persist(new Ent1(i, "name" + i));
+ }
+
+ em.persist(new Ent1(200, "twohundred"));
+
+ for (int i = 55; i < 65; i++) {
+ em.persist(new Ent1(i, "name" + i));
+ }
+
+ try {
+ em.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ emf.close();
+ }
+ }
+
+ public void testExceptionWithMultipleCommits() throws Throwable {
+ EntityManagerFactory emf = newEmf("batchLimit=-1");
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ em.persist(new Ent1(1, "one"));
+ expectedFailedObject = new Ent1(200, "twohundred");
+ em.persist(expectedFailedObject);
+ em.persist(new Ent1(5, "five"));
+ em.getTransaction().commit();
+ em.close();
+
+ EntityManager em2 = emf.createEntityManager();
+ em2.getTransaction().begin();
+ em2.persist(new Ent1(0, "zero"));
+ em2.persist(new Ent1(2, "two"));
+ em2.persist(new Ent1(3, "three"));
+ em2.getTransaction().commit();
+
+ em2.getTransaction().begin();
+ em2.persist(new Ent1(6, "six"));
+ em2.persist(new Ent1(200, "twohundred"));
+ em2.persist(new Ent1(7, "seven"));
+
+ try {
+ em2.getTransaction().commit();
+ } catch (Throwable excp) {
+ verifyExDetails(excp);
+ }
+ finally {
+ if (em2.getTransaction().isActive()) {
+ em2.getTransaction().rollback();
+ }
+ em2.close();
+ emf.close();
+ }
+ }
+
+ // Verify that the resultant exception contains the correct 'failed object'
+ // and exception message.
+ public void verifyExDetails(Throwable excp) throws Throwable {
+ // The cause should contain the 'failed object'
+ Throwable cause = excp.getCause();
+ verifyFailedObject(cause);
+ // The second cause should contain the message which shows the failing prepared statement.
+ cause = cause.getCause();
+ verifyExMsg(cause.getMessage());
+ }
+
+ public void verifyFailedObject(Throwable excp) throws Throwable {
+ if (excp instanceof ExceptionInfo) {
+ ExceptionInfo e = (ExceptionInfo) excp;
+
+ Ent1 failedObject = (Ent1) e.getFailedObject();
+
+ assertNotNull("Failed object was null.", failedObject);
+ assertEquals(expectedFailedObject, failedObject);
+ }
+ else {
+ throw excp;
+ }
+ }
+
+ public void verifyExMsg(String msg) {
+ assertNotNull("Exception message was null.", msg);
+ assertTrue("Did not see expected text in message. Expected <" + expectedFailureMsg + "> but was " + msg, msg
+ .contains(expectedFailureMsg));
+ }
+}
+
Propchange: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/batch/exception/TestBatchLimitException.java
------------------------------------------------------------------------------
svn:eol-style = native