Compare commits

...
Author SHA1 Message Date
rob bygrave 4c2e9ccacd [maven-release-plugin] prepare release ebean-11.22.10 2018-10-19 20:07:16 +13:00
rob bygrave 29aa395cd4 #1509 - DBMigration add ... List<String> getPendingDrops(); 2018-10-19 16:24:06 +13:00
rob bygrave 8160d1f01c #1508 - Invalid SQL used to fetch identity value for @Draftable with database platform that uses Identity but not getGeneratedKeys 2018-10-19 15:56:39 +13:00
rob bygrave 6fd76ff697 [maven-release-plugin] prepare for next development iteration 2018-10-15 22:35:28 +13:00
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
rob bygrave 5b09443cdf [maven-release-plugin] prepare release ebean-11.22.7 2018-10-04 22:10:06 +13:00
rob bygrave 31093babe3 #1498 - findIds() with @SoftDelete bean returns all Ids (when soft deleted ones should not return)
And fix delete permanent that cascades
2018-10-04 20:49:30 +13:00
rob bygrave f8b9ca2034 #1498 - findIds() with @SoftDelete bean returns all Ids (when soft deleted ones should not return) 2018-10-04 20:26:00 +13:00
rob bygrave c27749ae2d #1497 - SQL Error with @History + @SoftDelete + @OneToOne fetch=FetchType.EAGER 2018-10-04 19:59:36 +13:00
rob bygrave 2c04430185 No effective change - add testSoftDelete_includeSoftDeletes_findOne 2018-10-04 17:14:53 +13:00
rob bygrave dcde47daf6 Merge branch 'master' of github.com:ebean-orm/ebean 2018-09-28 16:34:08 +12:00
rob bygrave 57056d7abd #1496 - Query label lost for findId() 2018-09-28 16:33:54 +12:00
Rob Bygrave 3409f264ec [maven-release-plugin] prepare for next development iteration 2018-09-26 23:38:57 +12:00
47 changed files with 953 additions and 84 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.22.6</version>
<version>11.22.10</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.6</tag>
<tag>ebean-11.22.10</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.
*
@@ -8,7 +8,9 @@ import java.util.regex.Pattern;
*/
public class DbIdentity {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
private static final String TABLE_PLACEHOLDER = "{table}";
private static final Pattern TABLE_REPLACE = Pattern.compile(TABLE_PLACEHOLDER, Pattern.LITERAL);
/**
* Set if this DB supports sequences. Note some DB's support both Sequences
@@ -53,7 +55,9 @@ public class DbIdentity {
if (selectLastInsertedIdTemplate == null) {
return null;
}
if (!selectLastInsertedIdTemplate.contains(TABLE_PLACEHOLDER)) {
return selectLastInsertedIdTemplate;
}
return TABLE_REPLACE.matcher(selectLastInsertedIdTemplate).replaceAll(Matcher.quoteReplacement(table));
}
@@ -7,6 +7,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
/**
@@ -147,6 +148,11 @@ public interface DbMigration {
*/
void addDatabasePlatform(DatabasePlatform databasePlatform, String prefix);
/**
* Return the list of versions that contain pending drops.
*/
List<String> getPendingDrops();
/**
* Generate the next migration xml file and associated apply and rollback sql scripts.
* <p>
@@ -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);
}
@@ -134,27 +134,40 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Includes soft deletes rows in the result.
*/
SOFT_DELETED,
SOFT_DELETED(false),
/**
* Query runs against draft tables.
*/
DRAFT,
DRAFT(false),
/**
* Query runs against current data (normal).
*/
CURRENT,
CURRENT(false),
/**
* Query runs potentially returning many versions of the same bean.
*/
VERSIONS,
VERSIONS(true),
/**
* Query runs 'As Of' a given date time.
*/
AS_OF;
AS_OF(true);
private final boolean history;
TemporalMode(boolean history) {
this.history = history;
}
/**
* Return true if this is a history query.
*/
public boolean isHistory() {
return history;
}
/**
* Return the mode of the query of if null return CURRENT mode.
@@ -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";
@@ -303,6 +303,23 @@ public class DefaultDbMigration implements DbMigration {
}
}
/**
* Return the versions containing pending drops.
*/
public List<String> getPendingDrops() {
if (!online) {
DbOffline.setGenerateMigration();
}
setDefaults();
try {
return createRequest().getPendingDrops();
} finally {
if (!online) {
DbOffline.reset();
}
}
}
/**
* Load the configuration for each of the target platforms.
*/
@@ -494,6 +511,9 @@ public class DefaultDbMigration implements DbMigration {
if (nextDrop != null) {
return nextDrop;
}
if (generatePendingDrop != null) {
return generatePendingDrop;
}
return migrationConfig.getGeneratePendingDrop();
}
@@ -595,9 +615,6 @@ public class DefaultDbMigration implements DbMigration {
if (name != null) {
migrationConfig.setName(name);
}
if (generatePendingDrop != null) {
migrationConfig.setGeneratePendingDrop(generatePendingDrop);
}
}
}
@@ -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,25 @@ 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;
}
/**
* Return the SQL used to fetch the last inserted id value.
*/
public String getSelectLastInsertedId() {
return beanDescriptor.getSelectLastInsertedId(publish);
}
}
@@ -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.
*/
@@ -182,6 +182,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* getGeneratedKeys is not supported.
*/
private final String selectLastInsertedId;
private final String selectLastInsertedIdDraft;
private final boolean autoTunable;
@@ -473,6 +474,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.sequenceInitialValue = deploy.getSequenceInitialValue();
this.sequenceAllocationSize = deploy.getSequenceAllocationSize();
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
this.concurrencyMode = deploy.getConcurrencyMode();
this.updateChangesOnly = deploy.isUpdateChangesOnly();
this.indexDefinitions = deploy.getIndexDefinitions();
@@ -3086,8 +3088,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* supported.
* </p>
*/
public String getSelectLastInsertedId() {
return selectLastInsertedId;
public String getSelectLastInsertedId(boolean publish) {
return publish ? selectLastInsertedId : selectLastInsertedIdDraft;
}
/**
* Return true if this bean uses a SQL select to fetch the last inserted id value.
*/
public boolean supportsSelectLastInsertedId() {
return selectLastInsertedId != null;
}
@Override
@@ -1387,9 +1387,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
if (IdType.IDENTITY == desc.getIdType()) {
// used when getGeneratedKeys is not supported (SQL Server 2000)
// used when getGeneratedKeys is not supported (SQL Server 2000, SAP Hana)
String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable());
desc.setSelectLastInsertedId(selectLastInsertedId);
String selectLastInsertedIdDraft = (!desc.isDraftable()) ? selectLastInsertedId : dbIdentity.getSelectLastInsertedId(desc.getDraftTable());
desc.setSelectLastInsertedId(selectLastInsertedId, selectLastInsertedIdDraft);
return;
}
@@ -318,11 +318,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
/**
* Find the Id's of detail beans given a parent Id or list of parent Id's.
*/
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds) {
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds, boolean hard) {
if (parentId != null) {
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds);
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds, hard);
} else {
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds);
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds, hard);
}
}
@@ -127,14 +127,16 @@ class BeanPropertyAssocManySqlHelp<T> {
many.bindParentIdsIn(expr, parentIds, query);
}
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds, boolean hard) {
String rawWhere = deriveWhereParentIdSql(false, "");
SpiEbeanServer server = descriptor.getEbeanServer();
SpiQuery<?> q = many.newQuery(server);
many.bindParentIdEq(rawWhere, parentId, q);
if (hard) {
q.setIncludeSoftDeletes();
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
}
@@ -142,7 +144,7 @@ class BeanPropertyAssocManySqlHelp<T> {
return server.findIds(q, t);
}
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds, boolean hard) {
String rawWhere = deriveWhereParentIdSql(true, "");
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
@@ -153,7 +155,9 @@ class BeanPropertyAssocManySqlHelp<T> {
SpiQuery<?> q = many.newQuery(server);
//Query<?> q = server.find(propertyType);
many.bindParentIdsIn(expr, parentIds, q);
if (hard) {
q.setIncludeSoftDeletes();
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
}
@@ -126,6 +126,7 @@ public class DeployBeanDescriptor<T> {
* Used with Identity columns but no getGeneratedKeys support.
*/
private String selectLastInsertedId;
private String selectLastInsertedIdDraft;
/**
* The concurrency mode for beans of this type.
@@ -839,11 +840,16 @@ public class DeployBeanDescriptor<T> {
return selectLastInsertedId;
}
public String getSelectLastInsertedIdDraft() {
return selectLastInsertedIdDraft;
}
/**
* Set the SQL used to return the last inserted Id.
*/
public void setSelectLastInsertedId(String selectLastInsertedId) {
public void setSelectLastInsertedId(String selectLastInsertedId, String selectLastInsertedIdDraft) {
this.selectLastInsertedId = selectLastInsertedId;
this.selectLastInsertedIdDraft = selectLastInsertedIdDraft;
}
/**
@@ -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.
*/
@@ -754,7 +759,7 @@ public final class DefaultPersister implements Persister {
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
List<Object> childIds = many.findIdsByParentId(id, idList, t, null, deleteMode.isHard());
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
}
@@ -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
@@ -1052,7 +1062,7 @@ public final class DefaultPersister implements Persister {
} else {
// Delete recurse using the Id values of the children
Object parentId = desc.getId(parentBean);
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds, deleteMode.isHard());
if (!idsByParentId.isEmpty()) {
deleteChildrenById(t, targetDesc, idsByParentId, deleteMode);
}
@@ -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) {
@@ -39,7 +39,7 @@ public class InsertHandler extends DmlHandler {
* A SQL Select used to fetch back the Id where generatedKeys is not
* supported.
*/
private String selectLastInsertedId;
private boolean useSelectLastInsertedId;
/**
* Create to handle the insert execution.
@@ -79,7 +79,7 @@ public class InsertHandler extends DmlHandler {
useGeneratedKeys = true;
} else {
// use a query to get the last inserted id
selectLastInsertedId = meta.getSelectLastInsertedId();
useSelectLastInsertedId = meta.supportsSelectLastInsertedId();
}
}
@@ -117,8 +117,7 @@ public class InsertHandler extends DmlHandler {
}
/**
* Execute the insert in a normal non batch fashion. Additionally using
* getGeneratedKeys if required.
* Execute non batched insert additionally using getGeneratedKeys if required.
*/
@Override
public int execute() throws SQLException, OptimisticLockException {
@@ -127,7 +126,7 @@ public class InsertHandler extends DmlHandler {
// get the auto-increment value back and set into the bean
getGeneratedKeys();
} else if (selectLastInsertedId != null) {
} else if (useSelectLastInsertedId) {
// fetch back the Id using a query
fetchGeneratedKeyUsingSelect();
}
@@ -167,12 +166,10 @@ public class InsertHandler extends DmlHandler {
*/
private void fetchGeneratedKeyUsingSelect() throws SQLException {
Connection conn = transaction.getConnection();
PreparedStatement stmt = null;
ResultSet rset = null;
try {
stmt = conn.prepareStatement(selectLastInsertedId);
stmt = transaction.getConnection().prepareStatement(persistRequest.getSelectLastInsertedId());
rset = stmt.executeQuery();
setGeneratedKey(rset);
} finally {
@@ -38,7 +38,7 @@ public final class InsertMeta {
/**
* Used for DB that do not support getGeneratedKeys.
*/
private final String selectLastInsertedId;
private final boolean supportsSelectLastInsertedId;
private final Bindable shadowFKey;
@@ -69,7 +69,7 @@ public final class InsertMeta {
this.sqlNullId = null;
this.sqlDraftNullId = null;
this.supportsGetGeneratedKeys = false;
this.selectLastInsertedId = null;
this.supportsSelectLastInsertedId = false;
} else {
// insert sql for db identity or sequence insert
@@ -77,11 +77,11 @@ public final class InsertMeta {
if (id.getIdentityColumn() == null) {
this.identityDbColumns = new String[]{};
this.supportsGetGeneratedKeys = false;
this.selectLastInsertedId = null;
this.supportsSelectLastInsertedId = false;
} else {
this.identityDbColumns = new String[]{id.getIdentityColumn()};
this.supportsGetGeneratedKeys = dbPlatform.getDbIdentity().isSupportsGetGeneratedKeys();
this.selectLastInsertedId = desc.getSelectLastInsertedId();
this.supportsSelectLastInsertedId = desc.supportsSelectLastInsertedId();
}
this.sqlNullId = genSql(true, tableName, false);
this.sqlDraftNullId = desc.isDraftable() ? genSql(true, draftTableName, true) : sqlNullId;
@@ -116,15 +116,11 @@ public final class InsertMeta {
}
/**
* Returns sql that is used to fetch back the last inserted id. This will
* return null if it should not be used.
* <p>
* This is only for DB's that do not support getGeneratedKeys. For MS
* SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id".
* </p>
* Return true if we should use a SQL query to return the generated key.
* This can not be used with JDBC batch mode.
*/
public String getSelectLastInsertedId() {
return selectLastInsertedId;
public boolean supportsSelectLastInsertedId() {
return supportsSelectLastInsertedId;
}
/**
@@ -138,11 +138,14 @@ class CQueryBuilder {
// wrap as - delete from table where id in (select id ...)
String sql = buildSql(null, request, predicates, sqlTree).getSql();
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
sql = aliasReplace(sql, alias);
sql = aliasReplace(sql, alias(rootTableAlias));
return sql;
}
private String alias(String rootTableAlias) {
return (rootTableAlias == null) ? "t0" : rootTableAlias;
}
private <T> String buildUpdateSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
StringBuilder sb = new StringBuilder(200);
@@ -160,8 +163,7 @@ class CQueryBuilder {
// wrap as - update table set ... where id in (select id ...)
String sql = buildSqlUpdate(null, request, predicates, sqlTree).getSql();
sql = updateClause + " " + request.getBeanDescriptor().getWhereIdInSql() + "in (" + sql + ")";
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
sql = aliasReplace(sql, alias);
sql = aliasReplace(sql, alias(rootTableAlias));
return sql;
}
@@ -209,7 +211,12 @@ class CQueryBuilder {
*/
<T> CQueryFetchSingleAttribute buildFetchIdsQuery(OrmQueryRequest<T> request) {
request.getQuery().setSelectId();
SpiQuery<T> query = request.getQuery();
query.setSelectId();
BeanDescriptor<T> desc = request.getBeanDescriptor();
if (!query.isIncludeSoftDeletes() && desc.isSoftDelete()) {
query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias())));
}
return buildFetchAttributeQuery(request);
}
@@ -217,7 +224,7 @@ class CQueryBuilder {
* Return the history support if this query needs it (is a 'as of' type query).
*/
<T> CQueryHistorySupport getHistorySupport(SpiQuery<T> query) {
return query.getTemporalMode() != SpiQuery.TemporalMode.CURRENT ? historySupport : null;
return query.getTemporalMode().isHistory() ? historySupport : null;
}
/**
@@ -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) {
@@ -768,6 +768,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
copy.timeout = timeout;
copy.mapKey = mapKey;
copy.id = id;
copy.label = label;
copy.nativeSql = nativeSql;
copy.useBeanCache = useBeanCache;
copy.useQueryCache = useQueryCache;
copy.readOnly = readOnly;
@@ -1049,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();
}
}
@@ -0,0 +1,103 @@
package io.ebean.config;
import io.ebean.EbeanServer;
import io.ebean.EbeanServerFactory;
import io.ebean.Transaction;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.h2.H2Platform;
import org.junit.Test;
import org.tests.model.basic.EBasicVer;
import org.tests.model.draftable.BasicDraftableBean;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class PlatformNoGeneratedKeysTest {
static EbeanServer server = testH2Server();
@Test
public void insertBatch_expect_noIdValuesFetched() {
EBasicVer b0 = new EBasicVer("a");
EBasicVer b1 = new EBasicVer("b");
EBasicVer b2 = new EBasicVer("c");
try (Transaction transaction = server.beginTransaction()) {
transaction.setBatchMode(true);
server.save(b0);
server.save(b1);
server.save(b2);
transaction.commit();
}
assertThat(b0.getId()).isNull();
assertThat(b1.getId()).isNull();
assertThat(b2.getId()).isNull();
}
@Test
public void insertNoBatch_expect_selectIdentity() {
EBasicVer b0 = new EBasicVer("one");
server.save(b0);
assertThat(b0.getId()).isNotNull();
BasicDraftableBean d0 = new BasicDraftableBean("done");
server.save(d0);
assertThat(d0.getId()).isNotNull();
server.publish(BasicDraftableBean.class, d0.getId());
BasicDraftableBean one = server.find(BasicDraftableBean.class, d0.getId());
assertThat(one.getName()).isEqualTo("done");
assertThat(one.isDraft()).isFalse();
}
private static EbeanServer testH2Server() {
ServerConfig config = new ServerConfig();
config.setName("h2_noGeneratedKeys");
OtherH2Platform platform = new OtherH2Platform();
DbIdentity dbIdentity = platform.getDbIdentity();
dbIdentity.setIdType(IdType.IDENTITY);
dbIdentity.setSupportsIdentity(true);
dbIdentity.setSupportsGetGeneratedKeys(false);
dbIdentity.setSupportsSequence(false);
dbIdentity.setSelectLastInsertedIdTemplate("select identity() --{table}");
config.setDatabasePlatform(platform);
config.getDataSourceConfig().setUsername("sa");
config.getDataSourceConfig().setPassword("");
config.getDataSourceConfig().setUrl("jdbc:h2:mem:withPCQuery;");
config.getDataSourceConfig().setDriver("org.h2.Driver");
config.setDisableL2Cache(true);
config.setDefaultServer(false);
config.setRegister(false);
config.setDdlGenerate(true);
config.setDdlRun(true);
config.getClasses().add(EBasicVer.class);
config.getClasses().add(BasicDraftableBean.class);
return EbeanServerFactory.create(config);
}
static class OtherH2Platform extends H2Platform {
OtherH2Platform() {
super();
this.platform = Platform.GENERIC;
}
}
}
@@ -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) {
@@ -2,7 +2,6 @@ package io.ebeaninternal.dbmigration;
import io.ebean.EbeanServer;
import io.ebean.EbeanServerFactory;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import org.junit.Test;
import org.slf4j.Logger;
@@ -13,6 +12,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.StrictAssertions.assertThatThrownBy;
@@ -71,8 +71,11 @@ public class DbMigrationDropHistoryTest {
assertThat(migration.generateMigration()).isNull(); // subsequent call
List<String> pendingDrops = migration.getPendingDrops();
assertThat(pendingDrops).contains("1.1");
System.setProperty("ddl.migration.pendingDropsFor", "1.1");
//System.setProperty("ddl.migration.pendingDropsFor", "1.1");
migration.setGeneratePendingDrop("1.1");
assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1");
assertThatThrownBy(()->migration.generateMigration())
.isInstanceOf(IllegalArgumentException.class)
@@ -72,9 +72,9 @@ public class BeanPropertyAssocManyTest extends BaseTestCase {
customerIds.add(1L);
customerIds.add(2L);
List<Object> contactIdsForOne = contacts().findIdsByParentId(1L, null, null, null);
List<Object> contactIdsForOne = contacts().findIdsByParentId(1L, null, null, null, true);
List<Object> contactIdsForMultiple = contacts().findIdsByParentId(null, customerIds, null, null);
List<Object> contactIdsForMultiple = contacts().findIdsByParentId(null, customerIds, null, null, true);
assertThat(contactIdsForOne).isNotEmpty();
assertThat(contactIdsForMultiple).isNotEmpty();
+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();
}
}
@@ -85,8 +85,8 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase {
ids.add(1L);
ids.add(2L);
beanProperty.findIdsByParentId(null, ids, null, null);
beanProperty.findIdsByParentId(1L, null, null, null);
beanProperty.findIdsByParentId(null, ids, null, null, true);
beanProperty.findIdsByParentId(1L, null, null, null, true);
}
@Entity
@@ -0,0 +1,32 @@
package org.tests.model.draftable;
import io.ebean.annotation.Draft;
import io.ebean.annotation.Draftable;
import javax.persistence.Entity;
@Entity
@Draftable
public class BasicDraftableBean extends BaseDomain {
private String name;
@Draft
boolean draft;
public BasicDraftableBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDraft() {
return draft;
}
}
@@ -0,0 +1,62 @@
package org.tests.model.history;
import io.ebean.annotation.History;
import io.ebean.annotation.SoftDelete;
import org.tests.model.draftable.BaseDomain;
import javax.persistence.Entity;
import javax.persistence.OneToOne;
@History
@Entity
public class HsdSetting extends BaseDomain {
String key;
String val;
@SoftDelete
boolean deleted;
@OneToOne
private HsdUser user;
public HsdSetting(String key) {
this.key = key;
}
public HsdSetting() {
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public HsdUser getUser() {
return user;
}
public void setUser(HsdUser user) {
this.user = user;
}
}
@@ -0,0 +1,54 @@
package org.tests.model.history;
import io.ebean.annotation.History;
import io.ebean.annotation.SoftDelete;
import org.tests.model.draftable.BaseDomain;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToOne;
@History
@Entity
public class HsdUser extends BaseDomain {
String name;
@SoftDelete
boolean deleted;
@OneToOne(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
HsdSetting setting;
public HsdUser(String name) {
this.name = name;
}
public HsdUser() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public HsdSetting getSetting() {
return setting;
}
public void setSetting(HsdSetting setting) {
this.setting = setting;
}
}
@@ -30,7 +30,7 @@ public class TestHistoryExclude extends BaseTestCase {
}
@Test
public void testSoftDelete() {
public void testSoftDelete_includeSoftDeletes_findList() {
HeLink l = new HeLink("two", "boo");
Ebean.save(l);
@@ -44,6 +44,23 @@ public class TestHistoryExclude extends BaseTestCase {
assertThat(list).isNotEmpty();
}
@Test
public void testSoftDelete_includeSoftDeletes_findOne() {
HeLink l = new HeLink("three", "boo2");
Ebean.save(l);
Ebean.delete(l);
HeLink found = Ebean.find(HeLink.class)
.setId(l.getId())
.setIncludeSoftDeletes()
.findOne();
assertThat(found).isNotNull();
assertThat(found.getName()).isEqualTo("three");
}
@Test
public void testLazyLoad() {
@@ -0,0 +1,27 @@
package org.tests.model.history;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestHistorySoftDeleteOneToOne extends BaseTestCase {
@Test
public void findOne() {
HsdUser u1 = new HsdUser("u1");
Ebean.save(u1);
Ebean.delete(u1);
HsdUser one = Ebean.find(HsdUser.class)
.setId(u1.getId())
.setIncludeSoftDeletes()
.findOne();
assertThat(one).isNotNull();
}
}
@@ -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() {
@@ -6,18 +6,83 @@ import io.ebean.Query;
import io.ebean.SqlQuery;
import io.ebean.SqlRow;
import io.ebean.Transaction;
import org.tests.model.softdelete.EBasicSDChild;
import org.tests.model.softdelete.EBasicSoftDelete;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.softdelete.EBasicSDChild;
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() {
EBasicSoftDelete bean = new EBasicSoftDelete();
bean.setName("softDelChildren");
bean.addChild("child1", 10);
bean.addChild("child2", 20);
bean.addChild("child3", 30);
Ebean.save(bean);
Ebean.delete(bean.getChildren().get(0));
LoggedSqlCollector.start();
List<Object> ids = Ebean.find(EBasicSDChild.class).where().eq("owner", bean).findIds();
assertThat(ids).hasSize(2);
List<EBasicSDChild> beans = Ebean.find(EBasicSDChild.class).where().eq("owner", bean).findList();
assertThat(beans).hasSize(2);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("from ebasic_sdchild t0 where t0.owner_id = ? and t0.deleted = false");
assertThat(sql.get(1)).contains("from ebasic_sdchild t0 where t0.owner_id = ? and t0.deleted = false");
}
@Test
public void test() {
@@ -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() {
+1 -1
View File
@@ -4,7 +4,7 @@
<ddl-script name="order views" platforms="h2" drop="true">
drop view order_agg_vw if exists;
</ddl-script>
<ddl-script name="order views" platforms="generic,db2,h2,postgres,oracle,mysql">
<ddl-script name="order views" platforms="db2,h2,postgres,oracle,mysql">
create or replace view order_agg_vw as
select d.order_id, sum(d.order_qty * d.unit_price) as order_total,