Merge remote-tracking branch 'ebean/master' into feature/inheritance

This commit is contained in:
Roland Praml
2016-11-11 09:57:50 +01:00
8 changed files with 188 additions and 23 deletions
+12
View File
@@ -0,0 +1,12 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
spaces_around_operators = true
@@ -6,11 +6,10 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.io.IOException;
@@ -369,6 +368,11 @@ public class ScopedTransaction implements SpiTransaction {
transaction.flushBatchOnCascade();
}
@Override
public void flushBatchOnRollback() {
transaction.flushBatchOnRollback();
}
@Override
public void markNotQueryOnly() {
transaction.markNotQueryOnly();
@@ -5,11 +5,10 @@ import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import java.sql.Connection;
/**
@@ -117,6 +116,7 @@ public interface SpiTransaction extends Transaction {
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
@Override
int getBatchSize();
/**
@@ -235,6 +235,11 @@ public interface SpiTransaction extends Transaction {
*/
void flushBatchOnCascade();
/**
* If batch was on then effectively clear the batch such that we can handle exceptions and continue.
*/
void flushBatchOnRollback();
/**
* Mark the transaction explicitly as not being query only.
*/
@@ -25,7 +25,6 @@ import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdate;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -233,6 +232,15 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
@Override
public void rollbackTransIfRequired() {
if (batchOnCascadeSet) {
transaction.flushBatchOnRollback();
batchOnCascadeSet = false;
}
super.rollbackTransIfRequired();
}
/**
* Return true is this request was added to the JDBC batch.
*/
@@ -366,6 +374,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Process the persist request updating the document store.
*/
@Override
public void docStoreUpdate(DocStoreUpdateContext txn) throws IOException {
switch (type) {
@@ -387,6 +396,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Add this event to the queue entries in IndexUpdates.
*/
@Override
public void addToQueue(DocStoreUpdates docStoreUpdates) {
switch (type) {
case INSERT:
@@ -550,6 +560,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Return the bean associated with this request.
*/
@Override
public T getBean() {
return bean;
}
@@ -700,6 +711,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Set the generated key back to the bean. Only used for inserts with getGeneratedKeys.
*/
@Override
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// remember it for logging summary
@@ -718,6 +730,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Check for optimistic concurrency exception.
*/
@Override
public final void checkRowCount(int rowCount) {
if (ConcurrencyMode.VERSION == concurrencyMode && rowCount != 1) {
String m = Message.msg("persist.conc2", "" + rowCount);
@@ -761,6 +774,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Post processing.
*/
@Override
public void postExecute() {
changeLog();
@@ -1,15 +1,13 @@
package com.avaje.ebeaninternal.server.persist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import javax.persistence.PersistenceException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
/**
* Controls the batch ordering of persist requests.
@@ -226,6 +224,14 @@ public final class BatchControl {
flush(true);
}
/**
* Clears the batch, discarding all batched statements.
*/
public void clear() {
pstmtHolder.clear();
beanHoldMap.clear();
}
/**
* execute all the requests currently queued or batched.
*/
@@ -1,11 +1,9 @@
package com.avaje.ebeaninternal.server.persist;
import javax.persistence.PersistenceException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -119,8 +117,7 @@ public class BatchedPstmtHolder {
}
// clear the batch cache
stmtMap.clear();
maxSize = 0;
clear();
if (firstError != null) {
String msg = "Error when batch flush on sql: " + errorSql;
@@ -128,6 +125,11 @@ public class BatchedPstmtHolder {
}
}
public void clear() {
stmtMap.clear();
maxSize = 0;
}
/**
* Return the size of the biggest batched statement.
* <p>
@@ -10,14 +10,11 @@ import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEvent;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.io.IOException;
@@ -29,6 +26,8 @@ import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JDBC Connection based transaction.
@@ -235,6 +234,7 @@ public class JdbcTransaction implements SpiTransaction {
return logPrefix;
}
@Override
public String toString() {
return logPrefix;
}
@@ -324,6 +324,7 @@ public class JdbcTransaction implements SpiTransaction {
this.docStoreBatchSize = docStoreBatchSize;
}
@Override
public DocStoreMode getDocStoreMode() {
return docStoreMode;
}
@@ -400,7 +401,7 @@ public class JdbcTransaction implements SpiTransaction {
@Override
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
if (m2mIntersectionSave == null) {
// first attempt so yes allow this m2m intersection direction
// first attempt so yes allow this m2m intersection direction
m2mIntersectionSave = new HashMap<>();
m2mIntersectionSave.put(intersectionTable, beanName);
return true;
@@ -412,8 +413,8 @@ public class JdbcTransaction implements SpiTransaction {
return true;
}
// only allow if save coming from the same bean type
// to stop saves coming from both directions of m2m
// only allow if save coming from the same bean type
// to stop saves coming from both directions of m2m
return existingBean.equals(beanName);
}
@@ -480,6 +481,7 @@ public class JdbcTransaction implements SpiTransaction {
this.updateAllLoadedProperties = updateAllLoadedProperties;
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return updateAllLoadedProperties;
}
@@ -599,6 +601,7 @@ public class JdbcTransaction implements SpiTransaction {
}
}
@Override
public void checkBatchEscalationOnCollection() {
if (batchMode == PersistBatch.NONE && batchOnCascadeMode != PersistBatch.NONE) {
batchMode = batchOnCascadeMode;
@@ -606,6 +609,7 @@ public class JdbcTransaction implements SpiTransaction {
}
}
@Override
public void flushBatchOnCollection() {
if (batchOnCascadeSet) {
if (batchControl != null) {
@@ -634,6 +638,19 @@ public class JdbcTransaction implements SpiTransaction {
batchMode = oldBatchMode;
}
@Override
public void flushBatchOnRollback() {
if (batchControl != null) {
if (logger.isTraceEnabled()) {
logger.trace("... flushBatchOnRollback");
}
batchControl.clear();
}
// restore the previous batch mode
batchMode = oldBatchMode;
}
@Override
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
if (isBatch(batchMode, request.getType())) {
@@ -0,0 +1,105 @@
package com.avaje.tests.batchinsert;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.tests.model.basic.EBasicWithUniqueCon;
import com.avaje.tests.model.basic.EOptOneB;
import com.avaje.tests.model.basic.EOptOneC;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.sql.Savepoint;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class TestBatchOnCascadeExceptionHandling extends BaseTestCase {
@Test
public void testBatchScenarioWithSavepoint() throws SQLException {
EbeanServer server = Ebean.getDefaultServer();
Transaction txn = server.beginTransaction();
try {
server.save(createEntityWithName("before-savepoint"));
txn.flushBatch();
Savepoint sp = txn.getConnection().setSavepoint();
try {
server.save(createEntityWithName("confict"));
server.save(createEntityWithName("confict")); // unique key violation
} catch (PersistenceException e) {
txn.getConnection().rollback(sp);
server.save(createEntityWithName("after-savepoint"));
}
txn.commit();
} finally {
txn.end();
}
assertThat(server.find(EBasicWithUniqueCon.class).where().eq("name", "before-savepoint").findList()).isNotEmpty();
assertThat(server.find(EBasicWithUniqueCon.class).where().eq("name", "after-savepoint").findList()).isNotEmpty();
assertThat(server.find(EBasicWithUniqueCon.class).where().eq("name", "confict").findList()).isEmpty();
}
@Test
public void testBatchedInsertFailure() {
final EbeanServer server = Ebean.getDefaultServer();
server.save(createEntityWithName("foo"));
testBatchOnCascadeIsExceptionSafe(server, () -> {
server.save(createEntityWithName("foo")); // duplicate name on insert
});
}
@Test
public void testBatchedUpdateFailure() {
final EbeanServer server = Ebean.getDefaultServer();
server.save(createEntityWithName("bla"));
final EBasicWithUniqueCon bar = createEntityWithName("bar");
server.save(bar);
testBatchOnCascadeIsExceptionSafe(server, () -> {
bar.setName("bla");
server.save(bar); // duplicate name on update
});
}
@Test
public void testBatchedDeleteFailure() {
final EbeanServer server = Ebean.getDefaultServer();
final EOptOneC c = new EOptOneC();
server.save(c);
final EOptOneB b = new EOptOneB();
b.setC(c);
server.save(b);
testBatchOnCascadeIsExceptionSafe(server, () -> {
server.delete(c); // foreign key violation
});
}
protected void testBatchOnCascadeIsExceptionSafe(EbeanServer server, Runnable failingOperation) {
Transaction txn = server.beginTransaction();
assertThat(txn.getBatch()).isSameAs(PersistBatch.NONE);
assertThat(txn.getBatchOnCascade()).isSameAs(PersistBatch.ALL);
try {
failingOperation.run();
Assertions.fail("PersistenceException expected");
} catch (PersistenceException e) {
assertThat(txn.getBatch()).as("batch mode").isSameAs(PersistBatch.NONE); // should not have changed
BatchControl bc = ((SpiTransaction) txn).getBatchControl();
assertThat(bc == null || bc.isEmpty()).as("batch emtpy").isTrue();
} finally {
txn.end();
}
}
protected EBasicWithUniqueCon createEntityWithName(String name) {
EBasicWithUniqueCon it = new EBasicWithUniqueCon();
it.setName(name);
return it;
}
}