Package ghidra.util.database

# Class DBAnnotatedObject

java.lang.Object
ghidra.program.database.DatabaseObject
ghidra.util.database.DBAnnotatedObject

Direct Known Subclasses:
  `AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry`, `AbstractDBTraceSymbol`, `DBTraceAddressPropertyManager.DBTraceAddressPropertyEntry`, `DBTraceCodeManager.DBTraceCodePrototypeEntry`, `DBTraceEquate`, `DBTraceGuestPlatform`, `DBTraceGuestPlatform.DBTraceGuestLanguage`, `DBTraceGuestPlatformMappedRange`, `DBTraceMemoryBufferEntry`, `DBTraceObject`, `DBTraceObjectManager.DBTraceObjectSchemaEntry`, `DBTraceOverlaySpaceAdapter.DBTraceOverlaySpaceEntry`, `DBTraceRegisterContextSpace.DBTraceRegisterEntry`, `DBTraceSnapshot`, `DBTraceStaticMapping`, `DBTraceSymbolManager.DBTraceVariableStorageEntry`, `DBTreeRecord`

public class DBAnnotatedObject extends DatabaseObject
An object backed by a `DBRecord`

Essentially, this is a data access object (DAO) for Ghidra's custom database engine. Not all object fields necessarily have a corresponding database field. Instead, those fields are annotated, and various methods are provided for updating the record, and conversely, re-loading fields from the record. These objects are managed using a `DBCachedObjectStore`. An example object definition:

     interface Person {
     	// ...
     }

     @DBAnnotatedObjectInfo(version = 1)
     public class DBPerson extends DBAnnotatedObject implements Person {
     	public static final String TABLE_NAME = "Person"; // Conventionally defined here

     	// Best practice is to define column names, then use in annotations
     	static final String NAME_COLUMN_NAME = "Name";
     	static final String ADDRESS_COLUMN_NAME = "Address";

     	// Column handles
     	@DBAnnotatedColumn(NAME_COLUMN_NAME)
     	static DBObjectColumn NAME_COLUMN;
     	@DBAnnotatedColumn(ADDRESS_COLUMN_NAME)
     	static DBObjectColumn ADDRESS_COLUMN;

     	// Column-backed fields
     	@DBAnnotatedField(column = NAME_COLUMN_NAME, indexed = true)
     	private String name;
     	@DBAnnotatedField(column = ADDRESS_COLUMN_NAME)
     	private String address;

     	DBPerson(DBCachedObjectStore<DBPerson> store, DBRecord record) {
     		super(store, record);
     	}

     	// Not required, but best practice
     	private void set(String name, String address) {
     		this.name = name;
     		this.address = address;
     		update(NAME_COLUMN, ADDRESS_COLUMN);
     	}

     	// ... other methods, getters, setters
     }

See `DBCachedObjectStoreFactory` for example code that uses the example `DBPerson` class.

All realizations of `DBAnnotatedObject` must be annotated with `DBAnnotatedObjectInfo`. This, along with the field annotations, are used to derive the table schema. Note the inclusion of a `TABLE_NAME` field. It is not required, nor is it used implicitly. It's included in this example as a manner of demonstrating best practice. When instantiating the object store, the field is used to provide the table name.

Next, we define the column names. These are not required nor used implicitly, but using literal strings in the column annotations is discouraged. Next, we declare variables to receive column handles. These are essentially the column numbers, but we have a named handle for each. They are initialized automatically the first time a store is created for this class.

Next we declare the variables representing the actual column values. Their initialization varies depending on how the object is instantiated. When creating a new object, the fields remain uninitialized. In some cases, it may be appropriate to provide an initial (default) value in the usual fashion, e.g., `private String address = "123 Pine St.";` In this case, the corresponding database field of the backing record is implicitly initialized upon creation. If the object is being loaded from a table, its fields are initialized with values from its backing record.

Next we define the constructor. There are no requirements on its signature, but it must call `super`, so it likely takes its containing store and its backing record. Having the same signature as its super constructor allows the store to be created using a simple method reference, e.g., `DBPerson::new`. Additional user-defined parameters may be accepted. To pass such parameters, a lambda is recommended when creating the object store.

Finally, we demonstrate how to update the record. The record is not implicitly updated by direct modification of an annotated field. All setters must call `update(DBObjectColumn...)` after updating a field. A common practice, especially when the object will have all its fields set at once, is to include a `set` method that initializes the fields and updates the record in one `update(DBObjectColumn...)`.

Note that there is no way to specify the primary key. For object stores, the primary key is always the object id, and its type is always `long`.

- ## Field Summary

  ### Fields inherited from class ghidra.program.database.DatabaseObject

  `key`

- ## Constructor Summary

  Constructors
  Modifier
  Constructor
  Description
  `protected`
  `DBAnnotatedObject(DBCachedObjectStore<?> store, DBRecord record)`
  The object constructor

- ## Method Summary

  Modifier and Type
  Method
  Description
  `protected boolean`
  `doRefresh(DBRecord rec)`
  `protected void`
  `doUpdateAll()`
  `protected void`
  `doUpdated()`
  `protected void`
  `doWrite(DBObjectColumn column)`
  `protected void`
  `fresh(boolean created)`
  Extension point: Called when the object's fields are populated.
  `ObjectKey`
  `getObjectKey()`
  Get an opaque unique id for this object, whose hash is immutable
  `String`
  `getTableName()`
  `boolean`
  `isDeleted()`
  Check if this object has been deleted
  `protected boolean`
  `refresh()`
  Tells the object to refresh its state from the database.
  `protected boolean`
  `refresh(DBRecord rec)`
  Tells the object to refresh its state from the database using the specified record if not null.
  `protected void`
  `update(DBObjectColumn column)`
  1-arity version of `update(DBObjectColumn...)`
  `protected void`
  `update(DBObjectColumn... columns)`
  Write the given columns into the record and update the table
  `protected void`
  `update(DBObjectColumn col1, DBObjectColumn col2)`
  2-arity version of `update(DBObjectColumn...)`
  `protected void`
  `update(DBObjectColumn col1, DBObjectColumn col2, DBObjectColumn col3)`
  3-arity version of `update(DBObjectColumn...)`

  ### Methods inherited from class ghidra.program.database.DatabaseObject

  `checkDeleted, checkIsValid, checkIsValid, getKey, isDeleted, isInvalid, keyChanged, setDeleted, setInvalid, validate`

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### DBAnnotatedObject

    protected DBAnnotatedObject(DBCachedObjectStore<?> store, DBRecord record)
    The object constructor
    Parameters:
      `store` - the store containing this object
      `record` - the record backing this object

- ## Method Details

  - ### getObjectKey

    public ObjectKey getObjectKey()
    Get an opaque unique id for this object, whose hash is immutable
    Returns:
      the opaque object id

  - ### doWrite

    protected void doWrite(DBObjectColumn column)

  - ### update

    protected void update(DBObjectColumn column)
    1-arity version of `update(DBObjectColumn...)`
    Parameters:
      `column` - the column

  - ### update

    protected void update(DBObjectColumn col1, DBObjectColumn col2)
    2-arity version of `update(DBObjectColumn...)`
    Parameters:
      `col1` - a column
      `col2` - another column

  - ### update

    protected void update(DBObjectColumn col1, DBObjectColumn col2, DBObjectColumn col3)
    3-arity version of `update(DBObjectColumn...)`
    Parameters:
      `col1` - a column
      `col2` - another column
      `col3` - another column

  - ### update

    protected void update(DBObjectColumn... columns)
    Write the given columns into the record and update the table
    Parameters:
      `columns` - the columns to update

  - ### doUpdateAll

    protected void doUpdateAll() throws IOException
    Throws:
      `IOException`

  - ### doUpdated

    protected void doUpdated() throws IOException
    Throws:
      `IOException`

  - ### fresh

    protected void fresh(boolean created) throws IOException
    Extension point: Called when the object's fields are populated.

    This provides an opportunity for the object to initialize any non-database-backed fields that depend on the database-backed fields. Note that its use may indicate a situation better solved by a custom `DBCachedObjectStoreFactory.DBFieldCodec`. If both the database-backed and non-database-backed fields are used frequently, then a codec may not be indicated. If the database-backed fields are only used in this method or to encode another frequently-used field, then a codec is likely better.

    For a new object, the database-backed fields remain at their initial values. They will be saved after this method returns, so they may be further initialized with custom logic.

    For an object loaded from the database, the database-backed fields are already populated from the record when this method is called. They are not automatically saved after this method returns. This method should not further initialize database-backed fields in this case.

    Parameters:
      `created` - `true` when object is being created, or `false` when it is being loaded.
    Throws:
      `IOException` - if further initialization fails.

  - ### refresh

    protected boolean refresh()
    Description copied from class: `DatabaseObject`
    Tells the object to refresh its state from the database.
    Specified by:
      `refresh` in class `DatabaseObject`
    Returns:
      true if the object was able to refresh itself. Return false if the object was deleted. Objects that extend this class must implement a refresh method. If an object can never refresh itself, then it should always return false.

  - ### refresh

    protected boolean refresh(DBRecord rec)
    Description copied from class: `DatabaseObject`
    Tells the object to refresh its state from the database using the specified record if not null. NOTE: The default implementation ignores the record and invokes refresh(). Implementations of this method must take care if multiple database tables are used since the record supplied could correspond to another object. In some cases it may be best not to override this method or ignore the record provided.
    Overrides:
      `refresh` in class `DatabaseObject`
    Parameters:
      `rec` - valid record associated with object's key (optional, may be null to force record lookup or other refresh technique)
    Returns:
      true if the object was able to refresh itself. Return false if record is null and object was deleted. Objects that extend this class must implement a refresh method. If an object can never refresh itself, then it should always return false.

  - ### doRefresh

    protected boolean doRefresh(DBRecord rec) throws IOException
    Throws:
      `IOException`

  - ### isDeleted

    public boolean isDeleted()
    Check if this object has been deleted
    Returns:
      true if deleted
    See Also:
      - `DatabaseObject.isDeleted(ghidra.util.Lock)`

  - ### getTableName

    public String getTableName()