Normalmente para instanciar un objeto se recupera la información de la tabla relacional que le sirve como backstore usando la llave primaria.
Cada vez que llega un requerimiento instanciamos todos los objetos que se necesitan para satisfacerlo, es decir leemos las tablas de la base de datos que contienen el backstore de cada objeto que instanciemos.
De este modo en cada requerimiento el modelo de objetos activo refleja la información almacenada en la base de datos desde la última actualización (commit).
Llamaremos a esto "materialización on request", es decir los objetos son materializados fresquitos a partir de información en la base de datos en cada request.
package Comuna
sub new {
my ($class, $id_comuna) = @_;
return undef unless $id_comuna =~ /^\d+$/;
my $sth = $dbh->prepare("select * from comuna where id_comuna = $id_comuna");
$sth->execute;
if (my $self = $sth->fetchrow_hashref) {
return bless $self, $class;
} else {
return undef;
}
}
# Al pedir una instancia lo hacemos de la siguiente forma:
if (my $c = new Comuna($id_comuna)) {
print $c->nombre, "\n";
} else {
print "[No se pudo recuperar la comuna]";
}
...
package Comuna;
#
# Hay un módulo que importa un objeto database handler para acceder a la base de datos
#
use General qw/$dbh/;
use strict;
#
# Este hash contiene las instancias de objetos comunas, será indexado por el id de comuna para obtener una instancia.
# se va cargando a medida que se producen materializaciones producto de las instanciaciones.
#
our %CACHE;
#
# Nuestro rutina de instanciación (método estático de clase), recibe un id de comuna y retorna la instancia correspondiente a ese id.
# En cualquier caso de no poder materializar retorna undef (falso).
#
sub new {
my ($class, $id_comuna) = @_;
return undef unless $id_comuna =~ /^\d+$/;
#
# Si la comuna entregada no ha sido materializada hay que materializar a partir de la base de datos e incorporar al cache
#
unless ($CACHE{$id_comuna}) {
my $sth = $dbh->prepare("select * from comuna where id_comuna = $id_comuna");
$sth->execute;
my $self = $sth->fetchrow_hashref;
return undef unless $self;
bless $self, $class;
#
# Incorporamos el objeto comuna recién instanciado al cache
#
$CACHE{$id_comuna} = $self;
}
#
# Siempre retornamos del cache
#
return $CACHE{$id_comuna};
}
Hay que considerar que para un número muy elevado de instancias puede llegar a usarse gran cantidad de memoria, en algunos casos debe ser balanceado con MaxRequestsPerChild, debido a que eventualmente todos los objetos terminarán en el Cache.
En mod_perl, podemos materializar todo el cache completo al cargar el módulo, usando PerlModule, de ese modo las semánticas de copy on write del sistema operativo permitiran compartir la memoria con los procesos Apache hijos. Este es el ejemplo:
our %CACHE;
my $sth = $dbh->prepare("select * from comuna");
$sth->execute;
while (my $row = $sth->fetchrow_hashref) {
$CACHE{$row->{id_comuna}} = bless $row, "Comuna";
}
Luego de la definición de la variable %CACHE, leemos todos los registros de la base de datos e inicializamos todo el cache. Esto se ejecutará al cargar por primera vez el módulo.
Esta prematerialización es conveniente cuando sabemos que el número de instancias es finitas y tenemos la memoria suficiente para almecenarlas.
Se puede usar alguno de los módulos de shared memory de perl
y
colocar ahí el hash que guarda el cache; eventualmente hacer
esto usando PerlModule como en el caso anterior, permite asegurar la
materialización pensando que copy on write puede eventualmente
efectuar copias en algún momento.
| Logramos que cada objeto se instancie todas las veces que quiera, pero que sólo se materialize una vez en la vida de cada proceso apache. |