Compare commits

...
Author SHA1 Message Date
rob bygrave 249cc5df49 [maven-release-plugin] prepare release ebean-11.22.9 2018-10-15 22:35:12 +13:00
rob bygrave 2646e8c27f #1507 - SqlUpdate.execute PersistenceException is not translated into more specific exception (like DuplicateKeyException)
Support for sqlUpdate.executeBatch()
2018-10-15 22:30:42 +13:00
rob bygrave 3eaf47fd70 No effective change - add additional tests 2018-10-15 22:25:38 +13:00
rob bygrave 9d64352771 No effective change - add additional tests 2018-10-15 22:13:18 +13:00
rob bygrave aa7325b249 #1507 - SqlUpdate.execute PersistenceException is not translated into more specific exception (like DuplicateKeyException) 2018-10-15 22:12:39 +13:00
rob bygrave 2839434773 #1506 - @ElementCollection DDL generates 2 indexes on the same column 2018-10-13 00:23:44 +13:00
rob bygrave de9823b69b #1505 - Unidirectional @OneToOne orphanRemoval=true ... does not delete orphan that is replaced 2018-10-12 22:30:06 +13:00
rob bygrave 15cab24848 #1504 - ENH: Add SqlUpdate executeNow() method ... that executes now regardless of transaction batch mode
Add test for UpdateQuery (which executes immediately regardless)
2018-10-12 20:33:59 +13:00
rob bygrave b87e360ac7 #1504 - ENH: Add SqlUpdate executeNow() method ... that executes now regardless of transaction batch mode 2018-10-12 18:49:20 +13:00
rob bygrave 82e68d7756 [maven-release-plugin] prepare for next development iteration 2018-10-05 13:04:31 +13:00
rob bygrave 0371043914 [maven-release-plugin] prepare release ebean-11.22.8 2018-10-05 13:04:15 +13:00
rob bygrave 54b5684628 #1502 - findNative build query to findPagedList, the query count is incorrect. 2018-10-05 12:22:25 +13:00
rob bygrave da95ec0c05 #1501 - Change logging for io.ebeaninternal.dbmigration.DefaultDbMigration to "io.ebean.GenerateMigration" 2018-10-05 11:26:42 +13:00
rob bygrave b5eec93995 #1500 - Improve Postgres 10 partitioning support, easier to create multiple indexes on each partition 2018-10-05 10:41:40 +13:00
rob bygrave fc26765a7e #1499 - findNative with findIds (or findSingleAttributeList with id property) throws ArrayIndexOutOfBoundsException 2018-10-04 22:51:51 +13:00
rob bygrave 9a6d339449 [maven-release-plugin] prepare for next development iteration 2018-10-04 22:10:24 +13:00
26 changed files with 501 additions and 23 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.22.7</version>
<version>11.22.9</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.22.7</tag>
<tag>ebean-11.22.9</tag>
</scm>
<profiles>
+10
View File
@@ -98,6 +98,11 @@ public interface SqlUpdate {
/**
* Execute the update returning the number of rows modified.
* <p>
* Note that if the transaction has batch mode on then this update will use JDBC batch and may not execute until
* later - at commit time or a transaction flush. In this case this method returns -1 indicating that the
* update has been batched for later execution.
* </p>
* <p>
* After you have executed the SqlUpdate you can bind new variables using
* {@link #setParameter(String, Object)} etc and then execute the SqlUpdate
* again.
@@ -112,6 +117,11 @@ public interface SqlUpdate {
*/
int execute();
/**
* Execute the statement now regardless of the JDBC batch mode of the transaction.
*/
int executeNow();
/**
* Execute when addBatch() has been used to batch multiple bind executions.
*
@@ -312,4 +312,8 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
*/
int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
/**
* Execute the sql update regardless of transaction batch mode.
*/
int executeNow(SpiSqlUpdate sqlUpdate);
}
@@ -65,7 +65,7 @@ import java.util.List;
*/
public class DefaultDbMigration implements DbMigration {
protected static final Logger logger = LoggerFactory.getLogger(DefaultDbMigration.class);
protected static final Logger logger = LoggerFactory.getLogger("io.ebean.GenerateMigration");
private static final String initialVersion = "1.0";
@@ -24,9 +24,6 @@ public class ModelBuildElementTable {
VisitAllUsing.visitOne(targetDescriptor, new ModelBuildPropertyVisitor(ctx, table, targetDescriptor));
ctx.fkeyBuilder(table)
.addForeignKey(manyProp.getBeanDescriptor(), manyProp.getTableJoin(), true);
ctx.addTable(table);
}
@@ -2054,6 +2054,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return persister.executeSqlUpdate(updSql, t);
}
@Override
public int executeNow(SpiSqlUpdate sqlUpdate) {
return persister.executeSqlUpdateNow(sqlUpdate, null);
}
@Override
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
persister.addBatch(sqlUpdate, transaction);
@@ -133,6 +133,15 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
}
}
@Override
public int executeNow() {
if (server != null) {
return server.executeNow(this);
} else {
throw new IllegalStateException("server is null?");
}
}
@Override
public int[] executeBatch() {
if (server == null) {
@@ -8,6 +8,9 @@ import io.ebeaninternal.server.persist.BatchPostExecute;
import io.ebeaninternal.server.persist.BatchedSqlException;
import io.ebeaninternal.server.persist.PersistExecute;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
/**
* Wraps all the objects used to persist a bean.
*/
@@ -95,6 +98,13 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
return transaction.isBatchThisRequest();
}
/**
* Translate the SQLException into a specific exception given the platform.
*/
public PersistenceException translateSqlException(SQLException e) {
return transaction.translate(e.getMessage(), e);
}
/**
* Execute the statement.
*/
@@ -19,6 +19,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanManager;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.id.ImportedId;
import io.ebeaninternal.server.persist.BatchControl;
@@ -131,6 +132,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean[] dirtyProperties;
/**
* Imported OneToOne orphan that needs to be deleted.
*/
private EntityBean orphanBean;
/**
* Flag set when request is added to JDBC batch.
*/
@@ -1377,4 +1383,19 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion());
}
}
/**
* Set an orphan bean that needs to be deleted AFTER the request has persisted.
*/
public void setImportedOrphanForRemoval(BeanPropertyAssocOne<?> prop) {
Object orphan = getOrigValue(prop);
if (orphan instanceof EntityBean) {
orphanBean = (EntityBean)orphan;
}
}
public EntityBean getImportedOrphanForRemoval() {
return orphanBean;
}
}
@@ -28,18 +28,23 @@ public final class PersistRequestUpdateSql extends PersistRequest {
private boolean addBatch;
/**
* Create.
*/
private boolean forceNoBatch;
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
SpiTransaction t, PersistExecute persistExecute) {
SpiTransaction t, PersistExecute persistExecute, boolean forceNoBatch) {
super(server, t, persistExecute, sqlUpdate.getLabel());
this.type = Type.UPDATESQL;
this.updateSql = sqlUpdate;
this.forceNoBatch = forceNoBatch;
updateSql.reset();
}
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
SpiTransaction t, PersistExecute persistExecute) {
this(server, sqlUpdate, t, persistExecute, false);
}
@Override
public void profile(long offset, int flushCount) {
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
@@ -60,7 +65,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
@Override
public boolean isBatchThisRequest() {
return addBatch || super.isBatchThisRequest();
return !forceNoBatch && (addBatch || super.isBatchThisRequest());
}
@Override
@@ -74,10 +74,15 @@ public interface Persister {
int executeOrmUpdate(Update<?> update, Transaction t);
/**
* Execute the UpdateSql.
* Execute the SqlUpdate (taking into account transaction batch mode).
*/
int executeSqlUpdate(SqlUpdate update, Transaction t);
/**
* Execute the SqlUpdate now regardless of transaction batch mode.
*/
int executeSqlUpdateNow(SpiSqlUpdate update, Transaction t);
/**
* Execute the CallableSql.
*/
@@ -229,6 +229,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
if (!"".equals(propAnn.mappedBy())) {
prop.setOneToOneExported();
prop.setOrphanRemoval(propAnn.orphanRemoval());
} else if (propAnn.orphanRemoval()) {
prop.setOrphanRemoval(true);
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
@@ -137,7 +137,7 @@ public final class DefaultPersister implements Persister {
try {
return batchControl.execute(sqlUpdate.getSql(), sqlUpdate.isGetGeneratedKeys());
} catch (SQLException e) {
throw new PersistenceException(e);
throw transaction.translate(e.getMessage(), e);
}
}
@@ -150,6 +150,11 @@ public final class DefaultPersister implements Persister {
return executeOrQueue(new PersistRequestUpdateSql(server, (SpiSqlUpdate) updSql, (SpiTransaction) t, persistExecute));
}
@Override
public int executeSqlUpdateNow(SpiSqlUpdate updSql, Transaction t) {
return executeOrQueue(new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute, true));
}
/**
* Restore draft beans to match live beans given the query.
*/
@@ -896,6 +901,11 @@ public final class DefaultPersister implements Persister {
BeanDescriptor<?> desc = request.getBeanDescriptor();
SpiTransaction t = request.getTransaction();
EntityBean orphanForRemoval = request.getImportedOrphanForRemoval();
if (orphanForRemoval != null) {
delete(orphanForRemoval, request.getTransaction(), true);
}
// exported ones with cascade save
for (BeanPropertyAssocOne<?> prop : desc.propertiesOneExportedSave()) {
// check for partial beans
@@ -1092,6 +1102,10 @@ public final class DefaultPersister implements Persister {
// imported ones with save cascade
for (BeanPropertyAssocOne<?> prop : desc.propertiesOneImportedSave()) {
// check for partial objects
if (prop.isOrphanRemoval() && request.isDirtyProperty(prop)) {
request.setImportedOrphanForRemoval(prop);
}
if (request.isLoadedProperty(prop)) {
EntityBean detailBean = prop.getValueAsEntityBean(request.getEntityBean());
if (detailBean != null
@@ -1108,14 +1122,18 @@ public final class DefaultPersister implements Persister {
for (BeanPropertyAssocOne<?> prop : desc.propertiesOneExportedSave()) {
if (prop.isOrphanRemoval() && request.isDirtyProperty(prop)) {
Object origValue = request.getOrigValue(prop);
if (origValue instanceof EntityBean) {
delete((EntityBean) origValue, request.getTransaction(), true);
}
deleteOrphan(request, prop);
}
}
}
private void deleteOrphan(PersistRequestBean<?> request, BeanPropertyAssocOne<?> prop) {
Object origValue = request.getOrigValue(prop);
if (origValue instanceof EntityBean) {
delete((EntityBean) origValue, request.getTransaction(), true);
}
}
/**
* Support for loading any Imported Associated One properties that are not
* loaded but required for Delete cascade.
@@ -48,7 +48,7 @@ class ExeCallableSql {
}
} catch (SQLException ex) {
throw new PersistenceException(ex);
throw request.translateSqlException(ex);
} finally {
if (!batchThisRequest) {
@@ -55,8 +55,8 @@ class ExeUpdateSql {
request.postExecute();
return rowCount;
}
} catch (SQLException ex) {
throw new PersistenceException(ex);
} catch (SQLException e) {
throw request.translateSqlException(e);
} finally {
if (!batchThisRequest) {
@@ -148,7 +148,10 @@ class SqlTreeNodeBean implements SqlTreeNode {
public ScalarType<?> getSingleAttributeScalarType() {
if (properties == null || properties.length == 0) {
// if we have no property ask first children (in a distinct select with join)
// if we have also no children, NPE happens anyway.
if (children.length == 0) {
// expected to be a findIds query
return desc.getIdBinder().getBeanProperty().getScalarType();
}
return children[0].getSingleAttributeScalarType();
}
if (properties[0] instanceof STreePropertyAssocOne) {
@@ -769,6 +769,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
copy.mapKey = mapKey;
copy.id = id;
copy.label = label;
copy.nativeSql = nativeSql;
copy.useBeanCache = useBeanCache;
copy.useQueryCache = useQueryCache;
copy.readOnly = readOnly;
@@ -1050,7 +1051,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
CQueryPlanKey createQueryPlanKey() {
if (isNativeSql()) {
queryPlanKey = new NativeSqlQueryPlanKey(nativeSql + "-" + firstRow + "-" + maxRows);
queryPlanKey = new NativeSqlQueryPlanKey(type.ordinal() + nativeSql + "-" + firstRow + "-" + maxRows);
} else {
queryPlanKey = new OrmQueryPlanKey(planDescription(), maxRows, firstRow, rawSql);
}
@@ -36,6 +36,9 @@ create or replace function _partition_create(meta partition_meta, extra text)
language plpgsql
set timezone to 'UTC'
as $$
declare
idx_col text;
idx_name text;
begin
execute format('create table if not exists %I partition of %I for values from (''%s'') TO (''%s'')', meta.part_name, meta.base_name, meta.period_start, meta.period_end);
@@ -45,7 +48,12 @@ begin
end if;
if (length(meta.index_column) > 0) then
execute format('create index if not exists ix_%I_%s ON %I (%I)', meta.part_name, meta.index_column, meta.part_name, meta.index_column);
-- delimited for multiple indexes
foreach idx_col in array regexp_split_to_array(meta.index_column,';')
loop
idx_name = replace(idx_col, ',', '_');
execute format('create index if not exists ix_%I_%s ON %I (%s)', meta.part_name, idx_name, meta.part_name, idx_col);
end loop;
end if;
if (length(extra) > 0) then
@@ -7,6 +7,7 @@ import io.ebean.meta.MetaOrmQueryMetric;
import org.junit.Test;
import org.tests.model.basic.Country;
import org.tests.model.basic.Customer;
import org.tests.model.basic.EBasicWithUniqueCon;
import java.sql.Timestamp;
import java.util.List;
@@ -42,6 +43,32 @@ public class UpdateQueryTest extends BaseTestCase {
assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive");
}
@Test
public void update_withTransactionBatch() {
EbeanServer server = server();
try (Transaction transaction = server.beginTransaction()) {
transaction.setBatchMode(true);
UpdateQuery<Customer> update = server.update(Customer.class);
Query<Customer> query = update
.set("status", Customer.Status.ACTIVE)
.set("updtime", new Timestamp(System.currentTimeMillis()))
.where()
.eq("status", Customer.Status.NEW)
.gt("id", 99999)
.query();
// update executes now regardless of transaction batch mode
int rows = query.update();
assertThat(rows).isEqualTo(0);
transaction.commit();
}
}
@Test
@IgnorePlatform(Platform.SQLSERVER)
public void withTableAlias() {
@@ -199,4 +226,27 @@ public class UpdateQueryTest extends BaseTestCase {
assertThat(rows).isEqualTo(0);
}
@Test(expected = DuplicateKeyException.class)
public void exceptionTranslation() {
newEbasicWithUnique("o1","other1_a");
Integer id = newEbasicWithUnique("o2", "other1_b");
Ebean.update(EBasicWithUniqueCon.class)
.set("other", "other1_a")
.set("otherOne", "other1_a")
.where().idEq(id)
.update();
}
private Integer newEbasicWithUnique(String name, String other) {
EBasicWithUniqueCon b0 = new EBasicWithUniqueCon();
b0.setName(name);
b0.setOther(other);
b0.setOtherOne(other);
Ebean.save(b0);
return b0.getId();
}
}
@@ -854,6 +854,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return 0;
}
@Override
public int executeNow(SpiSqlUpdate sqlUpdate) {
return 0;
}
@Override
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
+12
View File
@@ -2,6 +2,7 @@ package org.tests.cache;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.tests.model.basic.Country;
import org.tests.model.basic.OCachedBean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
@@ -41,4 +42,15 @@ public class TestBeanCache extends BaseTestCase {
assertThat(sql).isEmpty();
}
@Test
public void find_whenNotExits() {
Country country = Ebean.find(Country.class)
.where()
.eq("name","NotValid")
.findOne();
assertThat(country).isNull();
}
}
@@ -61,6 +61,38 @@ public class TestOneToOneOrphanStringId extends BaseTestCase {
assertThat(deletes.get(0)).contains("delete from oto_atwo");
assertThat(deletes.get(1)).contains("delete from oto_aone");
}
@Test
public void test_remove() {
OtoAtwo b = new OtoAtwo("b3", "b test");
Ebean.save(b);
OtoAone a = new OtoAone("a3", "a test");
b.setAone(a);
Ebean.save(b);
LoggedSqlCollector.start();
OtoAone a2 = new OtoAone("a4", "a test");
b.setAone(a2);
Ebean.save(b);
List<String> sql = LoggedSqlCollector.current();
assertThat(sql).hasSize(3);
assertThat(sql.get(0)).contains("insert into oto_aone");
assertThat(sql.get(1)).contains("update oto_atwo set aone_id=? where id=?");
assertThat(sql.get(2)).contains("delete from oto_aone where id=?");
Ebean.delete(b);
List<String> deletes = LoggedSqlCollector.stop();
assertThat(deletes).hasSize(2);
assertThat(deletes.get(0)).contains("delete from oto_atwo");
assertThat(deletes.get(1)).contains("delete from oto_aone");
}
}
@@ -1,6 +1,9 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.PagedList;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Customer;
@@ -13,6 +16,114 @@ import static org.assertj.core.api.Assertions.assertThat;
public class TestQueryFindNative extends BaseTestCase {
@Test
public void findCount() {
ResetBasicData.reset();
String sql = "select n.id from contact n where n.first_name like ?";
LoggedSqlCollector.start();
int rowCount = server()
.findNative(Contact.class, sql)
.setParameter(1, "J%")
.findCount();
List<Integer> nativeIds =
server()
.findNative(Contact.class, sql)
.setParameter(1, "J%")
.findIds();
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(nativeIds).hasSize(rowCount);
assertThat(loggedSql).hasSize(2);
assertThat(loggedSql.get(0)).contains("select count(*) from ( select n.id from contact n where n.first_name like ?)");
assertThat(loggedSql.get(1)).startsWith("select n.id from contact n where n.first_name like ?");
}
@Test
public void findPagedList() {
ResetBasicData.reset();
String sql = "select n.id, n.first_name from contact n where n.first_name like ?";
PagedList<Contact> pagedList = server()
.findNative(Contact.class, sql)
.setParameter(1, "J%")
.setMaxRows(100)
.findPagedList();
LoggedSqlCollector.start();
int listSize = pagedList.getList().size();
int totalCount = pagedList.getTotalCount();
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(listSize).isEqualTo(totalCount);
assertThat(loggedSql).hasSize(2);
assertThat(loggedSql.get(0)).startsWith("select n.id, n.first_name from contact n where n.first_name like ?");
assertThat(loggedSql.get(1)).contains("select count(*) from ( select n.id, n.first_name from contact n where n.first_name like ?)");
}
@Test
public void findPagedList_withColumnAlias() {
ResetBasicData.reset();
String sql = "select n.id, 'SillyName' first_name from contact n where n.id < ? ";
PagedList<Contact> pagedList = server()
.findNative(Contact.class, sql)
.setParameter(1, 100)
.setMaxRows(100)
.findPagedList();
int listSize = pagedList.getList().size();
int totalCount = pagedList.getTotalCount();
assertThat(listSize).isEqualTo(totalCount);
for (Contact contact : pagedList.getList()) {
assertThat(contact.getFirstName()).isEqualTo("SillyName");
}
}
@Test
public void findIds() {
ResetBasicData.reset();
String sql = "select c.id from contact c where c.first_name like ? ";
List<Integer> ids = Ebean.createSqlQuery(sql)
.setParameter(1, "J%")
.findSingleAttributeList(Integer.class);
List<Integer> idsScalar =
server()
.findNative(Contact.class, sql)
.setParameter(1, "J%")
.findSingleAttributeList();
List<Integer> nativeIds =
server()
.findNative(Contact.class, sql)
.setParameter(1, "J%")
.findIds();
assertThat(nativeIds).isNotEmpty();
assertThat(nativeIds).containsAll(ids);
assertThat(idsScalar).containsAll(ids);
}
@Test
public void joinFromManyToOne() {
@@ -14,9 +14,48 @@ import org.tests.model.softdelete.EBasicSoftDelete;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
public class TestSoftDeleteBasic extends BaseTestCase {
@Test
public void testCascadeSaveDelete_other() {
EBasicSoftDelete bean = new EBasicSoftDelete();
bean.setName("cascadeOne");
bean.addChild("child1", 10);
bean.addChild("child2", 20);
bean.addChild("child3", 30);
bean.addNoSoftDeleteChild("nsd1", 101);
bean.addNoSoftDeleteChild("nsd2", 102);
Ebean.save(bean);
assertEquals(new Long(1), bean.getVersion());
assertEquals(new Long(1), bean.getChildren().get(0).getVersion());
LoggedSqlCollector.start();
Ebean.delete(bean);
// List<String> loggedSql = LoggedSqlCollector.stop();
//
// assertEquals(new Long(2), bean.getVersion());
// assertEquals(new Long(2), bean.getChildren().get(0).getVersion()); // Fails with 1
//
// // The children without SoftDelete are left as is (so no third statement)
// assertThat(loggedSql).hasSize(2);
//
// // first statement is a single bulk update of the children with SoftDelete
// assertThat(loggedSql.get(0)).contains("update ebasic_sdchild set deleted=");
// assertThat(loggedSql.get(0)).contains("where owner_id = ?");
//
// // second statement is the top level bean
// assertThat(loggedSql.get(1)).contains(
// "update ebasic_soft_delete set version=?, deleted=? where id=? and version=?");
}
@Test
public void testFindIdsWhenIncludeSoftDeletedChlld() {
@@ -0,0 +1,95 @@
package org.tests.update;
import io.ebean.BaseTestCase;
import io.ebean.DuplicateKeyException;
import io.ebean.Ebean;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import org.junit.Test;
import java.util.UUID;
public class TestSqlUpdateExceptions extends BaseTestCase {
private String sql = "insert into uuone (id, name, version) values (?,?,?)";
@Test(expected = DuplicateKeyException.class)
public void duplicateKey() {
UUID id = UUID.randomUUID();
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "hi");
sqlUpdate.setParameter(3, 1);
sqlUpdate.execute();
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "fail");
sqlUpdate.setParameter(3, 1);
sqlUpdate.execute();
}
@Test(expected = DuplicateKeyException.class)
public void duplicateKey_executeNow() {
UUID id = UUID.randomUUID();
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "hi");
sqlUpdate.setParameter(3, 1);
sqlUpdate.executeNow();
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "fail");
sqlUpdate.setParameter(3, 1);
sqlUpdate.executeNow();
}
@Test(expected = DuplicateKeyException.class)
public void duplicateKey_inBatch() {
UUID id = UUID.randomUUID();
try (Transaction transaction = Ebean.beginTransaction()) {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "hi in batch");
sqlUpdate.setParameter(3, 1);
sqlUpdate.addBatch();
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "fail in batch");
sqlUpdate.setParameter(3, 1);
sqlUpdate.addBatch();
transaction.commit();
}
}
@Test(expected = DuplicateKeyException.class)
public void duplicateKey_executeBatch() {
UUID id = UUID.randomUUID();
try (Transaction transaction = Ebean.beginTransaction()) {
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "hi in batch");
sqlUpdate.setParameter(3, 1);
sqlUpdate.addBatch();
sqlUpdate.setParameter(1, id);
sqlUpdate.setParameter(2, "fail in batch");
sqlUpdate.setParameter(3, 1);
sqlUpdate.addBatch();
int[] ints = sqlUpdate.executeBatch();
transaction.commit();
}
}
}
@@ -3,6 +3,7 @@ package org.tests.update;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.meta.MetaTimedMetric;
import org.junit.Assert;
import org.junit.Test;
@@ -38,6 +39,41 @@ public class TestSqlUpdateInTxn extends BaseTestCase {
}
}
@Test
public void testExecute_inTransaction_withBatch() {
try (Transaction transaction = Ebean.beginTransaction()) {
transaction.setBatchMode(true);
int row = Ebean.createSqlUpdate("update audit_log set description = description where id = ?")
.setParameter(1, 999999)
.execute();
// update statement using JDBC batch so not executed yet
assertThat(row).isEqualTo(-1);
transaction.commit();
}
}
@Test
public void testExecuteNow_inTransaction_withBatch() {
try (Transaction transaction = Ebean.beginTransaction()) {
transaction.setBatchMode(true);
int row = Ebean.createSqlUpdate("update audit_log set description = description where id = ?")
.setParameter(1, 999999)
.executeNow();
// update statement executed even though JDBC batch mode is on
assertThat(row).isEqualTo(0);
transaction.commit();
}
}
@Test
public void testSqlUpdateWithWhitespace() {