Class Db<T extends DbData<?>>

java.lang.Object
com.renomad.minum.database.AbstractDb<T>
com.renomad.minum.database.Db<T>
Type Parameters:
T - the type of data we'll be persisting (must extend from DbData

public class Db<T extends DbData<?>> extends AbstractDb<T>
a memory-based disk-persisted database class.
  • Constructor Details

    • Db

      public Db(Path dbDirectory, Context context, T instance)
      Constructs an in-memory disk-persisted database. Loading of data from disk happens at the first invocation of any command changing or requesting data, such as write(DbData), delete(DbData), or values(). See the private method loadData() for details.
      Parameters:
      dbDirectory - this uniquely names your database, and also sets the directory name for this data. The expected use case is to name this after the data in question. For example, "users", or "accounts".
      context - used to provide important state data to several components
      instance - an instance of the DbData object relevant for use in this database. Note that each database (that is, each instance of this class), focuses on just one data, which must be an implementation of DbData.
  • Method Details

    • write

      public 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);
      
      Specified by:
      write in class AbstractDb<T extends DbData<?>>
      Parameters:
      newData - the data we are writing
      Returns:
      the data with its new index assigned.
    • delete

      public void delete(T dataToDelete)
      Delete data

      Example:

           userDb.delete(user);
      
      Specified by:
      delete in class AbstractDb<T extends DbData<?>>
      Parameters:
      dataToDelete - the data we are serializing and writing
    • values

      public Collection<T> values()
      Description copied from class: AbstractDb
      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));
      }
      
      Specified by:
      values in class AbstractDb<T extends DbData<?>>
    • loadData

      public void loadData()
      This is what loads the data from disk the first time someone needs it. Because it is locked, only one thread can enter at a time. The first one in will load the data, and the second will encounter a branch which skips loading.
      Specified by:
      loadData in class AbstractDb<T extends DbData<?>>
    • 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());
      
      Overrides:
      registerIndex in class AbstractDb<T extends DbData<?>>
      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 AbstractDb.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 loadDataFromDisk() 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.
      Overrides:
      getIndexedData in class AbstractDb<T extends DbData<?>>
      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
    • stop

      public void stop()
      This function will stop the minum.database persistence cleanly.

      In order to do this, we need to wait for our threads to finish their work. In particular, we have offloaded our file writes to [actionQueue], which has an internal thread for serializing all actions on our minum.database

      Specified by:
      stop in class AbstractDb<T extends DbData<?>>
    • stop

      public void stop(int count, int sleepTime)
      Similar to stop() but gives more control over how long we'll wait before crashing it closed. See ActionQueue.stop(int, int)
      Specified by:
      stop in class AbstractDb<T extends DbData<?>>
      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.