X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FManual%2FCookbook.pod;h=b4d52da48c8d562280e44d5432491aa4045f5b16;hb=1415f198da91a23911972ad06b6dafaf6f4c0e8f;hp=7ae3630e59c6bf93957c70d06680da7345523fee;hpb=83677d0711ff901937ed402079dad9e9604e2dc5;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 7ae3630..b4d52da 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -141,7 +141,7 @@ Next, you can execute your complex query using bind parameters like this: ); ... and you'll get back a perfect L (except, of course, -that you cannot modify the rows it contains, ie. cannot call L, +that you cannot modify the rows it contains, e.g. cannot call L, L, ... on it). Note that you cannot have bind parameters unless is_virtual is set to true. @@ -201,7 +201,7 @@ to access the returned value: # SELECT name name, LENGTH( name ) # FROM artist -Note that the C attribute B with the sql +Note that the C attribute B with the SQL syntax C< SELECT foo AS bar > (see the documentation in L). You can control the C part of the generated SQL via the C<-as> field attribute as follows: @@ -317,7 +317,7 @@ Please see L documentation if you are in any way unsure about the use of the attributes above (C< join >, C< select >, C< as > and C< group_by >). -=head2 Subqueries (EXPERIMENTAL) +=head2 Subqueries You can write subqueries relatively easily in DBIC. @@ -329,7 +329,7 @@ You can write subqueries relatively easily in DBIC. artist_id => { 'IN' => $inside_rs->get_column('id')->as_query }, }); -The usual operators ( =, !=, IN, NOT IN, etc) are supported. +The usual operators ( =, !=, IN, NOT IN, etc.) are supported. B: You have to explicitly use '=' when doing an equality comparison. The following will B work: @@ -365,14 +365,10 @@ That creates the following SQL: WHERE artist_id = me.artist_id ) -=head3 EXPERIMENTAL - -Please note that subqueries are considered an experimental feature. - =head2 Predefined searches -You can write your own L class by inheriting from it -and defining often used searches as methods: +You can define frequently used searches as methods by subclassing +L: package My::DBIC::ResultSet::CD; use strict; @@ -415,10 +411,16 @@ Then call your new method in your code: Using SQL functions on the left hand side of a comparison is generally not a good idea since it requires a scan of the entire table. (Unless your RDBMS -supports indexes on expressions - including return values of functions -, and +supports indexes on expressions - including return values of functions - and you create an index on the return value of the function in question.) However, it can be accomplished with C when necessary. +Your approach for doing so will depend on whether you have turned +quoting on via the C and C attributes. If you +explicitly defined C and C in your +C (see L) then +you are using quoting, otherwise not. + If you do not have quoting on, simply include the function in your search specification as you would any column: @@ -775,7 +777,7 @@ B package My::App::Schema; - use base DBIx::Class::Schema; + use base 'DBIx::Class::Schema'; # load subclassed classes from My::App::Schema::Result/ResultSet __PACKAGE__->load_namespaces; @@ -795,7 +797,7 @@ B use strict; use warnings; - use base My::Shared::Model::Result::Baz; + use base 'My::Shared::Model::Result::Baz'; # WARNING: Make sure you call table() again in your subclass, # otherwise DBIx::Class::ResultSourceProxy::Table will not be called @@ -818,7 +820,7 @@ this example we have a single user table that carries a boolean bit for admin. We would like like to give the admin users objects (L) the same methods as a regular user but also special admin only methods. It doesn't make sense to create two -seperate proxy-class files for this. We would be copying all the user +separate proxy-class files for this. We would be copying all the user methods into the Admin class. There is a cleaner way to accomplish this. @@ -1221,6 +1223,8 @@ callback routine. =head1 TRANSACTIONS +=head2 Transactions with txn_do + As of version 0.04001, there is improved transaction support in L and L. Here is an example of the recommended way to use it: @@ -1252,11 +1256,16 @@ example of the recommended way to use it: deal_with_failed_transaction(); } +Note: by default C will re-run the coderef one more time if an +error occurs due to client disconnection (e.g. the server is bounced). +You need to make sure that your coderef can be invoked multiple times +without terrible side effects. + Nested transactions will work as expected. That is, only the outermost transaction will actually issue a commit to the $dbh, and a rollback at any level of any transaction will cause the entire nested transaction to fail. - + =head2 Nested transactions and auto-savepoints If savepoints are supported by your RDBMS, it is possible to achieve true @@ -1272,7 +1281,7 @@ row. my $schema = MySchema->connect("dbi:Pg:dbname=my_db"); # Start a transaction. Every database change from here on will only be - # commited into the database if the eval block succeeds. + # committed into the database if the eval block succeeds. eval { $schema->txn_do(sub { # SQL: BEGIN WORK; @@ -1325,7 +1334,7 @@ row. }; if ($@) { # There was an error while handling the $job. Rollback all changes - # since the transaction started, including the already commited + # since the transaction started, including the already committed # ('released') savepoints. There will be neither a new $job nor any # $thing entry in the database. @@ -1348,9 +1357,19 @@ commits are happening, but it works just the same as for plain L<>: If the C-block around C fails, a rollback is issued. If the C succeeds, the transaction is committed (or the savepoint released). -While you can get more fine-grained controll using C, C +While you can get more fine-grained control using C, C and C, it is strongly recommended to use C with coderefs. +=head2 Simple Transactions with DBIx::Class::Storage::TxnScopeGuard + +An easy way to use transactions is with +L. See L for an example. + +Note that unlike txn_do, TxnScopeGuard will only make sure the connection is +alive when issuing the C statement. It will not (and really can not) +retry if the server goes away mid-operations, unlike C. + =head1 SQL =head2 Creating Schemas From An Existing Database @@ -1625,10 +1644,10 @@ B Add the L schema component to your Schema class. This will add a new table to your database called C which will keep track of which version is installed -and warn if the user trys to run a newer schema version than the +and warn if the user tries to run a newer schema version than the database thinks it has. -Alternatively, you can send the conversion sql scripts to your +Alternatively, you can send the conversion SQL scripts to your customers as above. =head2 Setting quoting for the generated SQL @@ -1710,7 +1729,7 @@ methods: } ); -In conditions (eg. C<\%cond> in the L family of +In conditions (e.g. C<\%cond> in the L family of methods) you cannot directly use array references (since this is interpreted as a list of values to be Ced), but you can use the following syntax to force passing them as bind values: @@ -1728,6 +1747,75 @@ the bind values (the C<[1, 2, 3]> arrayref in the above example) wrapped in arrayrefs together with the column name, like this: C<< [column_name => value] >>. +=head2 Using Unicode + +When using unicode character data there are two alternatives - +either your database supports unicode characters (including setting +the utf8 flag on the returned string), or you need to encode/decode +data appropriately each time a string field is inserted into or +retrieved from the database. It is better to avoid +encoding/decoding data and to use your database's own unicode +capabilities if at all possible. + +The L component handles storing selected +unicode columns in a database that does not directly support +unicode. If used with a database that does correctly handle unicode +then strange and unexpected data corrupt B occur. + +The Catalyst Wiki Unicode page at +L +has additional information on the use of Unicode with Catalyst and +DBIx::Class. + +The following databases do correctly handle unicode data:- + +=head3 MySQL + +MySQL supports unicode, and will correctly flag utf8 data from the +database if the C is set in the connect options. + + my $schema = My::Schema->connection('dbi:mysql:dbname=test', + $user, $pass, + { mysql_enable_utf8 => 1} ); + + +When set, a data retrieved from a textual column type (char, +varchar, etc) will have the UTF-8 flag turned on if necessary. This +enables character semantics on that string. You will also need to +ensure that your database / table / column is configured to use +UTF8. See Chapter 10 of the mysql manual for details. + +See L for further details. + +=head3 Oracle + +Information about Oracle support for unicode can be found in +L. + +=head3 PostgreSQL + +PostgreSQL supports unicode if the character set is correctly set +at database creation time. Additionally the C +should be set to ensure unicode data is correctly marked. + + my $schema = My::Schema->connection('dbi:Pg:dbname=test', + $user, $pass, + { pg_enable_utf8 => 1} ); + +Further information can be found in L. + +=head3 SQLite + +SQLite version 3 and above natively use unicode internally. To +correctly mark unicode strings taken from the database, the +C flag should be set at connect time (in versions +of L prior to 1.27 this attribute was named +C). + + my $schema = My::Schema->connection('dbi:SQLite:/tmp/test.db', + '', '', + { sqlite_unicode => 1} ); + =head1 BOOTSTRAPPING/MIGRATING =head2 Easy migration from class-based to schema-based setup @@ -1825,12 +1913,27 @@ You can accomplish this by overriding C on your objects: sub insert { my ( $self, @args ) = @_; $self->next::method(@args); - $self->cds->new({})->fill_from_artist($self)->insert; + $self->create_related ('cds', \%initial_cd_data ); return $self; } -where C is a method you specify in C which sets -values in C based on the data in the C object you pass in. +If you want to wrap the two inserts in a transaction (for consistency, +an excellent idea), you can use the awesome +L: + + sub insert { + my ( $self, @args ) = @_; + + my $guard = $self->result_source->schema->txn_scope_guard; + + $self->next::method(@args); + $self->create_related ('cds', \%initial_cd_data ); + + $guard->commit; + + return $self + } + =head2 Wrapping/overloading a column accessor