package marubinotto.piggydb.impl.query;
import static marubinotto.piggydb.impl.QueryUtils.appendLimit;
import static marubinotto.util.CollectionUtils.joinToString;
import static org.apache.commons.lang.ObjectUtils.defaultIfNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import marubinotto.piggydb.impl.H2FragmentRepository;
import marubinotto.piggydb.impl.QueryUtils;
import marubinotto.piggydb.impl.mapper.FragmentRowMapper;
import marubinotto.piggydb.model.Fragment;
import marubinotto.piggydb.model.base.Repository;
import marubinotto.piggydb.model.entity.RawFragment;
import marubinotto.piggydb.model.query.FragmentsByIds;
import marubinotto.piggydb.model.query.FragmentsQuery;
import marubinotto.piggydb.model.query.FragmentsSortOption;
import marubinotto.util.Assert;
import marubinotto.util.CollectionUtils;
import marubinotto.util.paging.Page;
import marubinotto.util.paging.PageUtils;
import org.springframework.jdbc.core.JdbcTemplate;
public abstract class H2FragmentsQueryBase implements FragmentsQuery {
private H2FragmentRepository repository;
public void setRepository(Repository<Fragment> repository) {
Assert.Arg.notNull(repository, "repository");
this.repository = (H2FragmentRepository)repository;
}
public H2FragmentRepository getRepository() {
return this.repository;
}
public JdbcTemplate getJdbcTemplate() {
return getRepository().getJdbcTemplate();
}
public FragmentRowMapper getRowMapper() {
return getRepository().getFragmentRowMapper();
}
// ----- Sort option
private FragmentsSortOption sortOption = new FragmentsSortOption();
public void setSortOption(FragmentsSortOption sortOption) {
Assert.Arg.notNull(sortOption, "sortOption");
this.sortOption = sortOption;
}
public FragmentsSortOption getSortOption() {
return this.sortOption;
}
// ----- Eager fetching
private boolean eagerFetching = false;
private boolean eagerFetchingMore = false;
public void setEagerFetching(boolean eagerFetching) {
this.eagerFetching = eagerFetching;
}
public void setEagerFetchingMore(boolean eagerFetchingMore) {
this.eagerFetchingMore = eagerFetchingMore;
}
public boolean isEagerFetching() {
return this.eagerFetching;
}
public boolean isEagerFetchingMore() {
return this.eagerFetchingMore;
}
private void eagerFetch(List<RawFragment> fragments) throws Exception {
getRepository().fetchRelations(fragments, this.eagerFetching, this.eagerFetchingMore);
}
// ----- Main logic
private StringBuilder sql;
private List<Object> sqlArgs;
private String fromWhere;
private void buildSelectFromWhere() throws Exception {
this.sql = new StringBuilder();
this.sqlArgs = new ArrayList<Object>();
appendSelect(this.sql);
StringBuilder fromWhere = new StringBuilder();
appendFromWhere(fromWhere, this.sqlArgs);
this.fromWhere = fromWhere.toString();
this.sql.append(" " + this.fromWhere);
}
public List<Fragment> getAll() throws Exception {
buildSelectFromWhere();
appendSortOption(this.sql, getRowMapper().getColumnPrefix());
List<RawFragment> results = getRepository().query(this.sql.toString(), this.sqlArgs.toArray());
eagerFetch(results);
return CollectionUtils.<Fragment>covariantCast(results);
}
public Page<Fragment> getPage(int pageSize, int pageIndex) throws Exception {
buildSelectFromWhere();
appendSortOption(this.sql, getRowMapper().getColumnPrefix());
appendLimit(this.sql, pageSize, pageIndex);
List<RawFragment> results = getRepository().query(this.sql.toString(), this.sqlArgs.toArray());
eagerFetch(results);
return PageUtils.<Fragment>covariantCast(
PageUtils.toPage(results, pageSize, pageIndex, getTotalCounter()));
}
protected void appendSelect(StringBuilder sql) {
sql.append("select ");
sql.append(getRowMapper().selectAll());
// append a field to sort by a string field with case ignored
if (this.sortOption.orderBy.isString() && !this.sortOption.shuffle) {
sql.append(", ");
sql.append(ignoreCaseForSort(
this.sortOption.orderBy.getName(), getRowMapper().getColumnPrefix()));
}
}
protected abstract void appendFromWhere(StringBuilder sql, List<Object> args)
throws Exception;
protected void appendSortOption(StringBuilder sql, String columnPrefix) {
sql.append(" order by ");
// shuffle
if (this.sortOption.shuffle) {
sql.append("rand()");
return;
}
// sort field: [default] the column name with the prefix
// [string column] the alias name for upper-cased value in the select
if (this.sortOption.orderBy.isString())
sql.append("ns_" + this.sortOption.orderBy.getName());
else
sql.append(defaultIfNull(columnPrefix, "") + this.sortOption.orderBy.getName());
// asc/desc
if (this.sortOption.ascending)
sql.append(" nulls last");
else
sql.append(" desc nulls first");
}
protected static String ignoreCaseForSort(String columnName, String prefix) {
return "UPPER(" + prefix + columnName + ") as ns_" + columnName;
}
protected PageUtils.TotalCounter getTotalCounter() {
final String countSql = "select count(*) " + this.fromWhere;
final Object[] args = this.sqlArgs.toArray();
return new PageUtils.TotalCounter() {
public long getTotalSize() throws Exception {
return (Long)getJdbcTemplate().queryForObject(countSql, args, Long.class);
}
};
}
// ----- Utilities
protected FragmentsQuery getDelegateeQuery(Class<? extends FragmentsQuery> queryType)
throws Exception {
FragmentsQuery query = (FragmentsQuery)getRepository().getQuery(queryType);
query.setSortOption(getSortOption());
query.setEagerFetching(isEagerFetching());
query.setEagerFetchingMore(isEagerFetchingMore());
return query;
}
protected List<Fragment> getByIds(Collection<Long> fragmentIds) throws Exception {
Assert.Arg.notNull(fragmentIds, "fragmentIds");
FragmentsByIds query = (FragmentsByIds)getDelegateeQuery(FragmentsByIds.class);
query.setIds(fragmentIds);
return query.getAll();
}
protected void appendSqlToSelectFragmentIdsTaggedWithAnyOf(
StringBuilder sql, Set<Long> tagIds, boolean sort) {
sql.append("select distinct f.fragment_id");
if (sort) appendFieldForSort(sql, "f.");
sql.append(" from fragment as f, tagging as t");
sql.append(" where f.fragment_id = t.target_id");
sql.append(" and t.target_type = " + QueryUtils.TAGGING_TARGET_FRAGMENT);
sql.append(" and t.tag_id in (");
sql.append(joinToString(tagIds, ", "));
sql.append(")");
}
protected void appendFieldForSort(StringBuilder sql, String prefix) {
if (getSortOption().orderBy.isString())
sql.append(", " + ignoreCaseForSort(getSortOption().orderBy.getName(), prefix));
else
sql.append(", " + prefix + getSortOption().orderBy.getName());
}
}