~ubuntu-branches/ubuntu/precise/libtangram-perl/precise

« back to all changes in this revision

Viewing changes to tour

  • Committer: Bazaar Package Importer
  • Author(s): Stephen Zander
  • Date: 2001-09-21 10:20:34 UTC
  • Revision ID: james.westby@ubuntu.com-20010921102034-tohkprbnz4n1aua0
Tags: upstream-2.04
ImportĀ upstreamĀ versionĀ 2.04

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
use strict;
 
4
use lib '.';
 
5
use Tangram;
 
6
use Tangram::Springfield;
 
7
use Getopt::Std;
 
8
 
 
9
use DBI;
 
10
 
 
11
do {
 
12
  my %opt;
 
13
  getopts('pxtw', \%opt);
 
14
  
 
15
  $Tangram::TRACE = \*STDOUT if exists $opt{t};
 
16
  
 
17
  my @cp = qw( dbi:Pg:dbname=tour tangram tangram );
 
18
  my $cp = join(', ', map { "'$_'" } @cp);
 
19
  
 
20
  my ( $schema, $dbh, $storage, @kids, $marge, $homer, $homer_id,
 
21
       $ned_id, @sisters_id, $ned, @sisters, @pairs, $patty, $selma, $burns
 
22
     );
 
23
  
 
24
  if ($opt{w}) {
 
25
    $opt{p} = 1;
 
26
    open STDOUT, '>Tangram/Tour.pod';
 
27
  }
 
28
  
 
29
  my $tour = join '', <DATA>;
 
30
  
 
31
  if (exists $opt{p})
 
32
    {
 
33
      $tour =~ s/{{\n//gm;
 
34
                   $tour =~ s/}}\n//gm;
 
35
      print $tour;
 
36
      exit;
 
37
    }
 
38
  
 
39
  $tour =~ s[\@cp][$cp]g;
 
40
  
 
41
  if ($opt{x}) {
 
42
        system 'dropdb -q -U postgres tour 2>/dev/null';
 
43
        system 'createdb -q -U postgres tour';
 
44
 
 
45
    while ($tour =~ m[ {{ (.*?) }} ]sgx) {
 
46
      my $chunk = $1;
 
47
      my $show = $chunk;
 
48
      my $line = 1;
 
49
      $show =~ s/^/ sprintf "%03d: ", $line++/gem;
 
50
      print "executing:\n\n$show";
 
51
      eval "do { use strict; $chunk }";
 
52
    die $@ if $@;
 
53
    print "\n", '-' x 40, "\n\n";
 
54
    }
 
55
 
 
56
    %$homer = ();
 
57
    %$marge = ();
 
58
  }
 
59
};
 
60
 
 
61
print "finished!\n";
 
62
    
 
63
 
 
64
__END__
 
65
=head1 NAME
 
66
 
 
67
Tangram - Guided Tour
 
68
 
 
69
=head1 INTRODUCTION
 
70
 
 
71
In this tour, we add persistence to a simple Person design.
 
72
 
 
73
A Person is either a NaturalPerson or a LegalPerson. Persons (in
 
74
general) have a collection of addresses.
 
75
 
 
76
An address consists in a type (a string) and a city (also a string).
 
77
 
 
78
NaturalPerson - a subclass of Person - represents persons of flesh and
 
79
blood. NaturalPersons have a name and a firstName (both strings) and
 
80
an age (an integer). NaturalPersons sometimes have a partner (another
 
81
NaturalPerson) and even children (a collection of NaturalPersons).
 
82
 
 
83
LegalPerson - another subclass of Person - represents companies and
 
84
other entities that the law regards as 'persons'. A LegalPerson has a
 
85
name (a string) and a manager (a NaturalPerson).
 
86
 
 
87
All this is expressed in the following UML diagram:
 
88
 
 
89
 
 
90
                       +---------------------+        +--------------+ 
 
91
                       |       Person        |        |    Address   |
 
92
                       |     { abstract }    |1<>-->-*|--------------|
 
93
                       |---------------------|        | kind: string |
 
94
                       +---------------------+        | city: string |
 
95
                                   |                  +--------------+
 
96
                                   |
 
97
                    +--------------A--------------+
 
98
                    |                             |             
 
99
          +-------------------+           +---------------+          
 
100
      +--*|   NaturalPerson   |           |  LegalPerson  |        
 
101
      |   |-------------------|manager    |---------------|
 
102
      V   | firstName: string |1---<-----1| name: string  |        
 
103
      |   | name: string      |           +---------------+        
 
104
      +--*| age: integer      |
 
105
 children +-------------------+
 
106
                1       1 
 
107
                |    partner
 
108
                |       |
 
109
                +--->---+
 
110
 
 
111
B<Note that Tangram does I<not> create the corresponding Perl
 
112
packages!>. That's up to the user. However, to facilitate
 
113
experimentation, Tangram comes with a module that implements the
 
114
necessary classes. For more information see L<Tangram::Springfield>.
 
115
 
 
116
Before we can actually store objects we must complete two steps:
 
117
 
 
118
=over 4
 
119
 
 
120
=item 1
 
121
 
 
122
Create a Schema
 
123
 
 
124
=item 2
 
125
 
 
126
Create a database
 
127
 
 
128
=back
 
129
 
 
130
=head2 Creating a Schema
 
131
 
 
132
A Schema object contains information about the persistent
 
133
aspects of a system of classes.
 
134
 
 
135
It also gives a degree of control over the way Tangram performs the
 
136
object-relational mapping, but in this tour we will use all the defaults.
 
137
 
 
138
Here is the Schema for Springfield:
 
139
{{
 
140
 
 
141
   $schema = Tangram::Relational->schema( {
 
142
 
 
143
      classes => [
 
144
 
 
145
       Person => {
 
146
          abstract => 1,
 
147
      
 
148
          fields => {
 
149
              iarray => {
 
150
                 addresses => { class => 'Address', aggreg => 1 } }
 
151
          }
 
152
       },
 
153
 
 
154
      Address => {
 
155
         fields => {
 
156
            string => [ qw( kind city ) ],
 
157
         },
 
158
      },
 
159
 
 
160
      NaturalPerson => {
 
161
 
 
162
         bases => [ qw( Person ) ],
 
163
 
 
164
         fields => {
 
165
            string   => [ qw( firstName name ) ],
 
166
            int      => [ qw( age ) ],
 
167
            ref      => [ qw( partner ) ],
 
168
            array    => { children => 'NaturalPerson' },
 
169
         }
 
170
      },
 
171
 
 
172
      LegalPerson => {
 
173
         bases => [ qw( Person ) ],
 
174
 
 
175
         fields => {
 
176
            string   => [ qw( name ) ],
 
177
            ref      => [ qw( manager ) ],
 
178
            }
 
179
         },
 
180
   ] } );
 
181
}}
 
182
 
 
183
The Schema lists all the classes that need persistence, along with
 
184
their attributes and the inheritance relationships.  We must provide
 
185
type information for the attributes, because SQL is more typed than
 
186
Perl.  We also tell Tangram that C<Person> is an abstract class, so it
 
187
wastes no time attempting to retrieve objects of that exact class.
 
188
 
 
189
Note that Tangram cannot deduce this information by itself. While Perl
 
190
makes it possible to extract the list of all the classes in an
 
191
application, in general not all classes will need to persist. A class
 
192
may have both persistent and non-persistent bases.  As for attributes,
 
193
Perl's most typical representation for objects - a hash - even allows
 
194
two objects of the same class to have a different set of attributes.
 
195
 
 
196
For more information on creating Schemas, see L<Tangram::Relational>
 
197
and  L<Tangram::Schema>.
 
198
 
 
199
=head2 Setting up a database
 
200
 
 
201
Now we create a database. The simplest way is to create an
 
202
empty database and let Tangram initialize it:
 
203
{{
 
204
    use Tangram;
 
205
 
 
206
    $dbh = DBI->connect(
 
207
        @cp );  
 
208
 
 
209
    Tangram::Relational->deploy($schema, $dbh );
 
210
 
 
211
    $dbh->disconnect();
 
212
}}
 
213
 
 
214
Tangram::Relational is the vanilla object-relational backend. It
 
215
assumes that the database understands standard SQL, and that both the
 
216
database and the related DBI driver fully implements the DBI
 
217
specification.
 
218
 
 
219
Tangram also comes with vendor-specific backends for Mysql and
 
220
Sybase. When a vendor-specific backend exists, it should be used in
 
221
place of the vanilla backend.
 
222
 
 
223
For more information, see L<Tangram::Relational>, L<Tangram::Sybase>
 
224
and L<Tangram::mysql>.
 
225
 
 
226
=head2 Connecting to a database
 
227
 
 
228
We are now ready to store objects. First we connect to the database,
 
229
using the class method Tangram::Relational::connect (or
 
230
Tangram::mysql::connect for Mysql).
 
231
 
 
232
The first argument of connect() the schema object; the others are
 
233
passed directly to DBI::connect. The method returns a Tangram::Storage
 
234
object that will be used to communicate with the database.
 
235
 
 
236
For example:
 
237
 
 
238
{{
 
239
    $storage = Tangram::Relational->connect( $schema,
 
240
        @cp );
 
241
}}
 
242
 
 
243
connects to a database named Springfield via the vanilla Relational
 
244
backend, using a specific account and password.
 
245
 
 
246
For more information on connecting to databases, see  L<Tangram::Relational> and
 
247
L<Tangram::Storage>.
 
248
 
 
249
=head2 Inserting objects
 
250
 
 
251
Now we can populate the database:
 
252
 
 
253
{{
 
254
   $storage->insert( NaturalPerson->new(
 
255
      firstName => 'Montgomery', name => 'Burns' ) );
 
256
}}
 
257
 
 
258
This inserts a single NaturalPerson object into the database. We can
 
259
insert several objects in one call:
 
260
 
 
261
{{
 
262
   $storage->insert(
 
263
      NaturalPerson->new( firstName => 'Patty', name => 'Bouvier' ),
 
264
      NaturalPerson->new( firstName => 'Selma', name => 'Bouvier' ) );
 
265
}}
 
266
 
 
267
Sometimes Tangram saves objects implicitly:
 
268
 
 
269
{{
 
270
    @kids = (
 
271
        NaturalPerson->new( firstName => 'Bart', name => 'Simpson' ),
 
272
        NaturalPerson->new( firstName => 'Lisa', name => 'Simpson' ) );
 
273
 
 
274
    $marge = NaturalPerson->new(
 
275
        firstName => 'Marge', name => 'Simpson',
 
276
        addresses => [
 
277
            Address->new(
 
278
                kind => 'residence', city => 'Springfield' ) ],
 
279
        children => [ @kids ] );
 
280
 
 
281
    $homer = NaturalPerson->new( firstName => 'Homer', name => 'Simpson',
 
282
        addresses => [
 
283
            Address->new(
 
284
                kind => 'residence', city => 'Springfield' ),
 
285
            Address->new(
 
286
                kind => 'work', city => 'Springfield' ) ],
 
287
        children => [ @kids ] );
 
288
 
 
289
    $homer->{partner} = $marge;
 
290
    $marge->{partner} = $homer;
 
291
   
 
292
    $homer_id = $storage->insert( $homer );
 
293
}}
 
294
 
 
295
In the process of saving Homer, Tangram detects that it contains
 
296
references to objects that are not persistent yet (Marge, the
 
297
addresses and the kids), and inserts them automatically. Note that
 
298
Tangram can handle cycles: Homer and Marge refer to each other.
 
299
 
 
300
insert() returns an object id, or a list of object ids, that uniquely
 
301
identify the object(s) that have been inserted.
 
302
 
 
303
For more information on inserting objects, see L<Tangram::Storage>.
 
304
 
 
305
=head2 Updating objects
 
306
 
 
307
Updating works pretty much the same as inserting:
 
308
 
 
309
{{
 
310
    my $maggie = NaturalPerson->new(
 
311
      firstName => 'Maggie', name => 'Simpson' );
 
312
 
 
313
    push @{ $homer->{children} }, $maggie;
 
314
    push @{ $marge->{children} }, $maggie;
 
315
 
 
316
    $storage->update( $homer, $marge );
 
317
}}
 
318
 
 
319
Here again Tangram detects that Maggie is not already persistent in
 
320
$storage and automatically inserts it. Note that we need to update
 
321
Marge explicitly because she was already persistent.
 
322
 
 
323
For more information on updating objects, see L<Tangram::Storage>.
 
324
 
 
325
=head2 Memory management
 
326
 
 
327
...is still up to you. Tangram won't break in-memory cycles, it's a
 
328
persistence tool, not a memory management tool. Let's make sure we
 
329
don't leak objects:
 
330
 
 
331
{{
 
332
   $homer->{partner} = undef; # do this before $homer goes out of scope
 
333
}}
 
334
 
 
335
Also, when we're finished with a storage, we can explicitly disconnect it:
 
336
 
 
337
{{
 
338
   $storage->disconnect();
 
339
}}
 
340
 
 
341
Whether it's important or not to disconnect the Storage depends on
 
342
what version of Perl you use. If it's prior to 5.6, you I<must>
 
343
disconnect the storage explicitly (or at least call unload())
 
344
otherwise the Storage will prevent the objects it controls from being
 
345
reclaimed by Perl. For more information see see L<Tangram::Storage>.
 
346
 
 
347
=head2 Finding objects
 
348
 
 
349
After reconnecting to Springfield, we now want to retrieve some objects.
 
350
But how do we find them? Basically there are three options
 
351
 
 
352
=over 4
 
353
 
 
354
=item *
 
355
 
 
356
We know their IDs.
 
357
 
 
358
=item *
 
359
 
 
360
We obtain them from another object.
 
361
 
 
362
=item *
 
363
 
 
364
We use a query.
 
365
 
 
366
=back
 
367
 
 
368
=head2 Loading by ID
 
369
 
 
370
When an object is inserted, Tangram assigns an identifier to it.
 
371
IDs are numbers that uniquely identify objects in the database.
 
372
C<insert> returns the ID(s) of the object(s) it was passed:
 
373
 
 
374
{{
 
375
    $storage = Tangram::Relational->connect( $schema,
 
376
        @cp );
 
377
 
 
378
    $ned_id = $storage->insert( NaturalPerson->new(
 
379
        firstNname => 'Ned', name => 'Flanders' ) );
 
380
 
 
381
    @sisters_id = $storage->insert(
 
382
        NaturalPerson->new( firstName => 'Patty', name => 'Bouvier' ),
 
383
        NaturalPerson->new( firstName => 'Selma', name => 'Bouvier' ) );
 
384
}}
 
385
 
 
386
This enables us to retrieve the objects:
 
387
 
 
388
{{
 
389
    $ned = $storage->load( $ned_id );
 
390
    @sisters = $storage->load( @sisters_id );
 
391
}}
 
392
 
 
393
For more information on loading objects by id, see L<Tangram::Storage>.
 
394
 
 
395
=head2 Obtaining objects from other objects
 
396
 
 
397
Once Homer has been restored to his previous state, including his relations
 
398
with his family. Thus we can say:
 
399
 
 
400
{{
 
401
    $storage = Tangram::Relational->connect( $schema,
 
402
        @cp );
 
403
 
 
404
    $homer = $storage->load( $homer_id ); # load by id
 
405
 
 
406
    $marge = $homer->{partner};
 
407
    @kids = @{ $homer->{children} };
 
408
}}
 
409
 
 
410
Actually, when Tangram loads an object that contains references to
 
411
other persistent objects, it doesn't retrieve the referenced objects
 
412
immediately. Marge is retrieved only when Homer's 'partner' field is
 
413
accessed.  This mechanism is almost totally transparent, we'd have to
 
414
use C<tied> to observe a non-present collection or reference.
 
415
 
 
416
For more information on relationships, see L<Tangram::Schema>,
 
417
L<Tangram::Ref>, L<Tangram::Array>, L<Tangram::IntrArray>,
 
