tech.ydb.yoj.repository.db.Table Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yoj-repository Show documentation
Show all versions of yoj-repository Show documentation
Core YOJ (YDB ORM for Java) abstractions and APIs for domain entities, repositories, transactions etc.
package tech.ydb.yoj.repository.db;
import com.google.common.collect.Sets;
import lombok.NonNull;
import tech.ydb.yoj.databind.expression.FilterExpression;
import tech.ydb.yoj.databind.expression.OrderExpression;
import tech.ydb.yoj.repository.db.bulk.BulkParams;
import tech.ydb.yoj.repository.db.cache.FirstLevelCache;
import tech.ydb.yoj.repository.db.list.ListRequest;
import tech.ydb.yoj.repository.db.list.ListResult;
import tech.ydb.yoj.repository.db.list.ViewListResult;
import tech.ydb.yoj.repository.db.readtable.ReadTableParams;
import tech.ydb.yoj.repository.db.statement.Changeset;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
public interface Table> {
> Stream readTable(ReadTableParams params);
> Stream readTableIds(ReadTableParams params);
, ID extends Entity.Id> Stream readTable(Class viewClass, ReadTableParams params);
Class getType();
@CheckForNull
T find(Entity.Id id);
V find(Class viewType, Entity.Id id);
> List find(Range range);
> List findIds(Range range);
> List findIds(Set partialIds);
> List find(Class viewType, Range range);
> List find(Class viewType, Set ids);
List findAll();
List findAll(Class viewType);
List find(
@Nullable String indexName,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit,
@Nullable Long offset
);
> List findIds(
@Nullable String indexName,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit,
@Nullable Long offset
);
List find(
Class viewType,
@Nullable String indexName,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit,
@Nullable Long offset,
boolean distinct
);
> List find(
Set ids,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit
);
> List findUncached(
Set ids,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit
);
> List find(
Class viewType,
Set ids,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit
);
List find(
String indexName,
Set keys,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit
);
List find(
Class viewType,
String indexName,
Set keys,
@Nullable FilterExpression filter,
@Nullable OrderExpression orderBy,
@Nullable Integer limit
);
Stream streamAll(int batchSize);
> Stream streamAll(Class viewType, int batchSize);
> Stream streamPartial(ID partial, int batchSize);
, V extends ViewId> Stream streamPartial(Class viewType, ID partial, int batchSize);
> Stream streamAllIds(int batchSize);
> Stream streamPartialIds(ID partial, int batchSize);
long count(String indexName, FilterExpression filter);
long countAll();
// Unsafe
T insert(T t);
// Unsafe
T save(T t);
// Unsafe: may delete only entity, but not its projections, if entity was not loaded
void delete(Entity.Id id);
// Unsafe
void deleteAll();
default Stream readTable() {
return readTable(ReadTableParams.getDefault());
}
default > Stream readTableIds() {
return readTableIds(ReadTableParams.getDefault());
}
default FirstLevelCache getFirstLevelCache() {
return null;
}
@NonNull
default T find(Entity.Id id, Supplier extends X> throwIfAbsent) throws X {
T found = find(id);
if (found != null) {
return found;
} else {
throw throwIfAbsent.get();
}
}
default T findOrDefault(Entity.Id id, Supplier defaultSupplier) {
T found = find(id);
return found != null ? found : defaultSupplier.get();
}
default V find(Class viewType, Entity.Id id, Supplier extends X> throwIfAbsent) throws X {
V found = find(viewType, id);
if (found != null) {
return found;
} else {
throw throwIfAbsent.get();
}
}
default T modifyIfPresent(Entity.Id id, Function modify) {
return Optional.ofNullable(find(id))
.map(modify)
.map(this::save)
.orElse(null);
}
default T generateAndSaveNew(@NonNull Supplier generator) {
for (int i = 0; i < 7; i++) {
T t = generator.get();
if (find(t.getId()) == null) {
return save(t);
}
}
throw new IllegalStateException("Cannot generate unique entity id");
}
default T saveNewOrThrow(@NonNull T t, @NonNull Supplier extends X> alreadyExists) throws X {
if (find(t.getId()) != null) {
throw alreadyExists.get();
}
return save(t);
}
default T updateExistingOrThrow(@NonNull T t, @NonNull Supplier extends X> notFound) throws X {
if (find(t.getId()) == null) {
throw notFound.get();
}
return save(t);
}
default T saveOrUpdate(@NonNull T t) {
find(t.getId());
return save(t);
}
default T deleteIfExists(@NonNull Entity.Id id) {
T t = find(id);
if (t != null) {
delete(id);
}
return t;
}
default > void deleteAll(Set ids) {
find(ids);
ids.forEach(this::delete);
}
default > void deleteAll(Range range) {
find(range).forEach(e -> delete(e.getId()));
}
// Unsafe
@SuppressWarnings("unchecked")
default void insert(T first, T... rest) {
insertAll(concat(Stream.of(first), Stream.of(rest)).collect(toList()));
}
// Unsafe
default void insertAll(Collection extends T> entities) {
entities.forEach(this::insert);
}
// Unsafe
default > void delete(Set ids) {
ids.forEach(this::delete);
}
// Unsafe
default > void delete(Range range) {
findIds(range).forEach(this::delete);
}
default ListResult list(ListRequest request) {
List nextPage = toQueryBuilder(request).find();
return ListResult.forPage(request, postLoad(nextPage));
}
default ViewListResult list(Class viewType, ListRequest request) {
List nextPage = toQueryBuilder(request).find(viewType);
return ViewListResult.forPage(request, viewType, nextPage);
}
default > List find(Set ids) {
if (ids.isEmpty()) {
return List.of();
}
var orderBy = EntityExpressions.defaultOrder(getType());
var cache = getFirstLevelCache();
var isPartialIdMode = ids.iterator().next().isPartial();
var foundInCache = ids.stream()
.filter(cache::containsKey)
.map(cache::peek)
.flatMap(Optional::stream)
.collect(Collectors.toMap(Entity::getId, Function.identity()));
var remainingIds = Sets.difference(ids, foundInCache.keySet());
var foundInDb = findUncached(remainingIds, null, orderBy, null);
var merged = new HashMap, T>();
// some entries found in db with partial id query may already be in cache (after update/delete),
// so we must return actual entries from cache
for (var entry : foundInDb) {
var id = entry.getId();
if (cache.containsKey(id)) {
var cached = cache.peek(id);
cached.ifPresent(t -> merged.put(id, t));
// not present means marked as deleted in cache
} else {
merged.put(id, this.postLoad(entry));
}
}
// add entries found in cache and not fetched from db
for (var pair : foundInCache.entrySet()) {
var id = pair.getKey();
var entry = pair.getValue();
merged.put(id, entry);
}
if (!isPartialIdMode) {
Set> foundInDbIds = foundInDb.stream().map(Entity::getId).collect(toSet());
Set> foundInCacheIds = new HashSet<>(foundInCache.keySet());
Sets.difference(Sets.difference(ids, foundInDbIds), foundInCacheIds).forEach(cache::putEmpty);
}
return merged.values().stream().sorted(EntityIdSchema.SORT_ENTITY_BY_ID).collect(Collectors.toList());
}
default void bulkUpsert(List input, BulkParams params) {
throw new UnsupportedOperationException();
}
default TableQueryBuilder toQueryBuilder(ListRequest request) {
return query()
.index(request.getIndex())
.filter(request.getFilter())
.orderBy(request.getOrderBy())
.offset(request.getOffset())
.limit(request.getPageSize() + 1);
}
default List postLoad(List list) {
return list.stream().map(this::postLoad).collect(Collectors.toList());
}
default T postLoad(T e) {
return e.postLoad();
}
default long count(FilterExpression filter) {
return count(null, filter);
}
default TableQueryBuilder query() {
return new TableQueryBuilder<>(this);
}
@Deprecated
void update(Entity.Id id, Changeset changeset);
/**
* Base interface for views, that is, arbitrary subsets of {@link Entity} fields.
*
* @see ViewId
*/
interface View {
}
/**
* Base interface for ID-aware views, that is, subsets of {@link Entity} fields
* that contain the Entity's ID.
*
* @param entity type
* @see RecordViewId
*/
interface ViewId> extends View {
Entity.Id getId();
}
/**
* Base interface for ID-aware views that are implemented as {@link java.lang.Record Java Records}.
* Forwards {@link ViewId#getId() ViewId's getId() method} to the record's {@code id()} accessor,
* so you don't need to override the {@code getId()} method yourself.
*
* @param entity type
* @see ViewId
*/
interface RecordViewId> extends ViewId {
Entity.Id id();
@Override
default Entity.Id getId() {
return id();
}
}
}