Class Persistence

java.lang.Object
atessera.sensei.util.Persistence
All Implemented Interfaces:
AutoCloseable

public final class Persistence extends Object implements AutoCloseable
Provides persistent storage for application data using H2 MVStore as an embedded file-based key-value database.

This class manages student records and chat message history, storing them in a single MVStore file on disk. All mutating operations that affect student data are synchronized to prevent concurrent access issues when multiple threads operate on the same persistence instance.

Student storage: Students are stored in a map named "students" with their normalized email address (lowercased and trimmed) as the key and a JSON representation as the value. The email address serves as the unique identifier for all operations.

Chat memory: Chat message history is stored in a map named "chat" via chatMemoryStore(), which provides a ChatMemoryStore implementation backed by this persistence.

See Also:
  • Constructor Details

    • Persistence

      public Persistence(String path)
      Constructs a new persistence instance with the given file path. Creates parent directories if they do not exist.
      Parameters:
      path - the path to the MVStore database file
      Throws:
      NullPointerException - if path is null
    • Persistence

      public Persistence(Path path)
      Constructs a new persistence instance with the given file path. Creates parent directories if they do not exist.
      Parameters:
      path - the path to the MVStore database file
      Throws:
      NullPointerException - if path is null
  • Method Details

    • addStudent

      public boolean addStudent(Student student)
      Adds a new student to the persistence.

      The student's email address (normalized to lower case and trimmed) is used as the unique key. If a student with the same normalized email already exists, this method returns false without modifying the existing record.

      Parameters:
      student - the student to add; must not be null, and must have non-null and non-blank name, email, and assignedLabWorks
      Returns:
      true if the student was added successfully, false if a student with the same email already exists
      Throws:
      NullPointerException - if student, student.name, student.mail, or student.assignedLabWorks is null
      IllegalArgumentException - if student.name or student.mail is blank
    • updateStudent

      public boolean updateStudent(Student student, Predicate<Student> modifier)
      Atomically updates an existing student identified by email.

      The student argument serves only to identify the record (by email). The modifier predicate receives the current student object loaded from the database and may mutate its fields in place. If the predicate returns true, the modified student is persisted and the changes are committed. If the predicate returns false, no changes are saved.

      If the predicate throws an exception, the changes are not committed, leaving the stored record in its original state (atomicity guarantee).

      This method is synchronized to ensure thread safety.

      Parameters:
      student - the student object used to identify the record (by email); must not be null and must have a non-blank email
      modifier - a predicate that receives the current student, may mutate it, and returns true to persist changes
      Returns:
      true if the student was found and the predicate returned true, false if the student was not found or the predicate returned false
      Throws:
      NullPointerException - if student, student.mail, or modifier is null
      IllegalArgumentException - if student.mail is blank
      See Also:
    • deleteStudent

      public boolean deleteStudent(Student student, Predicate<Student> condition)
      Atomically deletes an existing student identified by email.

      The student argument serves to identify the record (by email). The condition predicate receives the current student object loaded from the database and may inspect its state. If the predicate returns true, the student is deleted from the store and the change is committed. If the predicate returns false, the student is kept.

      This pattern allows conditional deletion — for example, only deleting a student if they are suspended:

      
       Student id = new Student();
       id.setMail("john@example.com");
       persistence.deleteStudent(id, s -> s.isSuspended());
       

      If the predicate throws an exception, the deletion is not committed and the student remains in the store (atomicity guarantee).

      This method is synchronized to ensure thread safety.

      Parameters:
      student - the student object used to identify the record (by email); must not be null and must have a non-blank email
      condition - a predicate that receives the current student for inspection and returns true to proceed with deletion
      Returns:
      true if the student was found and deleted, false if the student was not found or the predicate returned false
      Throws:
      NullPointerException - if student, student.mail, or condition is null
      IllegalArgumentException - if student.mail is blank
      See Also:
    • getStudents

      public List<Student> getStudents()
      Returns all students currently stored in the persistence.

      The returned list is a snapshot of the current state. Changes to the list do not affect the underlying store.

      Returns:
      an unmodifiable list of all students; never null, may be empty if no students are stored
    • chatMemoryStore

      public dev.langchain4j.store.memory.chat.ChatMemoryStore chatMemoryStore()
      Creates a ChatMemoryStore backed by this persistence.

      Chat messages are stored in an internal map named "chat". This allows retaining conversation history across application restarts for LLM-based agents.

      Returns:
      a chat memory store backed by this persistence
    • commit

      public void commit()
      Commits all pending changes to the underlying store.

      Normally changes are committed automatically by mutating methods (addStudent(Student), updateStudent(Student, Predicate), deleteStudent(Student, Predicate)). This method is provided for cases where additional explicit commit is needed.

    • close

      public void close()
      Closes the underlying MVStore database, releasing all resources.

      This method is idempotent — calling it multiple times has no additional effect after the first call.

      Specified by:
      close in interface AutoCloseable