418
L<Tangram::Set> and L<Tangram::IntrSet>.
 
419
 
 
420
=head2 select
 
421
 
 
422
To retrieve all the objects of a given class, we use C<select>:
 
423
 
 
424
{{
 
425
    $storage = Tangram::Relational->connect( $schema,
 
426
        @cp );
 
427
 
 
428
    my @people = $storage->select( 'NaturalPerson' );
 
429
}}
 
430
 
 
431
Tangram supports polymorphic retrieval. Let's first insert a
 
432
LegalPerson:
 
433
 
 
434
{{
 
435
    $storage->insert( LegalPerson->new(
 
436
        name => 'Springfield Nuclear Power Plant', manager => $burns ) );
 
437
}}
 
438
 
 
439
 
 
440
Now we can retrieve all the Persons - Natural or Legal - by making a
 
441
single call to select(), passing it the base class name:
 
442
 
 
443
{{
 
444
    my @all = $storage->select( 'Person' );
 
445
}}
 
446
 
 
447
For more information on select(), see L<Tangram::Storage>.
 
448
 
 
449
=head2 Filtering
 
450
 
 
451
Usually we won't want to load I<all> the NaturalPersons, only those
 
452
objects that satisfy some condition. Say, for example, that we want to
 
453
load only the NaturalPersons whose name field is 'Simpson'. Here's how
 
454
this can be done:
 
455
 
 
456
{{
 
457
    my $person = $storage->remote( 'NaturalPerson' );
 
458
    my @simpsons = $storage->select( $person, $person->{name} eq 'Simpson' );
 
459
}}
 
460
 
 
461
This will bring in memory only the Simpsons; Burns or the Bouvier
 
462
sisters won't turn up.  The filtering happens on the database server
 
463
side, not in Perl space. Internally, Tangram translates the
 
464
C<$person->{name} eq 'Simpson'> clause into a piece of SQL code that
 
465
is passed down to the database.
 
466
 
 
467
The above example only begins to scratch the surface of Tangram's
 
468
filtering capabilities. The following examples are all legal and working code:
 
469
 
 
470
{{
 
471
    # find all the persons *not* named Simpson
 
472
 
 
473
    my $person = $storage->remote( 'NaturalPerson' );
 
474
    my @others = $storage->select( $person, $person->{name} ne 'Simpson' );
 
475
 
 
476
    # same thing in a different way
 
477
 
 
478
    my $person = $storage->remote( 'NaturalPerson' );
 
479
    my @others = $storage->select( $person, !($person->{name} eq 'Simpson') );
 
480
 
 
481
    # find all the persons who are older than me
 
482
 
 
483
    my $person = $storage->remote( 'NaturalPerson' );
 
484
    my @elders = $storage->select( $person, $person->{age} > 35 );
 
485
 
 
486
    # find all the Simpsons older than me
 
487
 
 
488
    my $person = $storage->remote( 'NaturalPerson' );
 
489
    my @simpsons = $storage->select( $person,
 
490
        $person->{name} eq 'Simpson' & $person->{age} > 35 );
 
491
 
 
492
    # find Homer's wife - note that select *must* be called in list context
 
493
 
 
494
    my ($person1, $person2) = $storage->remote(
 
495
        qw( NaturalPerson NaturalPerson ));
 
496
 
 
497
    my ($marge) = $storage->select( $person1,
 
498
        $person1->{partner} == $person2
 
499
        & $person2->{firstName} eq 'Homer' & $person2->{name} eq 'Simpson' );
 
500
 
 
501
    # find Homer's wife - this time Homer is already in memory
 
502
 
 
503
    my $homer = $storage->load( $homer_id );
 
504
    my $person = $storage->remote( 'NaturalPerson' );
 
505
 
 
506
    my ($marge) = $storage->select( $person,
 
507
        $person->{partner} == $homer );
 
508
 
 
509
    # find everybody who works in Springfield
 
510
 
 
511
    my $address = $storage->remote( 'Address' );
 
512
 
 
513
    my @population = $storage->select( $person,
 
514
        $person->{addresses}->includes( $address )
 
515
        & $address->{kind} eq 'work'
 
516
        & $address->{city} eq 'Springfield');
 
517
 
 
518
    # find the parents of Bart Simpson
 
519
 
 
520
    my ($person1, $person2) = $storage->remote(
 
521
        qw( NaturalPerson NaturalPerson ));
 
522
 
 
523
    my @parents = $storage->select( $person1,
 
524
        $person1->{children}->includes( $person2 )
 
525
           & $person2->{firstName} eq 'Bart'
 
526
           & $person2->{name} eq 'Simpson' );
 
527
 
 
528
    # load Bart
 
529
    my ($bart) = $storage->select( $person1, $person1->{firstName} eq 'Bart');
 
530
 
 
531
    # find the parents of Bart, this time given an object already loaded
 
532
    my $person = $storage->remote( 'NaturalPerson' );
 
533
 
 
534
    @parents = $storage->select( $person,
 
535
        $person->{children}->includes( $bart ) );
 
536
}}
 
537
 
 
538
Note that Tangram uses a single ampersand (&) or vertical bar (|) to
 
539
represent logical conjunction or disjunction, not the usual && or
 
540
||. This is due to a limitation in Perl's operator overloading
 
541
mechanism. Make sure you never forget this, because, unfortunately,
 
542
using && or || in place of & or | is not even a syntax error :(
 
543
 
 
544
Finally, Tangram make it possible to retrieve tuples of related objects:
 
545
 
 
546
{{
 
547
    my ($parent, $child) = $storage->remote('NaturalPerson', 'NaturalPerson');
 
548
 
 
549
    @pairs = $storage->select( [ $parent, $child ],
 
550
        $parent->{children}->includes($child) );
 
551
}}
 
552
 
 
553
@pairs contains a list of references to arrays of size two; each array
 
554
contains a pair of parent and child.
 
555
 
 
556
For more information on filters, see L<Tangram::Expr> and L<Tangram::Remote>.
 
557
 
 
558
=head2 Cursors
 
559
 
 
560
Cursors provide a way of retrieving objects one at a time.  This is
 
561
important is the result set is potentially large.  cursor() takes the
 
562
same arguments as select() and returns a Cursor objects that can be
 
563
used to iterate over the result set via methods current() and next():
 
564
 
 
565
{{
 
566
    $storage = Tangram::Relational->connect( $schema,
 
567
        @cp );
 
568
 
 
569
    # iterate over all the NaturalPersons in storage
 
570
 
 
571
    my $cursor = $storage->cursor( 'NaturalPerson' );
 
572
 
 
573
    while (my $person = $cursor->current())
 
574
    {
 
575
        # process $person
 
576
        $cursor->next();
 
577
    }
 
578
 
 
579
    $cursor->close();
 
580
}}
 
581
 
 
582
The Cursor will be automatically closed when $cursor is garbage-collected,
 
583
but Perl doesn't define just when that may happen :( Thus it's a good idea to
 
584
explicitly close the cursor.
 
585
 
 
586
Each Cursor uses a separate connection to the database. Consequently you can
 
587
have several cursors open at the same, all with pending results. Of course,
 
588
mixing reads and writes to the same tables can result in deadlocks.
 
589
 
 
590
For more information on cursors, see L<Tangram::Storage> and
 
591
L<Tangram::Cursor>.
 
592
 
 
593
=head2 Remote objects
 
594
 
 
595
At this point, most people wonder what $person I<exactly> is and how
 
596
it all works.  This section attempts to give an idea of the mechanisms
 
597
that are used.
 
598
 
 
599
In Tangram terminology, $person a I<remote> object. Its Perl class is
 
600
Tangram::Remote, but it's really a placeholder for an object of class
 
601
C<NaturalPerson> I<in the database>, much like a table alias in
 
602
SQL-speak.
 
603
 
 
604
When you request a remote object of a given class, Tangram arranges
 
605
that the remote object I<looks like> an object of the said class. It
 
606
I<seems> to have the same fields as a regular object, but don't be
 
607
misled, it's not the real thing, it's just a way of providing a nice
 
608
syntax.
 
609
 
 
610
If you dig it, you'll find out that a Remote is just a hash of
 
611
Tangram::Expr objects.  When you say $homer->{name}, an Expr is
 
612
returned, which, most of the time, can be used like any ordinary Perl
 
613
scalar. However, an Expr represents a value I<in the database>, it's
 
614
the equivalent of Remote, only for expressions, not for objects.
 
615
 
 
616
Expr objects that represent scalar values (e.g. ints, floats, strings)
 
617
can be compared between them, or compared with straight Perl
 
618
scalars. Reference-like Exprs can be compared between themselves and
 
619
with references
 
620
 
 
621
Expr objects that represent collections have an C<include> methods
 
622
that take a persistent object, a Remote object or an ID.
 
623
 
 
624
The result of comparing Exprs (or calling C<include>) is a
 
625
Tangram::Filter that will translate into part of the SQL where-clause
 
626
that will be passed to the RDBMS.
 
627
 
 
628
For more information on remote objects, see L<Tangram::Remote>.
 
629
 
 
630
=head2 Multiple loads
 
631
 
 
632
What happens when we load the same object twice? Consider:
 
633
 
 
634
{{
 
635
    my $person = $storage->remote( 'NaturalPerson' );
 
636
    my @simpsons = $storage->select( $person, $person->{name} eq 'Simpson' );
 
637
 
 
638
    my @people = $storage->select( 'NaturalPerson' );
 
639
}}
 
640
 
 
641
Obviously Homer Simpson will be retrieved by both selects. Are there
 
642
two Homers in memory now? Fortunately not. There is only one copy of
 
643
Homer in memory. When Tangram load an object, it checks whether an
 
644
object with the same ID is alredy present. If yes, it keeps the old
 
645
copy, which is desirable, since we may have changed it already.
 
646
 
 
647
Incidentally, this explains why a Storage will hold objects in memory
 
648
- until disconnected (again, this will change when Perl supports weak
 
649
references).
 
650
 
 
651
=head2 Transactions
 
652
 
 
653
Tangram wraps database transactions in a object-oriented interface:
 
654
 
 
655
    $storage->tx_start();
 
656
    $homer->{partner} = $marge;
 
657
    $marge->{partner} = $homer;
 
658
    $storage->update( $homer, $marge );
 
659
    $storage->tx_commit();
 
660
 
 
661
Both Marge and Homer will be updated, or none will. tx_rollback() drops
 
662
the changes.
 
663
 
 
664
Tangram does not emulate transactions for databases that do not
 
665
support them (like earlier versions of mySql).
 
666
 
 
667
Unlike DBI, Tangram allows the nested transactions:
 
668
 
 
669
    $storage->tx_start();
 
670
 
 
671
    {
 
672
        $storage->tx_start();
 
673
        $patty->{partner} = $selma;
 
674
        $selma->{partner} = $patty;
 
675
        $storage->tx_commit();
 
676
    }
 
677
 
 
678
    $homer->{partner} = $marge;
 
679
    $marge->{partner} = $homer;
 
680
    $storage->update( $homer, $marge );
 
681
 
 
682
    $storage->tx_commit();
 
683
 
 
684
Tangram uses a single database transaction, but commits it only when
 
685
the tx_commit()s exactly balance the tx_start()s. Thanks to this
 
686
feature any piece of code can open all the transactions it needs and
 
687
still cooperate smoothly with the rest of the application.  If a DBI
 
688
transaction is already active, it will be reused; otherwise a new one
 
689
will be started.
 
690
 
 
691
Tangram offer a more robust alternative to the start/commit code
 
692
sandwich.  tx_do() calls CODEREF in a transaction. If the CODEREF
 
693
dies, the transaction is rolled back; otherwise it's committed.  The
 
694
first example can be rewritten:
 
695
 
 
696
    $storage->tx_do( sub {
 
697
        $homer->{partner} = $marge;
 
698
        $marge->{partner} = $homer;
 
699
        $storage->update( $homer, $marge };
 
700
        } );
 
701
 
 
702
For more information on transactions, see L<Tangram::Storage>.
 
703
 
 
704
=cut