Class AbstractDb<T extends DbData<?>>

java.lang.Object
com.renomad.minum.database.AbstractDb<T>
Type Parameters:
T - This is the type of data, which is always an implementation of the DbData class. See the code of Inmate for an example of how this should look.
Direct Known Subclasses:
Db, DbEngine2

public abstract class AbstractDb<T extends DbData<?>> extends Object
The abstract database class is a representation of the essential capabilities of a Minum database.

There are two kinds of database provided, which only differ in how they store data on disk. The "classic" kind, Db, stores each piece of data in its own file. This is the simplest approach.

However, for significant speed gains, the new DbEngine2 will store each change as an append to a file, and will consolidate the on-disk data occasionally, and on start. That way is thousands of times faster to write to disk and to read from disk at startup.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    protected final Context
    Holds some system-wide information that is beneficial for components of the database
    protected final Map<Long,T>
    The internal data structure of the database that resides in memory.
    protected final Path
    The directory of the database on disk
    protected final T
    An empty instance of the type of data stored by this database, used for better handling of generics.
    protected final FileUtils
    Used for handling some file utilities in the database like creating directories
    protected AtomicLong
    The current index, used when creating new data items.
    protected final ILogger
    Used for providing logging throughout the database
    protected final Map<String,Function<T,String>>
    This map holds the functions that are registered to indexes, which are used to construct the mappings between string values and items in the database.
    protected final Map<String,Map<String,Set<T>>>
    This data structure is a nested map used for providing indexed data search.
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    protected
    AbstractDb(Path dbDirectory, Context context, T instance)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected void
    addToIndexes(T dbData)
    add the data to registered indexes.
    abstract void
    delete(T dataToDelete)
    Delete data
    protected void
    deleteFromMemory(T dataToDelete)
    Remove a particular item from the internal data structure in memory
    findExactlyOne(String indexName, String indexKey)
    A utility to find exactly one item from the database.
    findExactlyOne(String indexName, String indexKey, Callable<T> alternate)
    Find one item, with an alternate value if null
    This utility will search the indexes for a particular data by indexName and indexKey.
    getIndexedData(String indexName, String key)
    Given the name of a registered index (see registerIndex(String, Function)), use the key to find the collection of data that matches it.
    Get a set of the currently-registered indexes on this database, useful for debugging.
    abstract void
    Grabs all the data from disk and returns it as a list.
    protected boolean
    When new data comes in, we look at its "index" value.
    boolean
    registerIndex(String indexName, Function<T,String> keyObtainingFunction)
    Register an index in the database for higher performance data access.
    abstract void
    Used to cleanly stop the database.
    abstract void
    stop(int count, int sleepTime)
    Used to cleanly stop the database, with extra allowance of time for cleanup.
    abstract Collection<T>
    This method provides read capability for the values of a database.
    abstract T
    write(T newData)
    Write data to the database.
    protected void
    writeToMemory(T newData, boolean newElementCreated)
    Write database data into memory

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • dbDirectory

      protected final Path dbDirectory
      The directory of the database on disk
    • emptyInstance

      protected final T extends DbData<?> emptyInstance
      An empty instance of the type of data stored by this database, used for better handling of generics.
    • fileUtils

      protected final FileUtils fileUtils
      Used for handling some file utilities in the database like creating directories
    • context

      protected final Context context
      Holds some system-wide information that is beneficial for components of the database
    • logger

      protected final ILogger logger
      Used for providing logging throughout the database
    • data

      protected final Map<Long,T extends DbData<?>> data
      The internal data structure of the database that resides in memory. The beating heart of the database while it runs.
    • index

      protected AtomicLong index
      The current index, used when creating new data items. Each item has its own index value, this is where it is tracked.
    • registeredIndexes

      protected final Map<String,Map<String,Set<T extends DbData<?>>>> registeredIndexes
      This data structure is a nested map used for providing indexed data search.
      The outer map is between the name of the index and the inner map.
      The inner map is between strings and sets of items related to that string.
    • partitioningMap

      protected final Map<String,Function<T extends DbData<?>,String>> partitioningMap
      This map holds the functions that are registered to indexes, which are used to construct the mappings between string values and items in the database.
  • Constructor Details

    • AbstractDb

      protected AbstractDb(Path dbDirectory, Context context, T instance)
  • Method Details

    • stop

      public abstract void stop()
      Used to cleanly stop the database.
      In the case of Db this will interrupt its internal queue and tell it to finish up processing.
      In the case of DbEngine2 this will flush data to disk.
    • stop

      public abstract void stop(int count, int sleepTime)
      Used to cleanly stop the database, with extra allowance of time for cleanup.
      Note that this method mostly applies to Db, and not as much to DbEngine2. Only Db uses a processing queue on a thread which is what requires a longer shutdown time for interruption.
      Parameters:
      count - number of loops before we are done waiting for a clean close and instead crash the instance closed.
      sleepTime - how long to wait, in milliseconds, for each iteration of the waiting loop.
    • write

      public abstract T write(T newData)
      Write data to the database. Use an index of 0 to store new data, and a positive non-zero value to update data.

      Example of adding new data to the database:

               final var newSalt = StringUtils.generateSecureRandomString(10);
               final var hashedPassword = CryptoUtils.createPasswordHash(newPassword, newSalt);
               final var newUser = new User(0L, newUsername, hashedPassword, newSalt);
               userDb.write(newUser);
      

      Example of updating data:

              // write the updated salted password to the database
              final var updatedUser = new User(
                      user().getIndex(),
                      user().getUsername(),
                      hashedPassword,
                      newSalt);
              userDb.write(updatedUser);
      
      Parameters:
      newData - the data we are writing
      Returns:
      the data with its new index assigned.
    • writeToMemory

      protected void writeToMemory(T newData, boolean newElementCreated)
      Write database data into memory
      Parameters:
      newData - the new data may be totally new or an update
      newElementCreated - if true, this is a create. If false, an update.
    • processDataIndex

      protected boolean processDataIndex(T newData)
      When new data comes in, we look at its "index" value. If it is zero, it's a create, and we assign it a new value. If it is positive, it is an update, and we had better find it in the database already, or else throw an exception.
      Returns:
      true if a create, false if an update
    • delete

      public abstract void delete(T dataToDelete)
      Delete data

      Example:

           userDb.delete(user);
      
      Parameters:
      dataToDelete - the data we are serializing and writing
    • deleteFromMemory

      protected void deleteFromMemory(T dataToDelete)
      Remove a particular item from the internal data structure in memory
    • addToIndexes

      protected void addToIndexes(T dbData)
      add the data to registered indexes.
      For each of the registered indexes, get the stored function to obtain a string value which helps divide the overall data into partitions.
    • loadData

      public abstract void loadData() throws IOException
      Grabs all the data from disk and returns it as a list. This method is run by various programs when the system first loads.
      Throws:
      IOException
    • values

      public abstract Collection<T> values()
      This method provides read capability for the values of a database.
      The returned collection is a read-only view over the data, through Collections.unmodifiableCollection(Collection)

      Example:

      boolean doesUserAlreadyExist(String username) {
          return userDb.values().stream().anyMatch(x -> x.getUsername().equals(username));
      }
      
    • registerIndex

      public boolean registerIndex(String indexName, Function<T,String> keyObtainingFunction)
      Register an index in the database for higher performance data access.

      This command should be run immediately after database declaration, or more specifically, before any data is loaded from disk. Otherwise, it would be possible to skip indexing that data.


      Example:
               final var myDatabase = context.getDb("photos", Photograph.EMPTY);
               myDatabase.registerIndex("url", photo -> photo.getUrl());
      
      Parameters:
      indexName - a string used to distinguish this index. This string will be used again when requesting data in a method like getIndexedData(java.lang.String, java.lang.String) or findExactlyOne(java.lang.String, java.lang.String)
      keyObtainingFunction - a function which obtains data from the data in this database, used to partition the data into groups (potentially up to a 1-to-1 correspondence between id and object)
      Returns:
      true if the registration succeeded
      Throws:
      DbException - if the parameters are not entered properly, if the index has already been registered, or if the data has already been loaded. It is necessary that this is run immediately after declaring the database. To explain further: the data is not actually loaded until the first time it is needed, such as running a write or delete, or if the loadData() ()} method is run. Creating an index map for the data that is read from disk only occurs once, at data load time. Thus, it is crucial that the registerIndex command is run before any data is loaded.
    • getIndexedData

      public Collection<T> getIndexedData(String indexName, String key)
      Given the name of a registered index (see registerIndex(String, Function)), use the key to find the collection of data that matches it.
      Parameters:
      indexName - the name of an index
      key - a string value that matches a partition calculated from the partition function provided to registerIndex(String, Function)
      Returns:
      a collection of data, an empty collection if nothing found
    • getSetOfIndexes

      public Set<String> getSetOfIndexes()
      Get a set of the currently-registered indexes on this database, useful for debugging.
    • findExactlyOne

      public T findExactlyOne(String indexName, String indexKey)
      A utility to find exactly one item from the database.
      This utility will search the indexes for a particular data by indexName and indexKey. If not found, it will return null. If found, it will be returned. If more than one are found, an exception will be thrown. Use this tool when the data has been uniquely indexed, like for example when setting a unique identifier into each data.
      Parameters:
      indexName - the name of the index, an arbitrary value set by the user to help distinguish among potentially many indexes set on this data
      indexKey - the key for this particular value, such as a UUID or a name or any other way to partition the data
      See Also:
    • findExactlyOne

      public T findExactlyOne(String indexName, String indexKey, Callable<T> alternate)
      Find one item, with an alternate value if null
      This utility will search the indexes for a particular data by indexName and indexKey. If not found, it will return null. If found, it will be returned. If more than one are found, an exception will be thrown. Use this tool when the data has been uniquely indexed, like for example when setting a unique identifier into each data.
      Parameters:
      indexName - the name of the index, an arbitrary value set by the user to help distinguish among potentially many indexes set on this data
      indexKey - the key for this particular value, such as a UUID or a name or any other way to partition the data
      alternate - a functional interface that will be run if the result would have been null, useful for situations where you don't want the output to be null when nothing is found.
      See Also: