org.pipservices4.persistence.persistence.IdentifiableMemoryPersistence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pip-services4-persistence Show documentation
Show all versions of pip-services4-persistence Show documentation
Persistence components for Pip.Services in Java
The newest version!
package org.pipservices4.persistence.persistence;
import org.pipservices4.components.config.IConfigurable;
import org.pipservices4.commons.data.AnyValueMap;
import org.pipservices4.components.context.IContext;
import org.pipservices4.data.data.IIdentifiable;
import org.pipservices4.data.data.IStringIdentifiable;
import org.pipservices4.data.keys.IdGenerator;
import org.pipservices4.commons.errors.ApplicationException;
import org.pipservices4.commons.reflect.ObjectReader;
import org.pipservices4.commons.reflect.ObjectWriter;
import org.pipservices4.persistence.read.IGetter;
import org.pipservices4.persistence.read.ILoader;
import org.pipservices4.persistence.write.IPartialUpdater;
import org.pipservices4.persistence.write.ISaver;
import org.pipservices4.persistence.write.ISetter;
import org.pipservices4.persistence.write.IWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Abstract persistence component that stores data in memory
* and implements a number of CRUD operations over data items with unique ids.
* The data items must implement IIdentifiable interface.
*
* In basic scenarios child classes shall only override getPageByFilter()
,
* getListByFilter()
or deleteByFilter()
operations with specific filter function.
* All other operations can be used out of the box.
*
* In complex scenarios child classes can implement additional operations by
* accessing cached items via this._items
property and calling save()
method
* on updates.
*
* ### Configuration parameters ###
*
* - options:
*
* - max_page_size: Maximum number of items returned in a single page (default: 100)
*
*
*
* ### References ###
*
* - *:logger:*:*:1.0 (optional) ILogger components to pass log messages
*
*
* ### Examples ###
*
* {@code
* class MyMemoryPersistence extends IdentifiableMemoryPersistence {
*
* private Predicate composeFilter(FilterParams filter) {
* filter = filter != null ? filter : new FilterParams();
* String name = filter.getAsNullableString("name");
* return (item) -> {
* if (name != null && item.name != name)
* return false;
* return true;
* };
* }
*
* public DataPage getPageByFilter(IContext context, FilterParams filter, PagingParams paging) {
* super.getPageByFilter(context, this.composeFilter(filter), paging, null, null);
* }
*
* }
*
* MyMemoryPersistence persistence = new MyMemoryPersistence(MyData.class);
*
* MyData item = persistence.create("123", new MyData("1", "ABC"));
* DataPage mydata = persistence.getPageByFilter(
* "123",
* FilterParams.fromTuples("name", "ABC"),
* null, null, null);
* System.out.println(mydata.getData().toString()); // Result: { id: "1", name: "ABC" }
* persistence.deleteById("123", "1");
* ...
* }
*
*
* @see MemoryPersistence
*/
public class IdentifiableMemoryPersistence, K> extends MemoryPersistence
implements IConfigurable, IWriter, IGetter, ISetter, IPartialUpdater {
/**
* Creates a new instance of the persistence.
*
* @param type the class type
*/
protected IdentifiableMemoryPersistence(Class type) {
this(type, null, null);
}
// Pass the item type since Jackson cannot recognize type from generics
// This is related to Java type erasure issue
/**
* Creates a new instance of the persistence.
*
* @param type the class type
* @param loader (optional) a loader to load items from external datasource.
* @param saver (optional) a saver to save items to external datasource.
*/
protected IdentifiableMemoryPersistence(Class type, ILoader loader, ISaver saver) {
super(type, loader, saver);
}
/**
* Finds one element by id.
*
* @param id an id of data item.
* @return data item.
*/
protected T findOne(K id) {
Optional item = _items.stream().filter((v) -> v.getId().equals(id)).findFirst();
return item.orElse(null);
}
/**
* Finds all elements by ids.
*
* @param ids ids of data items.
* @return data list of items.
*/
protected List findAll(K[] ids) {
List result = new ArrayList<>();
for (K id : ids) {
Optional item = _items.stream().filter((v) -> v.getId().equals(id)).findAny();
result.add(item.orElse(null));
}
return result;
}
/**
* Gets a data item by its unique id.
*
* @param context (optional) a context to trace execution through call chain.
* @param id an id of data item to be retrieved.
* @return data item.
*/
public T getOneById(IContext context, K id) {
synchronized (_lock) {
T item = findOne(id);
if (item != null)
_logger.trace(context, "Retrieved %s by %s", item, id);
else
_logger.trace(context, "Cannot find %s by %s", _typeName, id);
return item;
}
}
/**
* Gets a list of data items retrieved by given unique ids.
*
* @param context (optional) a context to trace execution through call chain.
* @param ids ids of data items to be retrieved
* @return a data list.
*/
public List getListByIds(IContext context, K[] ids) {
List result = new ArrayList<>();
for (K oneId : ids) {
T item = getOneById(context, oneId);
if (item != null)
result.add(item);
}
return result;
}
/**
* Gets a list of data items retrieved by given unique ids.
*
* @param context (optional) a context to trace execution through call chain.
* @param ids ids of data items to be retrieved
* @return a data list.
*/
public List getListByIds(IContext context, List ids) {
List result = new ArrayList<>();
for (K oneId : ids) {
T item = getOneById(context, oneId);
if (item != null)
result.add(item);
}
return result;
}
/**
* Creates a data item.
*
* @param context (optional) a context to trace execution through call chain.
* @param item an item to be created.
* @return created item.
* @throws ApplicationException when error occured.
*/
@Override
public T create(IContext context, T item) throws ApplicationException {
// Assign unique string key
if (item instanceof IStringIdentifiable && item.getId() == null)
((IStringIdentifiable) item).setId(IdGenerator.nextLong());
synchronized (_lock) {
_items.add(item);
_logger.trace(context, "Created %s", item);
save(context);
}
return item;
}
/**
* Updates a data item.
*
* @param context (optional) a context to trace execution through call chain.
* @param newItem an item to be updated.
* @return updated item.
* @throws ApplicationException when error occured.
*/
public T update(IContext context, T newItem) throws ApplicationException {
synchronized (_lock) {
T oldItem = findOne(newItem.getId());
if (oldItem == null)
return null;
int index = _items.indexOf(oldItem);
if (index < 0)
return null;
_items.set(index, newItem);
_logger.trace(context, "Updated %s", newItem);
save(context);
return newItem;
}
}
/**
* Sets a data item. If the data item exists it updates it, otherwise it create
* a new data item.
*
* @param context (optional) a context to trace execution through call chain.
* @param newItem a item to be set.
* @return updated item.
* @throws ApplicationException when error occured.
*/
public T set(IContext context, T newItem) throws ApplicationException {
// Assign unique string key
if (newItem instanceof IStringIdentifiable && newItem.getId() == null)
((IStringIdentifiable) newItem).setId(IdGenerator.nextLong());
synchronized (_lock) {
T oldItem = findOne(newItem.getId());
if (oldItem == null)
_items.add(newItem);
else {
int index = _items.indexOf(oldItem);
if (index < 0)
_items.add(newItem);
else
_items.set(index, newItem);
}
_logger.trace(context, "Set %s", newItem);
save(context);
return newItem;
}
}
/**
* Deleted a data item by it's unique id.
*
* @param context (optional) a context to trace execution through call chain.
* @param id an id of the item to be deleted
* @return deleted item.
* @throws ApplicationException when error occured.
*/
@Override
public T deleteById(IContext context, K id) throws ApplicationException {
synchronized (_lock) {
T item = findOne(id);
if (item == null)
return null;
int index = _items.indexOf(item);
if (index < 0)
return null;
_items.remove(index);
_logger.trace(context, "Deleted %s", item);
save(context);
return item;
}
}
/**
* Deletes multiple data items by their unique ids.
*
* @param context (optional) a context to trace execution through call chain.
* @param ids ids of data items to be deleted.
* @throws ApplicationException when error occured.
*/
public void deleteByIds(IContext context, K[] ids) throws ApplicationException {
List idsList = Arrays.asList(ids);
int deleted = 0;
synchronized (_lock) {
Stream items = _items.stream();
items = items.filter(x -> idsList.contains(x.getId()));
List data = items.toList();
for (T item : data) {
_items.remove(item);
deleted++;
}
_logger.trace(context, "Deleted %d items", deleted);
if (deleted > 0) {
save(context);
}
}
}
/**
* Updates only few selected fields in a data item.
*
* @param context (optional) a context to trace execution through call chain.
* @param id an id of data item to be updated.
* @param data a map with fields to be updated.
* @return the updated data item.
*/
@Override
public T updatePartially(IContext context, K id, AnyValueMap data) throws ApplicationException {
synchronized (_lock) {
var index = this._items.stream().map(IIdentifiable::getId).collect(Collectors.toList()).indexOf(id);
if (index < 0) {
this._logger.trace(context, "Item %s was not found", id);
return null;
}
var item = this._items.get(index);
var properties = ObjectReader.getProperties(data.getAsObject());
ObjectWriter.setProperties(item, properties);
this._items.set(index, item);
this._logger.trace(context, "Partially updated item %s", id);
this.save(context);
return item;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy