|
Side of Software Persistence Library 2.0 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
public interface Database
A database of objects. This interface provides a way to add objects to a named database. When the client modifies these objects, the database is automatically updated. When the client no longer references these objects, they are automatically removed from the database.
This interface provides sophisticated database functionality through a simple, unobtrusive interface. It hides all of the details of a complex database management system. From a client's perspective, the database--or persistence layer--can be nearly transparent. Clients need not worry about database IDs or keys. There is no concept of a table or query, and there is no need for a mapping from an application class to a schema.
IllegalStateException
.Note: If the database system is a client-server system and this interface describes the client, then the operations
create
, open
,
close
, and delete
apply to
the client side.
Object addObject( Object object ) throws TransactionAbortedException boolean containsObject( Object object )The method
addObject
returns a proxy object.
The proxy object implements the same interfaces as the original object.
After calling addObject
, the client should
not retain any references to the original
object; it should reference the original object only through the returned proxy object.
Passing the proxy object to containsObject
returns true
.
Note: Clients do not need to invoke addObject
on
immutable database objects.
A consequence of using proxies is that
ordinary methods may throw TransactionAbortedException
as a result of
a database error or conflict. Even though this exception is a run-time exception,
it is intended to be caught.
create
.
Like all other database objects, it should be referenced only through its
proxy object. The root's proxy object is returned in getRoot
.The root object is typically a data structure, such as a map or index, and all database objects must be reachable (recursively) from this root.
Database
interface supports nested
transactions that satisfy the ACID requirements.
In other words, they exhibit:
When the client wishes to treat a group of operations as atomic,
it should invoke startTransaction
,
execute the operations, and then invoke commitTransaction
.
This chunk of code must be executed by the same application thread,
and it should catch and handle a TransactionAbortedException
. If
the client wishes to cancel a transaction programmatically, it should call
abortTransaction
.
Oftentimes, this transaction block of code would correspond to a synchronization
block if the client weren't using this library. The startTransaction
corresponds to monitor acquisition and the commitTransaction
corresponds to the monitor release. With transactions, however, there
is no need to lock database objects in the application code. The
database handles the concurrency (and deadlock avoidance)
automatically.
InMemoryDatabase
- Objects reside in main memory and do not persist across application runsFileDatabase
- Objects reside in a file and persist across application runsInMemoryDatabase
for FileDatabase
.
public class Application { private Database db; public Application() throws IOException { db = new ...; // some Database implementation like FileDatabase if( !db.exists() ) db.create( new HashMap() ); db.open( false ); } private Person createPerson() { Person person = new PersonImpl(); // some implementation of interface Person person = (Person)db.addObject( person ); // immediately add object to database return person; // return the proxy object } private void onCreatePerson() { try { db.startTransaction(); Person person = createPerson(); person.setFirstName( firstName ); person.setLastName( lastName ); db.commitTransaction(); } catch( TransactionAbortedException tae ) { Throwable cause = tae.getCause(); // inform the user that the operation failed // The state of the database is the same as before // the call to startTransaction. Specifically, // no new Person has been added. } } }Thread-safety. The methods
startTransaction
,
commitTransaction
, abortTransaction
,
addObject
, containsObject
,
and getRoot
are expected to be invoked by different threads.
No explicit synchronization is needed. The other methods, however, are not expected
to be executed by multiple threads; explicit synchronization is needed.
Transaction Policy. If possible, implementations
of this interface should delegate transactional
behavior, such as locking and deadlock avoidance, to an instance of
TransactionPolicy
.
When a top-level transaction is started, the database should
invoke startTransaction
on the underlying transaction policy.
Before a database accepts a new object (via addObject
), it should invoke
registerObject
on the transaction policy.
The database should let the transaction policy
carry out all method invocations on the actual object by invoking invoke
.
Last, before a database automatically deletes an object, it should
invoke unregisterObject
on the transaction policy.
Logging. Although it is not required, implementations of Database
should use Java's logging API to log database activity. It should log
activities in the "sos.db" logger, which, for convenience, is available as
the public static field logger
.
FileDatabase
,
InMemoryDatabase
Field Summary | |
---|---|
static java.util.logging.Logger |
logger
The object that should be used to log database activity. |
Method Summary | |
---|---|
void |
abortTransaction()
Aborts all changes under the calling thread's current transaction. |
java.lang.Object |
addObject(java.lang.Object object)
Adds the specified object to this database and returns a proxy object that the client should use instead. |
void |
close()
Closes this database. |
void |
commitTransaction(Progress progress)
Commits all changes under the calling thread's current transaction. |
boolean |
containsObject(java.lang.Object object)
Returns true if this database contains the specified object. |
boolean |
create(java.lang.Object root)
Creates a new database if and only if it does not exist. |
boolean |
delete()
Deletes this database. |
boolean |
exists()
Returns true if this database exists. |
java.lang.String |
getName()
Returns the name of this database. |
java.lang.Object |
getRoot()
Returns the root object of this database. |
TransactionPolicy |
getTransactionPolicy()
Returns the transaction policy governing the transactions. |
boolean |
isOpen()
Returns true if this database is open and ready to accept
requests. |
void |
open(boolean readOnly)
Opens this database. |
void |
startTransaction()
Starts a new (potentially nested) transaction for the calling thread. |
int |
transactionDepth()
Returns the nesting depth of the transaction associated with the current thread. |
Field Detail |
---|
static final java.util.logging.Logger logger
Method Detail |
---|
void abortTransaction()
java.lang.IllegalStateException
- if this database is not open
NoTransactionException
- if the calling thread does not have an
active transactioncommitTransaction(sos.db.Progress)
,
startTransaction()
,
isOpen()
java.lang.Object addObject(java.lang.Object object) throws TransactionAbortedException
The proxy object will implement
the same interfaces as object
. Specifically, the interfaces
returned by invoking getClass().getInterfaces()
on the proxy object will be (in order) the interfaces implemented
by the top-most superclass, the interfaces of the next lower superclass,
etc. down to the interfaces of the object's class. Also, an interface
will not be listed again if a superclass implements the same interface.
Any updates made to the proxy object will get propagated to the original object and the changes will automatically get saved in the database when the top-most transaction enclosing the updates commits.
The object will be automatically removed from this database after it becomes unreachable.
If there is no active transaction for the calling
thread, addObject
is atomic only with itself. That is, it is
as if startTransaction
is invoked immediately before the call to
addObject
and commitTransaction
immediately after.
object
- object to add to the database
object
java.lang.IllegalStateException
- if this database is not open or if this
database is open in read-only mode
java.lang.NullPointerException
- if object
is null
TransactionAbortedException
- if this database cannot add object
and the transaction has been aborted as a result. The cause of the abort
can be obtained with getCause
.
This exception is intended to be caught.containsObject(java.lang.Object)
,
isOpen()
,
startTransaction()
,
commitTransaction(sos.db.Progress)
void close() throws java.io.IOException
java.io.IOException
- if an I/O error occursvoid commitTransaction(Progress progress) throws TransactionAbortedException
Callers of this method have a chance to follow the method's progress
and to suggest an abort in the middle of committing. A custom
Progress
parameter can be passed in to update
a progress bar in the user interface or to respond to a cancel
button, for example. If the progress is canceled, this method
aborts with a TransactionAbortenull
dException.
progress
- an object to be updated and checked while the
commit is in progress (may be null
to indicate that no updating/checking
is necessary)
java.lang.IllegalStateException
- if this database is not open
NoTransactionException
- if the calling thread does not have an
active transaction
TransactionAbortedException
- if this database cannot commit
the transaction or if the progress is canceled. The cause of the abort
can be obtained with getCause
.
This exception is intended to be caught.startTransaction()
,
abortTransaction()
,
isOpen()
boolean containsObject(java.lang.Object object)
true
if this database contains the specified object.
object
must be a proxy object returned by
addObject
in order for the method to return true
.
If object
is null
, the method returns false
.
If this database is closed, then the method throws
an IllegalStateException
.
No transactions are used or created when checking for containment,
so TransactionAbortedException
is never thrown.
object
- the object to check for containment
true
if this database contains object
java.lang.IllegalStateException
- if this database is not openaddObject(java.lang.Object)
,
isOpen()
boolean create(java.lang.Object root) throws java.io.IOException
The root object should be an index-type object (like a map) that will hold references to objects added to this database. The primary way to retrieve database objects is through this root object.
After the invocation of this method, clients should no longer
reference root
. They should instead invoke getRoot
and reference the returned object.
root
- the root of this database
true
if this database does not exist and is successfully created;
false
if this database already exists
java.io.IOException
- if an I/O error occurs
java.lang.NullPointerException
- if root
is null
and this
database does not allow a null
rootgetRoot()
boolean delete() throws java.io.IOException
true
if this database is successfully deleted
java.lang.IllegalStateException
- if this database is open
java.io.IOException
- if an I/O error occursclose()
,
isOpen()
boolean exists()
true
if this database exists. To exist, a database normally
has to have been initialized with create
. Normally,
when a database exists, a call to open
will put it
in a state where it is ready to accept database operations
such as addObject
and getRoot
.
true
if this database existsopen(boolean)
java.lang.String getName()
java.lang.Object getRoot() throws TransactionAbortedException
This object may not be the same object provided in the original
invocation of create
; however, it will implement
the same interfaces and forward any requests to the original object.
null
)
java.lang.IllegalStateException
- if this database is not open
TransactionAbortedException
- if the root object cannot
be retrieved and the transaction has been aborted as a result.
The cause of the abort
can be obtained with getCause
.
This exception is intended to be caught.create(java.lang.Object)
,
isOpen()
TransactionPolicy getTransactionPolicy()
boolean isOpen()
true
if this database is open and ready to accept
requests.
true
if this database is openopen(boolean)
,
close()
void open(boolean readOnly) throws java.io.IOException
open
puts it into a state where it can accept database operations
such as addObject
and getRoot
.
If this database does not exists, this method throws
an IllegalStateException
.
If this database is already open, no change to the database occurs.
readOnly
- true
if database updates are not allowed
java.io.IOException
- if an I/O error occursexists()
void startTransaction() throws TransactionAbortedException
commitTransaction
or abortTransaction
by the same thread.
A call to startTransaction
by the same thread before the
transaction finishes starts a nested transaction with the original
transaction as its parent. Nested transactions are started
and stopped in a stack-like fashion.
All operations on database objects during a transaction are treated
as a single atomic operation with respect to this database. An
application should use transactions like it would the
synchronized
statement.
java.lang.IllegalStateException
- if this database is not open
TransactionAbortedException
- if the transaction cannot
be started. The cause of the exception
can be obtained with getCause
.
This exception is intended to be caught.abortTransaction()
,
commitTransaction(sos.db.Progress)
,
isOpen()
int transactionDepth()
java.lang.IllegalStateException
- if this database is not open
|
Side of Software Persistence Library 2.0 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |