The
Data source schema mode-table entry mode
Table entry mode acts as an object in the database table access entry, and one instance handles all rows in the table.
can be understood to encapsulate SQL statements that were previously dispersed across pages, and a table is an object that handles all the business logic associated with the table, which improves the reusability of the code.
Now, when I first graduated, I used the table-entry mode a lot.
Specific implementation See Code:
database.php
<?php class database{ //Just for demonstration, Typically, the configuration of a database is written separately in the configuration file private static $_dbConfig = Array ( ' host ' => ' 127.0.0.1 ', ' username ' => ' root ', ' pwd ' => ', ' dbname ' => ' bussiness ' ); private static $_instance; public static function getinstance () { if (Is_null (self::$_instance)) { &nbsP; self::$_instance = new mysqli (self::$_dbconfig[' host '], self::$_dbconfig[') Username '], self::$_dbconfig[' pwd '], self::$_dbconfig[' dbname ']; if (Self::$_instance->connect_errno) { throw new exception (self::$_instance->connect_error);
} } return self::$_instance; } }
person.php
<?php require_once ' database.php '; class person extends database{ public $instance; public $table = ' person '; Public function __construct () { $this-> Instance = person::getinstance (); } public function getpersonbyid ($personId) { $sql = "select * from $this->table where id=$ PersonId "; echo $sql; return $this->instance->query ($sql); } /** Some other additions and deletions to check the operation method ...**/ }
index.php
<?php
require_once ' person.php ';
$person = new Person ();
Var_dump ($person->getpersonbyid (1)->FETCH_ASSOC ());
die ();
Run Result:
SELECT * FROM person where id=1
Array (size=2)
' id ' => string ' 1 ' (length=1)
' Name ' => string ' Ben ' (length=3)
Data source schema mode-row entry mode
First, the concept
Row Data Portal: an object that acts as a single record entry in a data source, one instance per row.
Second, the simple implementation of the row data entry
For the sake of understanding, it is easy to realize first:
<?php /** * Enterprise Application architecture Data Source architecture mode row data entry 2010-09-27 sz * @author Member of Phppan.p#gmail.com http://www.phppan.com * Society (http://www.blog-brother.com/) * @package architecture */class persongateway { private
$_name;
private $_id;
private $_birthday;
public function __construct ($id, $name, $birthday) {
$this->setid ($id);
$this->setname ($name);
$this->setbirthday ($birthday); &NBSP;&NBSP;&NBSP;&NBSP} public function getname () {
return $this->_name; &NBSP;&NBSP;&NBSP;&NBSP} &NBSP;&NBSP;&NBSP;&NBsp;public function setname ($name) { $this->_
name = $name; &NBSP;&NBSP;&NBSP;&NBSP} public function getid () {
return $this->_id; &NBSP;&NBSP;&NBSP;&NBSP} public function setid ($id) {
$this->_id = $id; &NBSP;&NBSP;&NBSP;&NBSP} public function getbirthday () {
return $this->_birthday;
&NBSP;&NBSP;&NBSP;&NBSP} public function setbirthday ($birthday) {
$this->_birthday = $birthday;
&NBSP;&NBSP;&NBSP;&NBSP} /** * entry class itself has update operations */ public function update () { $data = array (' id ' => $this->_id, ' name ' =>
$this->_name, ' birthday ' => $this->_birthday);
$sql = "update person set "; foreach ($data as $field => $value ) { $sql .= "'" &NBSP;.
$field . "' = '" . $value . "',"; &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP} $sql =
substr ($sql, 0, -1); $sql .= " WHERE id = "
. $this->_id; &NBSP;&NBSP; return db::query ($sql);
&NBSP;&NBSP;&NBSP;&NBSP} /** * entry class itself has insert operations */ public function insert () { $data = array (' name ' => $this->_name, ')
Birthday ' => $this->_birthday);
$sql = "insert into person "; $sql .= "(' . implode ', '", array_
Keys ($data)) . "')"; $sql .= " values ('" . implode) ("', '",
array_values ($data)) . "')";
return db::query ($sql); &NBSP;&NBSP;&NBSP;&NBSP} public stAtic function load ($rs) { /* here can be added cache * / return new persongateway ($rs [' id '] ? $rs [
' ID '] : null, $rs [' name '], $rs [' birthday ']); &NBSP;&NBSP;&NBSP;&NBSP} } /** * staff lookup class */class personfinder { & Nbsp; public function find ($id) { $
sql = "select * from person where id = " . $id;
$rs = db::query ($sql);
return persongateway::load ($RS); &NBSP;&NBSP;&NBSP;&NBSP} public function findall () {
$sql = "Select * from person"; $rs = db::query ($sql);
$result = array (); if (Is_array ($rs)) { foreach ($rs as $row) { $result [] = persongateway::
Load ($row); }
} return $result; &NBSP;&NBSP;&NBSP;&NBSP} } class db { /** & nbsp; * This is just a demo method for executing SQL * @param string $sql SQL */   to execute;p ublic static function query ($sql) { echo
"Executive sql: ", $sql, " <br />"; if (Strpos ($sql, ' SELECT ') !== false { // example, returns query results for a select query return array (' id ' => 1, ' name ' => ' Martin ', ' birthday ')
=> ' 2010-09-15 ');
&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP}  } } /** * client invocation */class client { /** * main
program.
*/ public static function main () { header ("Content-type:text/html; charset=utf-8
"); &nBsp /* Write Sample */
$data = array (' name ' => ' Martin ', ' birthday ' => ' 2010-09-15 ');
$person = persongateway::load ($data);
$person->insert (); /* Update Sample */ $data = array (' id ' => 1, ' name ' => ' Martin ', ')
Birthday ' => ' 2010-09-15 ');
$person = persongateway::load ($data);
$person->setname (' Phppan ');
$person->update (); /* Query Sample */ $finder = new personfinder ();
$person = $finder->find (1);
echo $person->getname (); &NBSP;&NBSP;&NBSP;&NBSP} } Client::main ();?>
iii. operating mechanism
A row data entry is an object that is extremely similar to a single record, in which each column in the database is a field.
A row data entry can generally implement arbitrary conversions from the data source type to the type in memory.
There is no domain logic for the row data entry, and if it exists, it is an active record.
As you can see in the instance, set up a separate Orderfinder class to read information from the database. Of course, you can also choose not to create a new class, the static lookup method, but it does not support the need for different data sources to provide different lookup methods of polymorphism. Therefore, it is best to set the object of the lookup method individually.
Row data portals can be used for views in addition to tables. What you need to be aware of is the update operation of the view.
It is a good practice to have "define metadata mappings" in your code, so that all database access code can be automatically generated during automatic setup.
Iv. use of the scene
4.1 Transaction Scripts
The database access code can be well separated and easily reused by different transaction scripts. However, you may find that the business logic repeats itself in multiple scripts that might be useful in a row data entry. Moving these logic over and over makes the row data entry evolve into an activity record, reducing the duplication of business logic.
4.2 Field Models
If you want to change the structure of the database but do not want to change the domain logic, the use of row data entry is a good choice. In most cases, the data mapper is more suited to the domain model.
Row data entry can be used in conjunction with the data mapper, although this may seem a bit superfluous, however, this method works well when the row data entry is automatically generated from the metadata and the data mapper is implemented manually.
Data source schema mode-activity record
"Intent of the activity record"
An object that wraps a row in a datasheet or view, encapsulates database access, and adds domain logic to the data.
"Applicable scenario for Active records"
Applies to less complex domain logic, such as CRUD operations.
"Operating mechanism for Activity records"
objects have both data and behavior. It uses the most direct method of putting data access logic into the domain object.
The nature of the activity record is a domain model in which the class in the domain model and the record structure in the base database should match exactly, each of the fields of the class corresponds to each column of the table.
In general, activity records include the following methods:
1, the data row constructs an activity record instance;
2. To construct a new example for the future insertion of the table;
3, using static search method to wrap the commonly used SQL query and return activity records;
4. Update the database and insert the data in the activity record into the database;
5, get or set the domain;
6, the implementation of part of the business logic.
"Advantages and disadvantages of activity records"
Advantages:
1, simple, easy to create and easy to understand.
2, in the use of transaction scripts, reduce code replication.
3, you can change the database structure without changing the domain logic.
4. Derivation and test validation based on a single activity record can be effective.
Disadvantages:
1, there is no hidden relational database exists.
2. The active record is valid only if the active Record object and the table in the database correspond directly.
3, the requirements of the object design and the design of the database tightly coupled, which makes the project further refactoring is difficult
"Activity record and other modes"
Data source schema-type row data entry: Activity records are very similar to row data portals. The main difference between the two is that the row data entry has only database access and activity records have both data source logic and domain logic.
"PHP Examples of active records"
<?php /** * Enterprise Application architecture Data Source architecture mode activity Records 2010-10-17 sz * @ author phppan.p#gmail.com http://www.phppan.com * , member of the Society of Columbia (http:// www.blog-brother.com/) * @package architecture */ /** * Order */ class order { /** * orders id var <type> */ private $_order_id; /** * customer id * @var <type> */ private $_customer_id; /** *
Order Amount * @var <type> */ private $_amount; Public function __construct ($order _id, $customer _id, $amount) { $this-> _order_id = $order _id; $this->_customer_id = $customer _id; $this- ";_amount = $amount; } /** * Instance Delete operations */ public function delete () { $sql = "delete from order set where order_id = " . $this->_order_id . " and customer_id = . $this->_customer_id; return db::query ($sql); } /** * Instance update operations */ public function update () { } /** * insert Operation */ Public function insert () { } public static function load ($rs) { return new order ($rs [' order_id '] ? $rs [' order_id '] : null, $rs [' customer_id '], $rs [] Amount '] ? $rs [' Amount '] : 0); } } Class customer { private $_name; private $_customer_id; public function __ Construct ($customer _id, $name) { $this->_customer_id = $customer _id; $this->_name = $name; } /** * User Delete order operation This instance method includes business logic * by invoking the order instance to implement * assuming that this is the corresponding delete operation (in practice it might be a fake delete operation marked with a field) */ Public function deleteorder ($order _id) { $order = order::load (Array (
' order_id ' => $order _id, ' customer_id ' => $this->_customer_id);
return $order->delete (); } /** * Instance update operations */ Public function update () { } /** * Portal class itself has insert operations */ Public function insert () { } public static function Load ($rs) { /* Here you can add caching */ return new customer ($rs [' customer_id '] ? $rs [' customer_id '] : null, $rs [' name ']); } /** * According to customer id find * @param integer $id customer id * @ return customer Customer Object */ public static function find ($id) { Return customerfinder::find ($id); } } /** * People lookup class */ class customerfinder { public static function Find ($id) { $sql = "select * from person where customer_id = . $id; $rs = db::query ($sql); Return customer:: Load ($rs); } } class db { /** * This is just aExecute SQL Demo methods * @param string $sql need to perform sql */ public static function query ($sql) { echo "executive sql: ", $sql, " <br /> "; if (Strpos ($sql, ' SELECT ') !== false) { // example, return query results for select query return array (' customer_id ' => 1, ' name ') => ' Martin '; } } } /** * Client Invocation */ class client { /** * main program. * * Public static function main () { header ("content-type:text/html; Charset=utf-8 "); /* load Customer ID 1 Customer information */ $customer = customer::find (1); /* assume that the user has an order ID of 9527*/ $customer->deleteorder (9527); } } CliEnt::main (); ?>
As in the previous article, this is just an example of an active record, and for the application of the active record pattern, you can view the DB class in the Yii framework, which has a Cactiverecord abstract class in its source code, where you can see the application of the active record pattern.
In addition, if you create an activity record from a transaction script, you typically first wrap the table as a portal, and then start the behavior migration to make the table Drill-down into an active record.
You can use the Magic method to __set methods and __get methods for the access and settings of the fields in the active record, as in the Yii framework.
Data source schema mode-Data mapper
One: Data mapper
Relational databases are used to store data and relationships, and objects can handle business logic, so to leelawadee the data itself and the business logic into one object, we either use the activity record or separate the two and associate the two with the data mapper.
The data mapper is the intermediate software layer separating the memory object and the database, and the following sequence diagram describes the concept of the intermediate software layer:
In this sequence diagram, we also see a concept that the mapper needs to be able to get the domain object (in this case, a person is a domain object). As for the changes in the data (or the changes in the domain objects), the Mapper must know these changes, and at this point we need the work unit pattern (after discussion).
From the above, we seem to see the data mapper is quite simple, the complex part is: we need to deal with the table query, domain object inheritance, and so on. The fields of the realm object may come from multiple tables in the database, and we have to let the data mapper do more. Yes, as we mentioned above, the data mapper can do two complex parts:
1: Perceptual change;
2: By the result of the query of the table, assigning value to the domain object;
In order to perceive changes and to be consistent with database objects, you need to identify mappings (schema pattern object and relational structure pattern: Identity field), which typically requires a registry that identifies mappings, or holds an identity map for each lookup method, the following code is the latter:
void Main ()
{
sqlhelper.connectionstring = "Data source=xxx;initial catalog=xxx;integrated security=false; User Id=sa; password=xxx; Connect timeout=15; Encrypt=false; Trustservercertificate=false ";
var user1 = User.finduser ("6f7ff44435f3412cada61898bcf0df6c");
var user2 = User.finduser ("6f7ff44435f3412cada61898bcf0df6c");
(User1 = = User2). Dump ();
"End". Dump ();
}
Public abstract class Basemode
{
public string Id {get; set;}
public string Name {get; set;}
}
public class User:basemode
{
Static UserMap map = new UserMap ();
public static User Finduser (string id)
{
var user = map. Find (ID);
return user;
}
}
public class Usermap:abstractmapper<user>
{
Public User find (string id)
{
Return (User) abstractfind (ID);
}
protected override User Abstractfind (string id)
{
var user = base. Abstractfind (ID);
if (user = null)
{
' Is Null '. Dump ();
String sql = "SELECT * from [El_organization]." [User] WHERE id= @Id ";
var PMS = new sqlparameter[]
{
New SqlParameter ("@Id", Id)
};
var ds = SqlHelper.ExecuteDataset (CommandType.Text, SQL, PMS);
user = Datatablehelper.tolist<user> (ds. Tables[0]). FirstOrDefault ();
if (user = null)
{
return null;
}
user = Load (user);
return user;
}
return user;
}
Public list<user> findlist (string name)
{
SELECT * from USER WHERE NAME like name
List<user> users = null;
Return Loadall (users);
}
public void Update (user user)
{
UPDATE USER SET ....
}
}
Public abstract class abstractmapper<t> where T:basemode
{
The problem here is that as the object disappears, the Loadedmap is recycled.
Protected Dictionary<string, t> loadedmap = new dictionary<string, t> ();
Protected T Load (T-t)
{
if (Loadedmap.containskey (t.id))
{
return loadedmap[t.id];
}
Else
{
Loadedmap.add (t.id, T);
return t;
}
}
Protected list<t> Loadall (list<t> ts)
{
for (int i=0 i < ts. Count; i++)
{
Ts[i] = Load (Ts[i]);
}
return TS;
}
Protected virtual T Abstractfind (string id)
{
if (Loadedmap.containskey (ID))
{
return Loadedmap[id];
}
Else
{
return null;
}
}
}
Above is a simple mapper, which has the identity mapping function. Because there is an identity mapping, we run this code to get the result:
Return to the essence of the question: what is called "Data map"
Actually, this is a very important question.
UserMap uses the Find method to turn a database record into a user object, which is called a "data map," but what really plays a central role is the user = Datatablehelper.tolist<user> (ds). Tables[0]). FirstOrDefault (); This line of code. Further,datatablehelper.tolist<t> This method completes the data mapping function.
So what the,datatablehelper.tolist<t> method does, in fact, is to get the field values of the DataTable based on the property name. This is an easy way to or it might be a good idea in a lot of business-not-complex scenarios, but because the business is often complex, in practice, we don't have much of a way to use this method, and in most cases we need to encode like this to complete the mapping:
Someone. Name = convert.tostring (row["Name")
Do not doubt that the above line of code, called the data map, any tall concept, is actually that you write a lot of code.
Data mapping in 1.1 entityframework
This is a typical EF data mapping class,
public class Coursemap:entitytypeconfiguration<course>
{
Public Coursemap ()
{
Primary Key
This. Haskey (t => T.courseid);
Properties
This. Property (T => T.courseid)
. Hasdatabasegeneratedoption (Databasegeneratedoption.none);
This. Property (T => t.title)
. IsRequired ()
. Hasmaxlength (100);
Table & Column Mappings
This. ToTable ("Course");
This. Property (t => T.courseid). Hascolumnname ("CourseID");
This. Property (t => t.title). Hascolumnname ("Title");
This. Property (t => t.credits). Hascolumnname ("credits");
This. Property (t => T.departmentid). Hascolumnname ("DepartmentID");
Relationships
This. Hasmany (T => t.people)
. Withmany (T => t.courses)
. Map (M =>
{
M.totable ("Courseinstructor");
M.mapleftkey ("CourseID");
M.maprightkey ("PersonID");
});
This. Hasrequired (T => t.department)
. Withmany (T => t.courses)
. Hasforeignkey (d => D.departmentid);
}
}
We can see that the EF data map, that's the real data map. The most basic thing in the house is to do one of these things:
Which field the database is, and which property is the property of the corresponding memory object.
Ultimately, it generates domain models through an object factory, and the principle is as follows:
Internal static Course Buildcourse (IDataReader Reader)
{
Course Course = new Course (Reader[fieldnames.courseid]);
Contract. Title = Reader[fieldnames.title]. ToString ();
...
return contract;
}
Second: storage storehouse
UserMap is the concept of a data mapper too heavy? Because it does the mapping and persistence thing, it even has to hold a unit of work. So, if we can be like the EF, the mapper only does the mapping, and the rest of the things? OK, this part of the separation is called the storehouse.
Three: Say a little more datatablehelper.tolist<t> simplified data mapper
It's actually the DataTable to List. If you are using a framework such as EF or NHibernate, then use the mapper that they provide (strictly speaking, you are not using their mapper.) Because the frameworks themselves are using their own mapper, we are only configuring the data and relationships that the mapper needs, sometimes these configurations are in the configuration file, sometimes on the field or attribute, and sometimes on a simple but large line of code. We can of course create our own standard mapper, which is implemented by Tim McCarthy in the domain driven design C # 2008 implementation. However, EF and NHibernate are good, but many times we still have to use handwritten SQL because:
1:ef and NHibernate are required to learn cost, which represents the high cost of team training, and error-prone;
2: The efficiency of handwriting SQL should not be discarded.