| 1 |
lars |
1 |
<com:TContent ID="body">
|
|
|
2 |
<!-- $Id $ -->
|
|
|
3 |
|
|
|
4 |
<h1 id="140062">Data Mapper</h1>
|
|
|
5 |
<com:SinceVersion Version="3.1a" />
|
|
|
6 |
<p id="700505" class="block-content">Data Mappers moves data between objects and a database while keeping them
|
|
|
7 |
independent of each other and the mapper itself. If you started with
|
|
|
8 |
<a href="?page=Database.ActiveRecord">Active Records</a>, you may eventually
|
|
|
9 |
faced with more complex business
|
|
|
10 |
objects as your project progresses. When you build an object model with a
|
|
|
11 |
lot of business logic it's valuable to use these mechanisms to better organize
|
|
|
12 |
the data and the behavior that goes with it. Doing so leads to variant schemas;
|
|
|
13 |
that is, the object schema and the relational schema don't match up.
|
|
|
14 |
</p>
|
|
|
15 |
|
|
|
16 |
<p id="700506" class="block-content">The Data Mapper separates the in-memory objects from the database. Its responsibility
|
|
|
17 |
is to transfer data between the two and also to isolate them from each other.
|
|
|
18 |
With Data Mapper the in-memory objects needn't know even that there's a database
|
|
|
19 |
present; they need no SQL interface code, and certainly no knowledge of the
|
|
|
20 |
database schema. (The database schema is always ignorant of the objects that use it.)
|
|
|
21 |
</p>
|
|
|
22 |
|
|
|
23 |
<h2 id="140063">When to Use It</h2>
|
|
|
24 |
<p id="700507" class="block-content">The primary occasion for using Data Mapper is when you want the database schema
|
|
|
25 |
and the object model to evolve independently. Data Mapper's primary benefit is
|
|
|
26 |
that when working on the business (or domain) objects you can ignore the database, both in
|
|
|
27 |
design and in the build and testing process. The domain objects have no idea
|
|
|
28 |
what the database structure is, because all the correspondence is done by the mappers.
|
|
|
29 |
</p>
|
|
|
30 |
|
|
|
31 |
<p id="700508" class="block-content">This helps you in the code because you can understand and work with the domain objects
|
|
|
32 |
without having to understand how they're stored in the database. You can modify the
|
|
|
33 |
business models or the database without having to alter either. With complicated
|
|
|
34 |
mappings, particularly those involving <b>existing databases</b>, this is very valuable.
|
|
|
35 |
</p>
|
|
|
36 |
|
|
|
37 |
<p id="700509" class="block-content">The price, of course, is the extra layer that you don't get with
|
|
|
38 |
<a href="?page=Database.ActiveRecord">Active Record</a>,
|
|
|
39 |
so the test for using these patterns is the complexity of the business logic.
|
|
|
40 |
If you have fairly simple business logic, an <a href="?page=Database.ActiveRecord">Active Record</a>
|
|
|
41 |
will probably work.
|
|
|
42 |
For more complicated logic a Data Mapper may be more suitable.
|
|
|
43 |
</p>
|
|
|
44 |
|
|
|
45 |
<h2 id="140064">SqlMap Data Mapper</h2>
|
|
|
46 |
<p id="700510" class="block-content">The SqlMap DataMapper framework makes it easier to use a database with a PHP application.
|
|
|
47 |
SqlMap DataMapper couples objects with stored procedures or SQL statements using
|
|
|
48 |
a XML descriptor. Simplicity is the biggest advantage of the SqlMap DataMapper over
|
|
|
49 |
object relational mapping tools. To use SqlMap DataMapper you rely on your own objects,
|
|
|
50 |
XML, and SQL. There is little to learn that you don't already know.
|
|
|
51 |
With SqlMap DataMapper you have the full power of both SQL and stored procedures at
|
|
|
52 |
your fingertip
|
|
|
53 |
</p>
|
|
|
54 |
|
|
|
55 |
<p id="700511" class="block-content">
|
|
|
56 |
<img src=<%~ diagram.png %> alt="SqlMap Data Mapper Overview" id="fig:sqlmap.png" class="figure"/>
|
|
|
57 |
|
|
|
58 |
Here's a high level description of the work flow illustrated in the figure above.
|
|
|
59 |
Provide a parameter, either as an object or a primitive type. The parameter can be
|
|
|
60 |
used to set runtime values in your SQL statement or stored procedure. If a runtime value
|
|
|
61 |
is not needed, the parameter can be omitted.
|
|
|
62 |
</p>
|
|
|
63 |
<p id="700512" class="block-content">Execute the mapping by passing the parameter and the name you gave the statement or
|
|
|
64 |
procedure in your XML descriptor. This step is where the magic happens. The framework
|
|
|
65 |
will prepare the SQL statement or stored procedure, set any runtime values using your
|
|
|
66 |
parameter, execute the procedure or statement, and return the result.
|
|
|
67 |
</p>
|
|
|
68 |
|
|
|
69 |
<p id="700513" class="block-content">In the case of an update, the number of rows affected is returned. In the case of a
|
|
|
70 |
query, a single object, or a collection of objects is returned. Like the parameter,
|
|
|
71 |
the result object, or collection of objects, can be a plain-old object or a primitive PHP type.
|
|
|
72 |
</p>
|
|
|
73 |
|
|
|
74 |
<h2 id="140065">Setting up a database connection and initializing the SqlMap</h2>
|
|
|
75 |
<p id="700514" class="block-content">
|
|
|
76 |
A database connection for SqlMap can be set as follows.
|
|
|
77 |
See <a href="?page=Database.DAO">Establishing Database Connection</a> for
|
|
|
78 |
futher details regarding creation of database connection in general.
|
|
|
79 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700166">
|
|
|
80 |
//create a connection and give it to the SqlMap manager.
|
|
|
81 |
$dsn = 'pgsql:host=localhost;dbname=test'; //Postgres SQL
|
|
|
82 |
$conn = new TDbConnection($dsn, 'dbuser','dbpass');
|
|
|
83 |
$manager = new TSqlMapManager($conn);
|
|
|
84 |
$manager->configureXml('my-sqlmap.xml');
|
|
|
85 |
$sqlmap = $manager->getSqlMapGateway();
|
|
|
86 |
</com:TTextHighlighter>
|
|
|
87 |
</p>
|
|
|
88 |
|
|
|
89 |
<p id="700515" class="block-content">
|
|
|
90 |
The <tt>TSqlMapManager</tt> is responsible for setting up the database connection
|
|
|
91 |
and configuring the SqlMap with given XML file(s). The <tt>configureXml()</tt>
|
|
|
92 |
method accepts a string that points to a SqlMap XML configuration file. Once
|
|
|
93 |
configured, call the <tt>getSqlMapGateway()</tt> method to obtain an instance
|
|
|
94 |
of the SqlMap gateway interface (use this object to insert/delete/find records).
|
|
|
95 |
</p>
|
|
|
96 |
|
|
|
97 |
<p id="700516" class="block-content">
|
|
|
98 |
SqlMap database connection can also be configured using a <tt><module></tt>
|
|
|
99 |
tag in the <a href="?page=Configurations.AppConfig">application.xml</a>
|
|
|
100 |
or <a href="?page=Configurations.PageConfig">config.xml</a> as follows.
|
|
|
101 |
<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_700167">
|
|
|
102 |
<modules>
|
|
|
103 |
<module id="my-sqlmap" class="System.Data.SqlMap.TSqlMapConfig"
|
|
|
104 |
EnableCache="true" ConfigFile="my-sqlmap.xml" >
|
|
|
105 |
<database ConnectionString="pgsql:host=localhost;dbname=test"
|
|
|
106 |
Username="dbuser" Password="dbpass" />
|
|
|
107 |
</module>
|
|
|
108 |
</modules>
|
|
|
109 |
</com:TTextHighlighter>
|
|
|
110 |
</p>
|
|
|
111 |
|
|
|
112 |
<p id="700517" class="block-content">
|
|
|
113 |
The <tt>ConfigFile</tt> attribute should point to a SqlMap configuration file
|
|
|
114 |
(to be detailed later) either using absolute path, relative path or the
|
|
|
115 |
Prado's namespace dot notation path (must omit the ".xml" extension).
|
|
|
116 |
|
|
|
117 |
<div class="tip"><b class="note">Tip:</b>
|
|
|
118 |
The <tt>EnableCache</tt> attribute when set to "true" will cache the
|
|
|
119 |
parsed configuration. You must clear or disable the cache if you
|
|
|
120 |
make changes to your configuration file.
|
|
|
121 |
A <a href="?page=Advanced.Performance#6402">cache
|
|
|
122 |
module</a> must also be defined for the cache to function.
|
|
|
123 |
</div>
|
|
|
124 |
</p>
|
|
|
125 |
|
|
|
126 |
<p id="700518" class="block-content">To obtain the SqlMap gateway interface from the <module> configuration, simply
|
|
|
127 |
do, for example,
|
|
|
128 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700168">
|
|
|
129 |
class MyPage extends TPage
|
|
|
130 |
{
|
|
|
131 |
public function onLoad($param)
|
|
|
132 |
{
|
|
|
133 |
parent::onLoad($param);
|
|
|
134 |
$sqlmap = $this->Application->Modules['my-sqlmap']->Client;
|
|
|
135 |
$sqlmap->queryForObject(...); //query for some object
|
|
|
136 |
}
|
|
|
137 |
}
|
|
|
138 |
</com:TTextHighlighter>
|
|
|
139 |
</p>
|
|
|
140 |
|
|
|
141 |
<h2 id="140066">A quick example</h2>
|
|
|
142 |
<p id="700519" class="block-content">Let us
|
|
|
143 |
consider the following "users" table that contains two columns named "username" and "email",
|
|
|
144 |
where "username" is also the primary key.
|
|
|
145 |
<com:TTextHighlighter Language="sql" CssClass="source block-content" id="code_700169">
|
|
|
146 |
CREATE TABLE users
|
|
|
147 |
(
|
|
|
148 |
username VARCHAR( 20 ) NOT NULL ,
|
|
|
149 |
email VARCHAR( 200 ) ,
|
|
|
150 |
PRIMARY KEY ( username )
|
|
|
151 |
);
|
|
|
152 |
</com:TTextHighlighter>
|
|
|
153 |
</p>
|
|
|
154 |
<p id="700520" class="block-content">Next we define our plain <tt>User</tt> class as follows. Notice that
|
|
|
155 |
the <tt>User</tt> is very simple.
|
|
|
156 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700170">
|
|
|
157 |
class User
|
|
|
158 |
{
|
|
|
159 |
public $username;
|
|
|
160 |
public $email;
|
|
|
161 |
}
|
|
|
162 |
</com:TTextHighlighter>
|
|
|
163 |
</p>
|
|
|
164 |
</p>
|
|
|
165 |
|
|
|
166 |
<p id="700521" class="block-content">Next, we need to define a SqlMap XMl configuration file, lets name
|
|
|
167 |
the file as <tt>my-sqlmap.xml</tt>
|
|
|
168 |
<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_700171">
|
|
|
169 |
<?xml version="1.0" encoding="utf-8" ?>
|
|
|
170 |
<sqlMapConfig>
|
|
|
171 |
<select id="SelectUsers" resultClass="User">
|
|
|
172 |
SELECT username, email FROM users
|
|
|
173 |
</select>
|
|
|
174 |
</sqlMapConfig>
|
|
|
175 |
</com:TTextHighlighter>
|
|
|
176 |
</p>
|
|
|
177 |
<p id="700522" class="block-content">The <select> tag returns defines an SQL statement. The <tt>id</tt>
|
|
|
178 |
attribute will be used as the identifier for the query. The <tt>resultClass</tt>
|
|
|
179 |
attribute value is the name of the class the the objects to be returned.
|
|
|
180 |
We can now query the objects as follows:
|
|
|
181 |
|
|
|
182 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700172">
|
|
|
183 |
//assume that $sqlmap is an TSqlMapGateway instance
|
|
|
184 |
$userList = $sqlmap->queryForList("SelectUsers");
|
|
|
185 |
|
|
|
186 |
//Or just one, if that's all you need:
|
|
|
187 |
$user = $sqlmap->queryForObject("SelectUsers");
|
|
|
188 |
</com:TTextHighlighter>
|
|
|
189 |
</p>
|
|
|
190 |
|
|
|
191 |
<p id="700523" class="block-content">The above example shows demonstrates only a fraction of the capabilities
|
|
|
192 |
of the SqlMap Data Mapper. Further details can be found in the
|
|
|
193 |
<a href="http://www.pradosoft.com/demo/sqlamp/">SqlMap Manual</a>.
|
|
|
194 |
</p>
|
|
|
195 |
|
|
|
196 |
<h2 id="140067">Combining SqlMap with Active Records</h2>
|
|
|
197 |
<p id="700524" class="block-content">The above example may seem trival and it also seems that there is
|
|
|
198 |
alot work just to retrieve some data. However, notice that the <tt>User</tt>
|
|
|
199 |
class is totally unware of been stored in the database, and the database is
|
|
|
200 |
unware of the <tt>User</tt> class.
|
|
|
201 |
</p>
|
|
|
202 |
<p id="700525" class="block-content">
|
|
|
203 |
One of advantages of SqlMap is the
|
|
|
204 |
ability to map complex object relationship, collections from an existing
|
|
|
205 |
database. On the other hand, <a href="?page=Database.ActiveRecord">Active Record</a>
|
|
|
206 |
provide a very simple way
|
|
|
207 |
to interact with the underlying database but unable to do more complicated
|
|
|
208 |
relationship or collections. A good compromise is to use SqlMap to retrieve
|
|
|
209 |
complicated relationships and collections as Active Record objects and then using
|
|
|
210 |
these Active Records to do the updates, inserts and deletes.
|
|
|
211 |
</p>
|
|
|
212 |
<p id="700526" class="block-content">Continuing with the previous example, we change the definition of the
|
|
|
213 |
<tt>User</tt> class to become an Active Record.
|
|
|
214 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700173">
|
|
|
215 |
class UserRecord extends TActiveRecord
|
|
|
216 |
{
|
|
|
217 |
const TABLE='users'; //table name
|
|
|
218 |
|
|
|
219 |
public $username; //the column named "username" in the "users" table
|
|
|
220 |
public $email;
|
|
|
221 |
|
|
|
222 |
/**
|
|
|
223 |
* @return TActiveRecord active record finder instance
|
|
|
224 |
*/
|
|
|
225 |
public static function finder($className=__CLASS__)
|
|
|
226 |
{
|
|
|
227 |
return parent::finder($className);
|
|
|
228 |
}
|
|
|
229 |
}
|
|
|
230 |
</com:TTextHighlighter>
|
|
|
231 |
</p>
|
|
|
232 |
|
|
|
233 |
<p id="700527" class="block-content">We also need to change the definition of the SqlMap XML configuration. We
|
|
|
234 |
just need to change the value of <tt>resultClass</tt> attribute to <tt>UserRecord</tt>.
|
|
|
235 |
<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_700174">
|
|
|
236 |
<?xml version="1.0" encoding="utf-8" ?>
|
|
|
237 |
<sqlMapConfig>
|
|
|
238 |
<select id="SelectUsers" resultClass="UserRecord">
|
|
|
239 |
SELECT username, email FROM users
|
|
|
240 |
</select>
|
|
|
241 |
</sqlMapConfig>
|
|
|
242 |
</com:TTextHighlighter>
|
|
|
243 |
</p>
|
|
|
244 |
|
|
|
245 |
|
|
|
246 |
<p id="700528" class="block-content">The PHP code for retrieving the users remains the same, but SqlMap
|
|
|
247 |
returns Active Records instead, and we can take advantage of the Active Record methods.
|
|
|
248 |
|
|
|
249 |
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_700175">
|
|
|
250 |
//assume that $sqlmap is an TSqlMapGateway instance
|
|
|
251 |
$user = $sqlmap->queryForObject("SelectUsers");
|
|
|
252 |
|
|
|
253 |
$user->email = 'test@example.com'; //change data
|
|
|
254 |
$user->save(); //save it using Active Record
|
|
|
255 |
</com:TTextHighlighter>
|
|
|
256 |
</p>
|
|
|
257 |
|
|
|
258 |
<h2 id="140068">References</h2>
|
|
|
259 |
<ul id="u1" class="block-content">
|
|
|
260 |
<li>Fowler et. al. <i>Patterns of Enterprise Application Architecture</i>,
|
|
|
261 |
Addison Wesley, 2002.</li>
|
|
|
262 |
<li>iBatis Team. <i>iBatis Data Mapper</i>,
|
|
|
263 |
<a href="http://ibatis.apache.org">http://ibatis.apache.org</a>.</li>
|
|
|
264 |
</ul>
|
|
|
265 |
|
|
|
266 |
<div class="last-modified">$Id: SqlMap.page 2136 2007-08-20 12:09:02Z xue $</div></com:TContent>
|