Java tutorial
/* * Copyright 2015 Samppa Saarela * * Licensed under the Apache License, Version 2.0 (the "License"); * 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 java.util.Collection; import java.util.List; import java.util.Map; import org.javersion.core.Revision; import org.javersion.core.VersionNode; import org.javersion.object.ObjectVersion; import org.javersion.path.PropertyPath; import org.javersion.util.Check; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; 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 DocumentVersionStoreJdbc<Id, M, V extends JDocumentVersion<Id>> extends AbstractVersionStoreJdbc<Id, M, V, DocumentUpdateBatch<Id, M, V>, DocumentStoreOptions<Id, M, V>> { protected final Expression<?>[] versionAndParentsSince; /** * No-args constructor for proxies */ @SuppressWarnings("unused") public DocumentVersionStoreJdbc() { super(); versionAndParentsSince = null; } public DocumentVersionStoreJdbc(DocumentStoreOptions<Id, M, V> options) { super(options); versionAndParentsSince = concat(versionAndParentColumns, options.sinceVersion.ordinal); } public void append(Id docId, VersionNode<PropertyPath, Object, M> version) { options.transactions.writeRequired(() -> { doAppend(ImmutableMultimap.of(docId, version)); return null; }); } public void append(Id docId, Iterable<VersionNode<PropertyPath, Object, M>> versions) { options.transactions.writeRequired(() -> { ImmutableMultimap.Builder<Id, VersionNode<PropertyPath, Object, M>> builder = ImmutableMultimap .builder(); doAppend(builder.putAll(docId, versions).build()); return null; }); } public void append(Multimap<Id, VersionNode<PropertyPath, Object, M>> versionsByDocId) { options.transactions.writeRequired(() -> { doAppend(versionsByDocId); return null; }); } @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.ordinal.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.ordinal); BooleanExpression predicate = versionsOf(docId) .and(predicate(GT, options.version.ordinal, constant(sinceOrdinal))); FetchResults<Id, M> results = fetch(versionsAndParents, false, predicate); return results.containsKey(docId) ? results.getVersions(docId) : ImmutableList.of(); } @Override public DocumentUpdateBatch<Id, M, V> updateBatch(Collection<Id> ids) { return new DocumentUpdateBatch<>(this); } protected void doAppend(Multimap<Id, VersionNode<PropertyPath, Object, M>> versionsByDocId) { DocumentUpdateBatch<Id, M, V> batch = updateBatch(versionsByDocId.keys()); for (Id docId : versionsByDocId.keySet()) { for (VersionNode<PropertyPath, Object, M> version : versionsByDocId.get(docId)) { batch.addVersion(docId, version); } } batch.execute(); } protected BooleanExpression versionsOf(Id docId) { return predicate(EQ, options.version.docId, constant(docId)).and(options.version.ordinal.isNotNull()); } 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.ordinal.gt(options.sinceVersion.ordinal), 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), // Return "since" row even if there is no newer versions versionsOf(docId).or(predicate(IS_NULL, options.version.docId))); qry.orderBy(options.version.ordinal.asc()); return verifyVersionsAndParentsSince( qry.transform(groupBy(options.version.revision).list(versionAndParentsSince)), since); } @Override protected SQLUpdateClause setOrdinal(SQLUpdateClause versionUpdateBatch, long ordinal) { return versionUpdateBatch.set(options.version.ordinal, ordinal).setNull(options.version.txOrdinal); } @Override protected Map<Revision, Id> getUnpublishedRevisionsForUpdate() { return options.queryFactory.from(options.version).where(options.version.txOrdinal.isNotNull()) .orderBy(options.version.txOrdinal.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.ordinal.asc()).forUpdate().iterate().close(); } }