Commit 074f9ac0 by Qiang Xue

Finished dao guide chapter [skip ci]

parent a5582168
Database Access Objects Database Access Objects
=============== =======================
> Note: This section is under development. Built on top of [PDO](http://www.php.net/manual/en/book.pdo.php), Yii DAO (Database Access Objects) provides an
object-oriented API for accessing relational databases. It is the foundation for other more advanced database
access methods, including [query builder](db-query-builder.md) and [active record](db-active-record.md).
Yii includes a database access layer built on top of PHP's [PDO](http://www.php.net/manual/en/book.pdo.php). The database access objects (DAO) interface provides a When using Yii DAO, you mainly need to deal with plain SQLs and PHP arrays. As a result, it is the most efficient
uniform API, and solves some inconsistencies that exist between different database applications. Whereas Active Record provides database interactions through models, and the Query Builder assists in composing dynamic queries, DAO is a simple and efficient way to execute straight SQL on your database. You'll want to use DAO when the query to be run is expensive and/or no application models--and their corresponding business logic--are required. way to access databases. However, because SQL syntax may vary for different databases, using Yii DAO also means
you have to take extra effort to create a database-agnostic application.
By default, Yii supports the following DBMS: Yii DAO supports the following databases out of box:
- [MySQL](http://www.mysql.com/) - [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/) - [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/) - [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/) - [PostgreSQL](http://www.postgresql.org/)
- [CUBRID](http://www.cubrid.org/): version 9.3 or higher. (Note that due to a [bug](http://jira.cubrid.org/browse/APIS-658) in - [CUBRID](http://www.cubrid.org/): version 9.3 or higher.
the cubrid PDO extension, quoting of values will not work, so you need CUBRID 9.3 as the client as well as the server)
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher. - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher.
Configuration ## Creating DB Connections <a name="creating-db-connections"></a>
-------------
To start interacting with a database (using DAO or otherwise), you need to configure the application's database To access a database, you first need to connect to it by creating an instance of [[yii\db\Connection]]:
connection component. The Data Source Name (DSN) configures to which database application and specific database the application should connect:
```php
$db = new yii\db\Connection([
'dsn' => 'mysql:host=localhost;dbname=example',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
]);
```
Because a DB connection often needs to be accessed in different places, a common practice is to configure it
in terms of an [application component](structure-application-components.md) like the following:
```php ```php
return [ return [
...@@ -31,14 +43,7 @@ return [ ...@@ -31,14 +43,7 @@ return [
// ... // ...
'db' => [ 'db' => [
'class' => 'yii\db\Connection', 'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB 'dsn' => 'mysql:host=localhost;dbname=example',
//'dsn' => 'sqlite:/path/to/database/file', // SQLite
//'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
//'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
//'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver
//'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver
//'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver
//'dsn' => 'oci:dbname=//localhost:1521/mydatabase', // Oracle
'username' => 'root', 'username' => 'root',
'password' => '', 'password' => '',
'charset' => 'utf8', 'charset' => 'utf8',
...@@ -48,11 +53,25 @@ return [ ...@@ -48,11 +53,25 @@ return [
]; ];
``` ```
Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) for more details You can then access the DB connection via the expression `Yii::$app->db`.
on the format of the DSN string. Refer to [[yii\db\Connection]] for the full list of properties you can configure in the class.
> Tip: You can configure multiple DB application components if your application needs to access multiple databases.
When configuring a DB connection, you should always specify its Data Source Name (DSN) via the [[yii\db\Connection::dsn|dsn]]
property. The format of DSN varies for different databases. You may refer to Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php)
for more details. Below are some examples:
* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`
* SQLite: `sqlite:/path/to/database/file`
* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`
* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`
* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`
* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`
* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`
* Oracle: `oci:dbname=//localhost:1521/mydatabase`
Note that if you are connecting with a database via ODBC, you should configure the [[yii\db\Connection::driverName]] Note that if you are connecting with a database via ODBC, you should configure the [[yii\db\Connection::driverName]]
property so that Yii knows the actual database type. For example, property so that Yii can know the actual database type. For example,
```php ```php
'db' => [ 'db' => [
...@@ -64,49 +83,52 @@ property so that Yii knows the actual database type. For example, ...@@ -64,49 +83,52 @@ property so that Yii knows the actual database type. For example,
], ],
``` ```
You may access the primary `db` connection via the expression `\Yii::$app->db`. You may also configure multiple Besides the [[yii\db\Connection::dsn|dsn]] property, you often need to configure [[yii\db\Connection::username|username]]
DB connections in a single application. Simply assign different IDs to them in the application configuration: and [[yii\db\Connection::password|password]]. Please refer to [[yii\db\Connection]] for the full list of configurable properties.
```php > Info: When you create a DB connection instance, the actual connection to the database is not established until
return [ you execute the first SQL or you call the [[yii\db\Connection::open()|open()]] method explicitly.
// ...
'components' => [
// ...
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
],
'secondDb' => [
'class' => 'yii\db\Connection',
'dsn' => 'sqlite:/path/to/database/file',
],
],
// ...
];
```
Now you can use both database connections at the same time as needed:
```php ## Executing SQL Queries <a name="executing-sql-queries"></a>
$primaryConnection = \Yii::$app->db;
$secondaryConnection = \Yii::$app->secondDb;
```
If you don't want to define the connection as an [application component](structure-application-components.md), you can instantiate it directly: Once you have a database connection instance, you can execute a SQL query by taking the following steps:
1. Create a [[yii\db\Command]] with a plain SQL;
2. Bind parameters (optional);
3. Call one of the SQL execution methods in [[yii\db\Command]].
The following examples various ways of fetching data from a database:
```php ```php
$connection = new \yii\db\Connection([ $db = new yii\db\Connection(...);
'dsn' => $dsn,
'username' => $username, // return a set of rows. each row is an associative array of column names and values.
'password' => $password, // an empty array is returned if no results
]); $posts = $db->createCommand('SELECT * FROM post')
$connection->open(); ->queryAll();
// return a single row (the first row)
// false is returned if no results
$post = $db->createCommand('SELECT * FROM post WHERE id=1')
->queryOne();
// return a single column (the first column)
// an empty array is returned if no results
$titles = $db->createCommand('SELECT title FROM post')
->queryColumn();
// return a scalar
// false is returned if no results
$count = $db->createCommand('SELECT COUNT(*) FROM post')
->queryScalar();
``` ```
> Tip: If you need to execute an SQL query immediately after establishing a connection (e.g., to set the timezone or character set), you can add the following to your application configuration file: > Note: To preserve precision, the data fetched from databases are all represented as strings, even if the corresponding
database column types are numerical.
> Tip: If you need to execute a SQL query right after establishing a connection (e.g., to set the timezone or character set),
> you can do so in the [[yii\db\Connection::EVENT_AFTER_OPEN]] event handler. For example,
> >
```php ```php
return [ return [
...@@ -117,6 +139,7 @@ return [ ...@@ -117,6 +139,7 @@ return [
'class' => 'yii\db\Connection', 'class' => 'yii\db\Connection',
// ... // ...
'on afterOpen' => function($event) { 'on afterOpen' => function($event) {
// $event->sender refers to the DB connection
$event->sender->createCommand("SET time_zone = 'UTC'")->execute(); $event->sender->createCommand("SET time_zone = 'UTC'")->execute();
} }
], ],
...@@ -125,95 +148,139 @@ return [ ...@@ -125,95 +148,139 @@ return [
]; ];
``` ```
Executing Basic SQL Queries
---------------------------
Once you have a database connection instance, you can execute SQL queries using [[yii\db\Command]]. ### Binding Parameters <a name="binding-parameters"></a>
### Running SELECT Queries When creating a DB command from a SQL with parameters, you should almost always use the approach of binding parameters
to prevent SQL injection attacks. For example,
When the query to be executed returns a set of rows, you'll use `queryAll`:
```php ```php
$command = $connection->createCommand('SELECT * FROM post'); $post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
$posts = $command->queryAll(); ->bindValue(':id', $_GET['id'])
->bindValue(':status', 1)
->queryOne();
``` ```
When the query to be executed only returns a single row, you'll use `queryOne`: In the SQL statement, you can embed one or multiple parameter placeholders (e.g. `:id` in the above example). A parameter
placeholder should be a string starting with a colon. You may then call one of the following parameter binding methods
to bind the parameter values:
* [[yii\db\Command::bindValue()|bindValue()]]: bind a single parameter value
* [[yii\db\Command::bindValues()|bindValues()]]: bind multiple parameter values in one call
* [[yii\db\Command::bindParam()|bindParam()]]: similar to [[yii\db\Command::bindValue()|bindValue()]] but also
support binding parameter references.
The following example shows alternative ways of binding parameters:
```php ```php
$command = $connection->createCommand('SELECT * FROM post WHERE id=1'); $params = [':id' => $_GET['id'], ':status' => 1];
$post = $command->queryOne();
$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
->bindValues($params)
->queryOne();
$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)
->queryOne();
``` ```
When the query returns multiple rows but only one column, you'll use `queryColumn`: Parameter binding is implemented via [prepared statements](http://php.net/manual/en/mysqli.quickstart.prepared-statements.php).
Besides preventing SQL injection attacks, it may also improve performance by preparing a SQL statement once and
executing it multiple times with different parameters. For example,
```php ```php
$command = $connection->createCommand('SELECT title FROM post'); $command = $db->createCommand('SELECT * FROM post WHERE id=:id');
$titles = $command->queryColumn();
$post1 = $command->bindValue(':id', 1)->queryRow();
$post2 = $command->bindValue(':id', 2)->queryRow();
``` ```
When the query only returns a scalar value, you'll use `queryScalar`: Because [[yii\db\Command::bindParam()|bindParam()]] supports binding parameters by references, the above code
can also be written like the following:
```php ```php
$command = $connection->createCommand('SELECT COUNT(*) FROM post'); $command = $db->createCommand('SELECT * FROM post WHERE id=:id')
$postCount = $command->queryScalar(); ->bindParam(':id', $id);
$id = 1;
$post1 = $command->queryRow();
$id = 2;
$post2 = $command->queryRow();
``` ```
### Running Queries That Don't Return Values Notice that you bind the placeholder to the `$id` variable before the execution, and then change the value of that variable
before each subsequent execution (this is often done with loops). Executing queries in this manner can be vastly
more efficient than running a new query for every different parameter value.
If SQL executed doesn't return any data--for example, INSERT, UPDATE, and DELETE, you can use command's `execute` method: ### Executing Non-SELECT Queries <a name="non-select-queries"></a>
The `queryXyz()` methods introduced in the previous sections all deal with SELECT queries which fetch data from databases.
For queries that do not bring back data, you should call the [[yii\db\Command::execute()]] method instead. For example,
```php ```php
$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1'); $db->createCommand('UPDATE post SET status=1 WHERE id=1')
$command->execute(); ->execute();
``` ```
Alternatively, you can use the dedicated `insert`, `update`, and `delete` methods. These methods will properly quote table and column names used in your query, and you only need to provide the necessary values: The [[yii\db\Command::execute()]] method returns the number of rows affected by the SQL execution.
[[Ought to put a link to the reference docs here.]] For INSERT, UPDATE and DELETE queries, instead of writing plain SQLs, you may call [[yii\db\Command::insert()|insert()]],
[[yii\db\Command::update()|update()]], [[yii\db\Command::delete()|delete()]], respectively, to build the corresponding
SQLs. These methods will properly quote table and column names and bind parameter values. For example,
```php ```php
// INSERT // INSERT (table name, column values)
$connection->createCommand()->insert('user', [ $db->createCommand()->insert('user', [
'name' => 'Sam', 'name' => 'Sam',
'age' => 30, 'age' => 30,
])->execute(); ])->execute();
// INSERT multiple rows at once // UPDATE (table name, column values, condition)
$connection->createCommand()->batchInsert('user', ['name', 'age'], [ $db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();
// DELETE (table name, condition)
$db->createCommand()->delete('user', 'status = 0')->execute();
```
You may also call [[yii\db\Command::batchInsert()|batchInsert()]] to insert multiple rows in one shot, which is much
more efficient than inserting one row at a time:
```php
// table name, column names, column values
$db->createCommand()->batchInsert('user', ['name', 'age'], [
['Tom', 30], ['Tom', 30],
['Jane', 20], ['Jane', 20],
['Linda', 25], ['Linda', 25],
])->execute(); ])->execute();
```
// UPDATE
$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();
// DELETE ## Quoting Table and Column Names <a name="quoting-table-and-column-names"></a>
$connection->createCommand()->delete('user', 'status = 0')->execute();
```
Quoting Table and Column Names <a name="quoting-table-and-column-names"></a> When writing database-agnostic code, properly quote table and column names is often a headache because
------------------------------ different databases have different name quoting rules. To overcome this problem, you may use the following
quoting syntax introduced by Yii:
To make column and table names safe to use in queries, you can have Yii properly quote them for you: * `[[column name]]`: enclose a column name to be quoted in double square brackets;
* `{{table name}}`: enclose a table name to be quoted in double curly brackets.
Yii DAO will automatically turn such constructs in a SQL into the corresponding quoted column or table names.
For example,
```php ```php
$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; // executes this SQL for MySQL: SELECT COUNT(`id`) FROM `employee`
$rowCount = $connection->createCommand($sql)->queryScalar(); $count = $db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}")
->queryScalar();
``` ```
In the code above, `[[$column]]` will be converted to a properly quoted column name, while `{{table}}` will be converted to a properly quoted table name.
There's a special variant on this syntax specific to tablenames: `{{%Y}}` automatically appends the application's table prefix to the provided value, if a table prefix has been set: ### Using Table Prefix <a name="using-table-prefix"></a>
```php If most of your DB tables use some common prefix in their tables, you may use the table prefix feature supported
$sql = "SELECT COUNT([[$column]]) FROM {{%table}}"; by Yii DAO.
$rowCount = $connection->createCommand($sql)->queryScalar();
```
The code above will result in selecting from `tbl_table`, if you have table prefix configured like so: First, specify the table prefix via the [[yii\db\Connection::tablePrefix]] property:
```php ```php
return [ return [
...@@ -228,123 +295,92 @@ return [ ...@@ -228,123 +295,92 @@ return [
]; ];
``` ```
The alternative is to quote table and column names manually using [[yii\db\Connection::quoteTableName()]] and Then in your code, whenever you need to refer to a table whose name contains such a prefix, use the syntax
[[yii\db\Connection::quoteColumnName()]]: `{{%table name}}`. The percentage character will be automatically replaced with the table prefix that you have specified
when configuring the DB connection. For example,
```php ```php
$column = $connection->quoteColumnName($column); // executes this SQL for MySQL: SELECT COUNT(`id`) FROM `tbl_employee`
$table = $connection->quoteTableName($table); $count = $db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}")
$sql = "SELECT COUNT($column) FROM $table"; ->queryScalar();
$rowCount = $connection->createCommand($sql)->queryScalar();
``` ```
Using Prepared Statements
-------------------
To securely pass query parameters to your queries, you should make use of prepared statements. First, create a named placeholder in your query (using the syntax `:placeholder`). Then bind the placeholder to a variable and execute the query: ## Performing Transactions <a name="performing-transactions"></a>
```php When running multiple related queries in a sequence, you may need to wrap them in a transaction to ensure the integrity
$command = $connection->createCommand('SELECT * FROM post WHERE id=:id'); and consistency of your database. If any of the queries fails, the database will be rolled back to the state as if
$command->bindValue(':id', $_GET['id']); none of these queries is executed.
$post = $command->queryOne();
```
Another purpose for prepared statements (aside from improved security) is the ability to execute a query multiple times while preparing it only once: The following code shows a typical way of using transactions:
```php ```php
$command = $connection->createCommand('DELETE FROM post WHERE id=:id'); $db->transaction(function($db) {
$command->bindParam(':id', $id); $db->createCommand($sql1)->execute();
$db->createCommand($sql2)->execute();
$id = 1; // ... executing other SQL statements ...
$command->execute(); });
$id = 2;
$command->execute();
``` ```
Notice that you bind the placeholder to the variable before the execution, and then change the value of that variable before each subsequent execution (this is often done with loops). Executing queries in this manner can be vastly more efficient than running each query one at a time. The above code is equivalent to the following:
Performing Transactions
-----------------------
When running multiple, related queries in a sequence, you may need to wrap them in a transaction to
protect your data's integrity. Transactions allow you to write a series of queries such that they'll all succeed or have no effect whatsoever. Yii provides a simple interface to work with transactions in simple
cases but also for advanced usage when you need to define isolation levels.
The following code shows a simple pattern that all code that uses transactional queries should follow:
```php ```php
$transaction = $connection->beginTransaction(); $transaction = $db->beginTransaction();
try { try {
$connection->createCommand($sql1)->execute(); $db->createCommand($sql1)->execute();
$connection->createCommand($sql2)->execute(); $db->createCommand($sql2)->execute();
// ... executing other SQL statements ... // ... executing other SQL statements ...
$transaction->commit(); $transaction->commit();
} catch(\Exception $e) { } catch(\Exception $e) {
$transaction->rollBack(); $transaction->rollBack();
throw $e; throw $e;
} }
``` ```
The first line starts a new transaction using the [[yii\db\Connection::beginTransaction()|beginTransaction()]] method of the database connection By calling the [[yii\db\Connection::beginTransaction()|beginTransaction()]] method, a new transaction is started.
object. The transaction itself is represented by a [[yii\db\Transaction]] object stored in `$transaction`. The transaction is represented as a [[yii\db\Transaction]] object stored in the `$transaction` variable. Then,
We wrap the execution of all queries in a try-catch block to be able to handle errors. the queries being executed are enclosed in a `try...catch...` block. If all queries are executed successfully,
We call [[yii\db\Transaction::commit()|commit()]] on success to commit the transaction and the [[yii\db\Transaction::commit()|commit()]] method is called to commit the transaction. Otherwise, an exception
[[yii\db\Transaction::rollBack()|rollBack()]] in case of an error. This will revert the effect of all queries will be triggered and caught, and the [[yii\db\Transaction::rollBack()|rollBack()]] method is called to roll back
that have been executed inside of the transaction. the changes made the queries prior to that failed query in the transaction.
`throw $e` is used to re-throw the exception in case we can not handle the error ourselves and delegate it
to some other code or the Yii error handler.
It is also possible to nest multiple transactions, if needed:
```php ### Specifying Isolation Levels <a name="specifying-isolation-levels"></a>
// outer transaction
$transaction1 = $connection->beginTransaction();
try {
$connection->createCommand($sql1)->execute();
// inner transaction Yii also supports setting [isolation levels] for your transactions. By default, when starting a new transaction,
$transaction2 = $connection->beginTransaction(); it will use the isolation level set by your database system. You can override the default isolation level as follows,
try {
$connection->createCommand($sql2)->execute();
$transaction2->commit();
} catch (Exception $e) {
$transaction2->rollBack();
}
$transaction1->commit(); ```php
} catch (Exception $e) { $isolationLevel = \yii\db\Transaction::REPEATABLE_READ;
$transaction1->rollBack();
}
```
Note that your DBMS should have support for Savepoints for this to work as expected. $db->transaction(function ($db) {
The above code will work for any DBMS but transactional safety is only guaranteed if ....
the underlying DBMS supports it. }, $isolationLevel);
Yii also supports setting [isolation levels] for your transactions. // or alternatively
When beginning a transaction it will run in the default isolation level set by your database system.
You can specifying an isolation level explicitly when starting a transaction:
```php $transaction = $db->beginTransaction($isolationLevel);
$transaction = $connection->beginTransaction(\yii\db\Transaction::REPEATABLE_READ);
``` ```
Yii provides four constants for the most common isolation levels: Yii provides four constants for the most common isolation levels:
- [[\yii\db\Transaction::READ_UNCOMMITTED]] - the weakest level, Dirty reads, Non-repeatable reads and Phantoms may occur. - [[\yii\db\Transaction::READ_UNCOMMITTED]] - the weakest level, Dirty reads, non-repeatable reads and phantoms may occur.
- [[\yii\db\Transaction::READ_COMMITTED]] - avoid Dirty reads. - [[\yii\db\Transaction::READ_COMMITTED]] - avoid dirty reads.
- [[\yii\db\Transaction::REPEATABLE_READ]] - avoid Dirty reads and Non-repeatable reads. - [[\yii\db\Transaction::REPEATABLE_READ]] - avoid dirty reads and non-repeatable reads.
- [[\yii\db\Transaction::SERIALIZABLE]] - the strongest level, avoids all of the above named problems. - [[\yii\db\Transaction::SERIALIZABLE]] - the strongest level, avoids all of the above named problems.
You may use the constants named above but you can also use a string that represents a valid syntax that can be Besides using the above constants to specify isolation levels, you may also use strings with a valid syntax supported
used in your DBMS following `SET TRANSACTION ISOLATION LEVEL`. For postgres this could be for example by the DBMS that you are using. For example, in PostgreSQL, you may use `SERIALIZABLE READ ONLY DEFERRABLE`.
`SERIALIZABLE READ ONLY DEFERRABLE`.
Note that some DBMS allow setting of the isolation level only for the whole connection so subsequent transactions Note that some DBMS allow setting the isolation level only for the whole connection. Any subsequent transactions
may get the same isolation level even if you did not specify any. When using this feature will get the same isolation level even if you do not specify any. When using this feature
you may need to set the isolation level for all transactions explicitly to avoid conflicting settings. you may need to set the isolation level for all transactions explicitly to avoid conflicting settings.
At the time of this writing affected DBMS are MSSQL and SQLite. At the time of this writing, only MSSQL and SQLite are affected.
> Note: SQLite only supports two isolation levels, so you can only use `READ UNCOMMITTED` and `SERIALIZABLE`. > Note: SQLite only supports two isolation levels, so you can only use `READ UNCOMMITTED` and `SERIALIZABLE`.
Usage of other levels will result in an exception being thrown. Usage of other levels will result in an exception being thrown.
...@@ -356,8 +392,43 @@ You have to call [[yii\db\Transaction::setIsolationLevel()]] in this case after ...@@ -356,8 +392,43 @@ You have to call [[yii\db\Transaction::setIsolationLevel()]] in this case after
[isolation levels]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels [isolation levels]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
Replication and Read-Write Splitting ### Nesting Transactions <a name="nesting-transactions"></a>
------------------------------------
If your DBMS supports Savepoint, you may nest multiple transactions like the following:
```php
$db->transaction(function ($db) {
// outer transaction
$db->transaction(function ($db) {
// inner transaction
});
});
```
Or alternatively,
```php
$outerTransaction = $db->beginTransaction();
try {
$db->createCommand($sql1)->execute();
$innerTransaction = $db->beginTransaction();
try {
$db->createCommand($sql2)->execute();
$innerTransaction->commit();
} catch (Exception $e) {
$innerTransaction->rollBack();
}
$outerTransaction->commit();
} catch (Exception $e) {
$outerTransaction->rollBack();
}
```
## Replication and Read-Write Splitting <a name="read-write-splitting"></a>
Many DBMS support [database replication](http://en.wikipedia.org/wiki/Replication_(computing)#Database_replication) Many DBMS support [database replication](http://en.wikipedia.org/wiki/Replication_(computing)#Database_replication)
to get better database availability and faster server response time. With database replication, data are replicated to get better database availability and faster server response time. With database replication, data are replicated
...@@ -516,44 +587,44 @@ $rows = $db->useMaster(function ($db) { ...@@ -516,44 +587,44 @@ $rows = $db->useMaster(function ($db) {
You may also directly set `$db->enableSlaves` to be false to direct all queries to the master connection. You may also directly set `$db->enableSlaves` to be false to direct all queries to the master connection.
Working with database schema ## Working with Database Schema <a name="database-schema"></a>
----------------------------
### Getting schema information Yii DAO provides a whole set of methods to let you manipulate database schema, such as creating new tables,
dropping a column from a table, etc. These methods are listed as follows:
You can get a [[yii\db\Schema]] instance like the following: * [[yii\db\Command::createTable()|createTable()]]: creating a table
* [[yii\db\Command::renameTable()|renameTable()]]: renaming a table
* [[yii\db\Command::dropTable()|dropTable()]]: removing a table
* [[yii\db\Command::truncateTable()|truncateTable()]]: removing all rows in a table
* [[yii\db\Command::addColumn()|addColumn()]]: adding a column
* [[yii\db\Command::renameColumn()|renameColumn()]]: renaming a column
* [[yii\db\Command::dropColumn()|dropColumn()]]: removing a column
* [[yii\db\Command::alterColumn()|alterColumn()]]: altering a column
* [[yii\db\Command::addPrimaryKey()|addPrimaryKey()]]: adding a primary key
* [[yii\db\Command::dropPrimaryKey()|dropPrimaryKey()]]: removing a primary key
* [[yii\db\Command::addForeignKey()|addForeignKey()]]: adding a foreign key
* [[yii\db\Command::dropForeignKey()|dropForeignKey()]]: removing a foreign key
* [[yii\db\Command::createIndex()|createIndex()]]: creating an index
* [[yii\db\Command::dropIndex()|dropIndex()]]: removing an index
```php These methods can be used like the following:
$schema = $connection->getSchema();
```
It contains a set of methods allowing you to retrieve various information about the database:
```php
$tables = $schema->getTableNames();
```
For the full reference, check [[yii\db\Schema]].
### Modifying schema
Aside from basic SQL queries, [[yii\db\Command]] contains a set of methods allowing the modification of the database schema:
- createTable, renameTable, dropTable, truncateTable
- addColumn, renameColumn, dropColumn, alterColumn
- addPrimaryKey, dropPrimaryKey
- addForeignKey, dropForeignKey
- createIndex, dropIndex
These can be used as follows:
```php ```php
// CREATE TABLE // CREATE TABLE
$connection->createCommand()->createTable('post', [ $db->createCommand()->createTable('post', [
'id' => 'pk', 'id' => 'pk',
'title' => 'string', 'title' => 'string',
'text' => 'text', 'text' => 'text',
]); ]);
``` ```
For the full reference, check [[yii\db\Command]]. You can also retrieve the definition information about a table through
the [[yii\db\Connection::getTableSchema()|getTableSchema()]] method of a DB connection. For example,
```php
$table = $db->getTableSchema('post');
```
The method returns a [[yii\db\TableSchema]] object which contains the information about the table's columns,
primary keys, foreign keys, etc. All these information are mainly utilized by [query builder](db-query-builder.md)
and [active record](db-active-record.md) to help you write database-agnostic code.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment