mirror of
https://github.com/yasuhirokimura/db18
synced 2026-06-08 18:29:23 +00:00
371 lines
16 KiB
HTML
371 lines
16 KiB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||
<title>Dbstl persistence</title>
|
||
<link rel="stylesheet" href="gettingStarted.css" type="text/css" />
|
||
<meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
|
||
<link rel="start" href="index.html" title="Berkeley DB Programmer's Reference Guide" />
|
||
<link rel="up" href="stl.html" title="Chapter 7. Standard Template Library API" />
|
||
<link rel="prev" href="stl_complex_rw.html" title="Store and Retrieve data or objects of complex types" />
|
||
<link rel="next" href="stl_container_specific.html" title="Dbstl container specific notes" />
|
||
</head>
|
||
<body>
|
||
<div xmlns="" class="navheader">
|
||
<div class="libver">
|
||
<p>Library Version 18.1.40</p>
|
||
</div>
|
||
<table width="100%" summary="Navigation header">
|
||
<tr>
|
||
<th colspan="3" align="center">Dbstl persistence</th>
|
||
</tr>
|
||
<tr>
|
||
<td width="20%" align="left"><a accesskey="p" href="stl_complex_rw.html">Prev</a> </td>
|
||
<th width="60%" align="center">Chapter 7. Standard Template Library API</th>
|
||
<td width="20%" align="right"> <a accesskey="n" href="stl_container_specific.html">Next</a></td>
|
||
</tr>
|
||
</table>
|
||
<hr />
|
||
</div>
|
||
<div class="sect1" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h2 class="title" style="clear: both"><a id="stl_persistence"></a>Dbstl persistence</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="toc">
|
||
<dl>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="stl_persistence.html#directdbget">Direct database get</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="stl_persistence.html#chg_persistence">Change persistence</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="stl_persistence.html#obj_life_persistence">Object life time and persistence </a>
|
||
</span>
|
||
</dt>
|
||
</dl>
|
||
</div>
|
||
<p>
|
||
The following sections provide information on how to
|
||
achieve persistence using dbstl.
|
||
</p>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="directdbget"></a>Direct database get</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Each container has a <span class="bold"><strong>begin()</strong></span> method
|
||
which produces an iterator.
|
||
These <span class="bold"><strong>begin</strong></span> methods take
|
||
a boolean parameter, <span class="bold"><strong>directdb_get</strong></span>, which
|
||
controls the caching behavior of the iterator. The default value of this
|
||
parameter is <code class="literal">true</code>.
|
||
</p>
|
||
<p>
|
||
If <span class="bold"><strong>directdb_get</strong></span> is
|
||
<code class="literal">true</code>, then the persistent object is
|
||
fetched anew from the database each time the iterator is
|
||
dereferenced as a pointer by use of the star-operator
|
||
(<span class="bold"><strong>*iterator</strong></span>) or by use
|
||
of the arrow-operator (<span class="bold"><strong>iterator->member</strong></span>).
|
||
If <span class="bold"><strong>directdb_get</strong></span> is
|
||
<code class="literal">false</code>, then the first dereferencing
|
||
of the iterator fetches the object from the database, but
|
||
later dereferences can return cached data.
|
||
</p>
|
||
<p>
|
||
With <span class="bold"><strong>directdb_get</strong></span> set
|
||
to <code class="literal">true</code>, if you call:
|
||
</p>
|
||
<pre class="programlisting">(*iterator).datamember1=new-value1;
|
||
(*iterator).datamember2=new-value2; </pre>
|
||
<p>
|
||
then the assignment to <code class="literal">datamember1</code>
|
||
will be lost, because the second dereferencing of the
|
||
iterator would cause the cached copy of the object to be
|
||
overwritten by the object's persistent data from the
|
||
database.
|
||
</p>
|
||
<p>
|
||
You also can use the arrow operator like this:
|
||
</p>
|
||
<pre class="programlisting">iterator->datamember1=new-value1;
|
||
iterator->datamember2=new-value2; </pre>
|
||
<p>
|
||
This works exactly the same way as <span class="bold"><strong>iterator::operator*</strong></span>. For this
|
||
reason, the same caching rules apply to arrow operators as
|
||
they do for star operators.
|
||
</p>
|
||
<p>
|
||
One way to avoid this problem is to create a reference
|
||
to the object, and use it to access the object:
|
||
</p>
|
||
<pre class="programlisting">container::value_type &ref = *iterator;
|
||
ref.datamember1=new-value1;
|
||
ref.datamember2=new-value2;
|
||
...// more member function calls and datamember assignments
|
||
ref._DB_STL_StoreElement(); </pre>
|
||
<p>
|
||
The above code will not lose the newly assigned value
|
||
of <code class="literal">ref.datamember1</code> in the way that the
|
||
previous example did.
|
||
</p>
|
||
<p>
|
||
In order to avoid these complications, you can assign
|
||
to the object referenced by an iterator with another
|
||
object of the same type like this:
|
||
</p>
|
||
<pre class="programlisting">container::value_type obj2;
|
||
obj2.datamember1 = new-value1;
|
||
obj2.datamember2 = new-value2;
|
||
*itr = obj2; </pre>
|
||
<p>
|
||
This code snippet causes the new values in
|
||
<code class="literal">obj2</code> to be stored into the
|
||
underlying database.
|
||
</p>
|
||
<p>
|
||
If you have two iterators going through the same
|
||
container like this:
|
||
</p>
|
||
<pre class="programlisting">for (iterator1 = v.begin(), iterator2 = v.begin();
|
||
iterator1 != v.end();
|
||
++iterator1, ++iterator2) {
|
||
*iterator1 = new_value;
|
||
print(*iterator2);
|
||
} </pre>
|
||
<p>
|
||
then the printed value will depend on the value of
|
||
<span class="bold"><strong>directdb_get</strong></span> with
|
||
which the iterator had been created. If <span class="bold"><strong>directdb_get</strong></span> is
|
||
<code class="literal">false</code>, then the original,
|
||
persistent value is printed; otherwise the newly assigned
|
||
value is returned from the cache when
|
||
<code class="literal">iterator2</code> is dereferenced. This
|
||
happens because each iterator has its own cached copy of
|
||
the persistent object, and the dereferencing of
|
||
<code class="literal">iterator2</code> refreshes
|
||
<code class="literal">iterator2</code>'s copy from the database,
|
||
retrieving the value stored by the assignment to
|
||
<code class="literal">*iterator1</code>.
|
||
</p>
|
||
<p>
|
||
Alternatively, you can set <span class="bold"><strong>directdb_get </strong></span> to
|
||
<code class="literal">false</code> and call <code class="methodname">iterator2->refresh()</code>
|
||
immediately before the dereferencing of
|
||
<code class="literal">iterator2</code>, so that
|
||
<code class="literal">iterator2</code>'s cached value is
|
||
refreshed.
|
||
</p>
|
||
<p>
|
||
If <span class="bold"><strong>directdb_get</strong></span> is
|
||
<code class="literal">false</code>, a few of the tests in
|
||
dbstl's test kit will fail. This is because the above
|
||
contrived case appears in several of C++ STL tests.
|
||
Consequently, the default value of the <span class="bold"><strong>directdb_get</strong></span> parameter in the
|
||
<code class="methodname">container::begin()</code> methods is
|
||
<code class="literal">true</code>. If your use cases avoid such
|
||
bizarre usage of iterators, you can set it to
|
||
<code class="literal">false</code>, which makes the iterator
|
||
read operation faster.
|
||
</p>
|
||
</div>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="chg_persistence"></a>Change persistence</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
If you modify the object to which an iterator refers by
|
||
using one of the following:
|
||
</p>
|
||
<pre class="programlisting">(*iterator).member_function_call()</pre>
|
||
<p>
|
||
or
|
||
</p>
|
||
<pre class="programlisting">(*iterator).data_member = new_value</pre>
|
||
<p>
|
||
then you should call
|
||
<code class="methodname">iterator->_DB_STL_StoreElement()</code>
|
||
to store the change. Otherwise the change is lost after
|
||
the iterator moves on to other elements.
|
||
</p>
|
||
<p>
|
||
If you are storing a sequence, and you modified some
|
||
part of it, you should also call
|
||
<code class="methodname">iterator->_DB_STL_StoreElement()</code>
|
||
before moving the iterator.
|
||
</p>
|
||
<p>
|
||
And in both cases, if <span class="bold"><strong>directdb_get</strong></span> is
|
||
<code class="literal">true</code>
|
||
(this is the default value), you should call
|
||
<code class="methodname">_DB_STL_StoreElement()</code> after
|
||
the change and before the next iterator movement OR the
|
||
next dereferencing of the iterator by the star or arrow
|
||
operators (<code class="literal">iterator::operator*</code> or
|
||
<code class="literal">iterator::operator-></code>).
|
||
Otherwise, you will lose the change.
|
||
</p>
|
||
<p>
|
||
If you update the element by assigning to a
|
||
dereferenced iterator like this:
|
||
</p>
|
||
<pre class="programlisting">*iterator = new_element;</pre>
|
||
<p>
|
||
then you never have to call
|
||
<code class="methodname">_DB_STL_StoreElement()</code>
|
||
because the change is stored in the database
|
||
automatically.
|
||
</p>
|
||
</div>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="obj_life_persistence"></a>Object life time and persistence </h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Dbstl is an interface to Berkeley DB, so it is used to
|
||
store data persistently. This is really a different
|
||
purpose from that of regular C++ STL. This difference in
|
||
their goals has implications on expected object lifetime:
|
||
In standard STL, when you store an object A of type ID
|
||
into C++ stl vector V using V.push_back(A), if a proper
|
||
copy constructor is provided in A's class type, then the
|
||
copy of A (call it B) and everything in B, such as another
|
||
object C pointed to by B's data member B.c_ptr, will be
|
||
stored in V and will live as long as B is still in V and V
|
||
is alive. B will be destroyed when V is destroyed or B is
|
||
erased from V.
|
||
</p>
|
||
<p>
|
||
This is not true for dbstl, which will copy A's data
|
||
and store it in the underlying database. The copy is by
|
||
default a shallow copy, but users can register their
|
||
object marshalling and unmarshalling functions using the
|
||
<code class="classname">DbstlElemTraits</code> class template.
|
||
So if A is passed to a <code class="classname">db_vector</code>
|
||
container, <code class="literal">dv</code>, by using
|
||
<code class="literal">dv.push_back(A)</code>, then dbstl copies
|
||
A's data using the registered functions, and stores data
|
||
into the underlying database. Consequently, A will be
|
||
valid, even if the container is destroyed, because it is
|
||
stored into the database.
|
||
</p>
|
||
<p>
|
||
If the copy is simply a shallow copy, and A is later
|
||
destroyed, then the pointer stored in the database will
|
||
become invalid. The next time we use the retrieved object,
|
||
we will be using an invalid pointer, which probably will
|
||
result in errors. To avoid this, store the referred object
|
||
C rather than the pointer member A.c_ptr itself, by
|
||
registering the right marshalling/unmarshalling function
|
||
with <code class="classname">DbstlElemTraits</code>.
|
||
</p>
|
||
<p>
|
||
For example, consider the following example class
|
||
declaration:
|
||
</p>
|
||
<pre class="programlisting">class ID
|
||
{
|
||
public:
|
||
string Name;
|
||
int Score;
|
||
}; </pre>
|
||
<p>
|
||
Here, the class ID has a data member <span class="bold"><strong>Name</strong></span>, which refers to a memory
|
||
address of the actual characters in the string. If we
|
||
simply shallow copy an object, <code class="literal">id</code>, of
|
||
class ID to store it, then the stored data,
|
||
<code class="literal">idd</code>, is invalid when
|
||
<code class="literal">id</code> is destroyed. This is because
|
||
<code class="literal">idd</code> and <code class="literal">id</code> refer
|
||
to a common memory address which is the base address of
|
||
the memory space storing all characters in the string, and
|
||
this memory space is released when <code class="literal">id</code>
|
||
is destroyed. So <code class="literal">idd</code> will be referring
|
||
to an invalid address. The next time we retrieve
|
||
<code class="literal">idd</code> and use it, there will probably
|
||
be memory corruption.
|
||
</p>
|
||
<p>
|
||
The way to store <code class="literal">id</code> is to write a
|
||
marshal/unmarshal function pair like this:
|
||
</p>
|
||
<pre class="programlisting">void copy_id(void *dest, const ID&elem)
|
||
{
|
||
memcpy(dest, &elem.Score, sizeof(elem.Score));
|
||
char *p = ((char *)dest) + sizeof(elem.Score);
|
||
strcpy(p, elem.Name.c_str());
|
||
}
|
||
|
||
void restore_id(ID& dest, const void *srcdata)
|
||
{
|
||
memcpy(&dest.Score, srcdata, sizeof(dest.Score));
|
||
const char *p = ((char *)srcdata) + sizeof(dest.Score);
|
||
dest.Name = p;
|
||
}
|
||
|
||
size_t size_id(const ID& elem)
|
||
{
|
||
return sizeof(elem.Score) + elem.Name.size() +
|
||
1;// store the '\0' char.
|
||
} </pre>
|
||
<p>
|
||
Then register the above functions before storing any
|
||
instance of <code class="classname">ID</code>:
|
||
</p>
|
||
<pre class="programlisting">DbstlElemTraits<ID>::instance()->set_copy_function(copy_id);
|
||
DbstlElemTraits<ID>::instance()->set_size_function(size_id);
|
||
DbstlElemTraits<ID>::instance()->set_restore_function(restore_id); </pre>
|
||
<p>
|
||
This way, the actual data of instances of ID are
|
||
stored, and so the data will persist even if the container
|
||
itself is destroyed.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="navfooter">
|
||
<hr />
|
||
<table width="100%" summary="Navigation footer">
|
||
<tr>
|
||
<td width="40%" align="left"><a accesskey="p" href="stl_complex_rw.html">Prev</a> </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="u" href="stl.html">Up</a>
|
||
</td>
|
||
<td width="40%" align="right"> <a accesskey="n" href="stl_container_specific.html">Next</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td width="40%" align="left" valign="top">Store and Retrieve data or
|
||
objects of complex types </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="h" href="index.html">Home</a>
|
||
</td>
|
||
<td width="40%" align="right" valign="top"> Dbstl container specific
|
||
notes</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</body>
|
||
</html>
|