Compare commits

...
Author SHA1 Message Date
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
18 changed files with 368 additions and 34 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.22.6</version>
<version>11.22.8</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.8</tag>
</scm>
<profiles>
@@ -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";
@@ -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));
}
@@ -754,7 +754,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);
}
@@ -1052,7 +1052,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);
}
@@ -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
@@ -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();
@@ -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,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();
}
}
@@ -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,11 +6,10 @@ 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;
@@ -18,6 +17,33 @@ import static org.assertj.core.api.Assertions.assertThat;
public class TestSoftDeleteBasic extends BaseTestCase {
@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() {