Contents
See also
The DbControl
is the class with the main responsibility for
item handling. To it's help it has the HibernateUtil
which
collects most methods interfacing with Hibernate. You create a new DbControl
object by calling the SessionControl.newDbControl()
method.
A DbControl
object is not thread-safe and should not be used by multiple threads
at the same time.
The creation of a new item is initialised by a client application calling
the static AnyItem.getNew()
method on the item class. The call then
proceeds as follows (not actual code):
1. AnyItem.getNew(DbControl, optional parameters) 2. | DbControl.newItem(itemClass, dataClass) 3. | | data = new AnyData() 4. | | item = new AnyItem(data) 5. | | item.setDbControl(this) 6. | optionally initialise other properties, ie. call set methods 7. | return item to client application
Now the client application has a detached object. It will not
be saved to the database automatically. For this to happen the
client application must call the DbControl.saveItem()
method. The call then proceeds as follows:
1. DbControl.saveItem(item) 2. | check that the item is not already saved (ie, id == 0) 3. | item.setDbControl(this) 4. | item.initPermissions(0, 0) 5. | item.checkPermission(Permission.CREATE) 6. | commitQueue.add(item, Transactional.Action.CREATE)
The item has now been attached to the DbControl
and
added to the commit queue which means it will
be processed by the next call to DbControl.commit()
.
The item has not been saved to the database after this call. What
happens at commit time is outlined below.
The loading of an item is initialised by a client application
calling the static AnyItem.getById()
or some other method
on the item class. The call then proceeds as follows:
1. AnyItem.getById(DbControl, id)
2. | DbControl.loadItem(AnyItem.class, id)
3. | | load data object using Hibernate: data = HibernateUtil.loadItem(...)
4. | | DbControl.getItem(AnyItem.class, data)
5. | | | check item cache if an item object already exists
6. | | | if it does return that item, otherwise proceed
7. | | | item = new AnyItem(data)
8. | | | item.setDbControl(this)
9. | | | item.initPermissions(0, 0)
10.| | | item.checkPermission(Permission.READ)
11.| | | add the item the item cache
12.| | | add the item to the commit queue if it is Controlled
using:
| | | commitQueue.add(item, Transactional.Action.UPDATE)
13.| return item to client application
The item is now loaded and managed by the core. Any changes made to
it will automatically be saved to the database when DbControl.commit
is called. What happens at commit time is outlined below.
If you do not want changes to be saved it is possible to detach the item. See below.
It is possible to delete an existing item. Deletion is initialised
by a client application by calling the DbControl.deleteItem()
method. The call then proceeds as follows:
1. DbControl.deleteItem(item) 2. | check that the item exists in the database (id != 0) 3. | item.setDbControl(this) 4. | item.initPermissions(0, 0) 5. | item.checkPermission(Permission.DELETE) 6. | commitQueue.add(item, Transactional.Action.DELETE)
The item has now been added to the commit queue which means it will
be processed by the next call to DbControl.commit()
.
The item has not been deleted from the database after this call. What
happens at commit time is outlined below.
You must detach an item from the current session when you want to keep
it in memory while interacting with the user. Ie. load item from database,
detach it, display an edit item screen, reattach the item and commit.
Detaching an item means that changes made to it will not be saved to
the database unless it is reattached again. An item becomes detached when
a client application calls DbControl.detachItem()
or
when the DbControl
that loaded is closed. The call
to the DbControl.detachItem()
method proceeds as follows:
1. DbControl.detachItem(item) 2. | remove data object from Hibernate first-level cache: HibernateUtil.evictData(...) 3. | commitQueue.remove(item) 4. | itemCache.remove(item.getData()) 5. | item.setDbControl(null)
A detached item can later be reattached. This will also reenable automatic
saving of modification to the item. An item is re-attached when a client
application calls the DbControl.reattachItem(item)
method.
The call the proceeds as follows:
1. DbControl.reattachItem(item) 2. | check that the item exists in the database id != 0 3. | item.setDbControl(this) 4. | item.initPermissions(0, 0) 5. | if the logged in user has write permission attach the item to | the Hibernate session with update: HibernateUtil.updateData(...) 6. | else if the logged in user has read permission attach the item to | the Hibernate session with lock: HibernateUtil.lockData(...) 7. | else throw a PermissionDeniedException 8. | add the item to the item cache 9. | add the item to the commit queue if it is Controlled
A transaction is committed when the client application calls
DbControl.commit()
. A commit processes items in
the commit queue in the same order as they were entered. In the
queue we find the following types of items:
Validatable
items that needs case 2 data validation before
they are saved to the database
Transactional
items that needs their own transactional
processing
DiskConsumable
items that needs to check the logged in
user's quota before they are saved to the database
DbControl.commit()
proceeds as follows:
1. DbControl.commit() 2. | iterate the commit queue and... 3. | | ...call validate() on each Validatable item 4. | | ...call onBeforeCommit() on each Transactional item 5. | | ...call onBeforeCommit() on each new item and on items that should be deleted 6. | | ...call DbControl.updateDiskUsage() for each DiskConsumable item 7. | | ...save new items to the database: | | HibernateUtil.saveData(...) | | add to item cache | | call onAfterInsert() 8. | | ...delete items that should be deleted: | | call isUsed() to check if the item is used | | HibernateUtil.deleteData(...) | | remove the item from the item cache 9. | | ...clear the commit queue from items that are not Controlled 10.| HibernateUtil.commit(...) 11.| if successful iterate the commit queue and... 12.| | ...call onAfterCommit() on each Transactional item 13.| if there is an error call DbControl.rollback()
A transaction cannot be explicitly rollbacked by a client application.
A rollback only happens when a commit ends with an error or when
the DbControl.close()
method is called.
1. DbControl.close() 2. | HibernateUtil.rollback(...) 3. | HibernateUtil.close(...) 4. | iterate the commit queue and... 5. | ...call onRollback() on each Transactional item 6. | empty commit queue and item cache and get rid of all Hibernate objects
Sometimes you need to do a time-consuming operation while
keeping a DbControl
object in memory. For example,
a file upload may take several minutes. In those cases it is a good idea
to disconnect from the database to avoid hanging on to a database
connection that is not used. You do this by calling the DbControl.disconnect()
method. Under normal circumstances it is perfectly safe to use this method even if you
have made changes to some items. The changes are always kept in memory until
the commit()
method is called.
The only exception to this rule is if you use any of the batch classes, ie. reporters. For performance reasons changes to those items are flushed to the database at regular intervals. If the connections is dropped those changes will be lost.
1. DbControl.disconnect() 2. | HibernateUtil.disconnect(...)
Use the DbControl.reconnect()
method to reconnect to the
database again.
1. DbControl.reconnect() 2. | HibernateUtil.reconnect(...)
Normally everything is cleaned up when the DbControl.close()
method is
called, but we can never trust a client application to actually call it. Therfore
we have added code in the finalize()
method to call close.
But this may not be enough. Remember that it is the SessionControl
that creates the DbControl
and that it is the SessionControl
that contains all access permission information. If the SessionControl.logout()
method is called we get into strange situation, if we continue to use
the same DbControl
object. It can get even more strange if the
we login as another user.
To prevent this situation the SessionControl
contains a cache of all
DbControl
objects that has been created and when the logout method
is called it will also close all DbControl
:s that are still open.
The cache is using weak references in order to let the garbage collector do it's
work when the client application has released the reference. For the same reason
all other internal references to a DbControl
(ie. from BasicItem
and other objects are kept as weak references.