/*
* Copyright 2015 Samppa Saarela
*
* Licensed under the Apache License, Version 2.0 (the "License");x
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.javersion.store.jdbc;
import static com.querydsl.core.group.GroupBy.groupBy;
import static com.querydsl.core.types.Ops.EQ;
import static com.querydsl.core.types.Ops.GT;
import static com.querydsl.core.types.Ops.IS_NULL;
import static com.querydsl.core.types.dsl.Expressions.constant;
import static com.querydsl.core.types.dsl.Expressions.predicate;
import static java.util.Collections.singleton;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.javersion.core.Revision;
import org.javersion.object.ObjectVersion;
import org.javersion.util.Check;
import com.google.common.collect.ImmutableList;
import com.querydsl.core.ResultTransformer;
import com.querydsl.core.group.Group;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.sql.SQLQuery;
import com.querydsl.sql.dml.SQLUpdateClause;
public class EntityVersionStoreJdbc<Id extends Comparable, M, V extends JEntityVersion<Id>>
extends AbstractVersionStoreJdbc<Id, M, V, EntityUpdateBatch<Id, M, V>, EntityStoreOptions<Id, M, V>> {
protected final ResultTransformer<List<Group>> versionAndParentsSince;
/**
* No-args constructor for proxies
*/
@SuppressWarnings("unused")
protected EntityVersionStoreJdbc() {
super();
versionAndParentsSince = null;
}
public EntityVersionStoreJdbc(EntityStoreOptions<Id, M, V> options) {
super(options);
Expression<?>[] values = concat(versionAndParentColumns, options.sinceVersion.localOrdinal);
versionAndParentsSince = groupBy(options.version.revision).list(values);
}
@Override
protected FetchResults<Id, M> doFetch(Id docId, boolean optimized) {
Check.notNull(docId, "docId");
BooleanExpression predicate = versionsOf(docId);
List<Group> versionsAndParents = fetchVersionsAndParents(optimized, predicate,
options.version.localOrdinal.asc());
return fetch(versionsAndParents, optimized, predicate);
}
@Override
protected List<ObjectVersion<M>> doFetchUpdates(Id docId, Revision since) {
List<Group> versionsAndParents = versionsAndParentsSince(docId, since);
if (versionsAndParents.isEmpty()) {
return ImmutableList.of();
}
Long sinceOrdinal = versionsAndParents.get(0).getOne(options.sinceVersion.localOrdinal);
BooleanExpression predicate = versionsOf(docId)
.and(predicate(GT, options.version.localOrdinal, constant(sinceOrdinal)));
FetchResults<Id, M> results = fetch(versionsAndParents, false, predicate);
return results.containsKey(docId) ? results.getVersions(docId) : ImmutableList.of();
}
@Override
public EntityUpdateBatch<Id, M, V> updateBatch(Id id) {
return updateBatch(singleton(id));
}
@Override
public EntityUpdateBatch<Id, M, V> updateBatch(Collection<Id> docIds) {
return options.transactions.writeMandatory(() -> new EntityUpdateBatch<>(this, docIds));
}
@Override
protected SQLUpdateClause setOrdinal(SQLUpdateClause versionUpdateBatch, long ordinal) {
return versionUpdateBatch.set(options.version.ordinal, ordinal);
}
protected List<Group> versionsAndParentsSince(Id docId, Revision since) {
SQLQuery<?> qry = options.queryFactory.from(options.sinceVersion);
// Left join version version on version.ordinal > since.ordinal and version.doc_id = since.doc_id
qry.leftJoin(options.version).on(
options.version.localOrdinal.gt(options.sinceVersion.localOrdinal),
predicate(EQ, options.version.docId, options.sinceVersion.docId));
// Left join parents
qry.leftJoin(options.parent).on(options.parent.revision.eq(options.version.revision));
qry.where(options.sinceVersion.revision.eq(since),
versionsOf(docId).or(predicate(IS_NULL, options.version.docId)));
qry.orderBy(options.version.localOrdinal.asc());
return verifyVersionsAndParentsSince(qry.transform(versionAndParentsSince), since);
}
@Override
protected Map<Revision, Id> getUnpublishedRevisionsForUpdate() {
return options.queryFactory
.from(options.version)
.where(options.version.ordinal.isNull())
.orderBy(options.version.localOrdinal.asc(), options.version.revision.asc())
.forUpdate()
.transform(groupBy(options.version.revision).as(options.version.docId));
}
@Override
protected void lockForMaintenance(Id docId) {
options.queryFactory
.select(options.version.revision)
.from(options.version)
.where(versionsOf(docId))
.orderBy(options.version.localOrdinal.asc())
.forUpdate()
.iterate()
.close();
}
@Nonnull
private BooleanExpression versionsOf(Id docId) {
return predicate(EQ, options.version.docId, constant(docId));
}
}