liens sur la Javadoc
Les objets du graphe d'objets qui euvent être persistés sont
des POJO qui ne sont pas nécessairement sérialisables, mais il
y a
des conditions:
Il faut considérer l'utilisation des l'implémentation JOAFIP des collections pour de meilleurs performances (implémente le standard Sun et plus)
Pour pouvoir utiliser JOAFIP il est nécessaire de comprendre l'état de persistance
des objets
Notes:
après fermeture de la session d'accès aux données, les objets persistents deviennent detachés, alots que les transient and detachés reste dans leur état. Un appel à getObject() dans une session d'accès aux données nouvellement ouverte permet d'obtenir une référence sur un objet persisté.
Pour les objets qui ne
sont pas
sauvegardés sérialisé dans un seul enregistrement, il n'est pas
possible d'utiliser un objet persisté quand la session d'accès aux
données est fermée, il est nécessaire de faire une copie de l'objet et
ensuite de fermer la session puis de travailler avec la copie.
IFilePerstence#copy(Object
object) permet de faire une copie détaché de
la persistance.
une configuration plus aisée peut être faite via setup facility
Ci-après les utilisations de base sont decrites.
voir aussi "setup facility" ou le javadoc du builder
/*
creation of
the file persitence manager */
final
FilePersistenceBuilder
builder = new FilePersistenceBuilder();
builder.setPathName("runtime/database");
builder.setProxyMode(true);
builder.setRemoveFiles(true);
builder.setGarbageManagement(false);
builder.setCrashSafeMode(false);
builder.setFileCache(PAGE_SIZE, NUMBER_OF_PAGE);
IFilePersistence
filePersistence;
try {
filePersistence
= builder.build();
} catch
(FilePersistenceException exception) {
final
EnumFileState fileState = FilePersistence.fileState(exception);
switch(fileState) {
case STATE_OK:
/* exception is not relative to file
state */
throw exception;
case
STATE_CORRUPTED:
/*
file content corrupted */
throw exception;
case
STATE_RESTORED_DATA_LOST:
/* create file persistence using file restored even data lost */
filePersistence
= new
FilePersistence(directory);
break;
case
STATE_RESTORED_NO_DATA_LOST:
/* create file persistence using file restored */
filePersistence
= builder.build();
break;
case
STATE_UNSTABLE:
/* need
file maintenance */
throw exception;
}
}
/*
start data access session */
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
persistentObject
= session.getObject("name");
if( persistentObject
== null ) {
/* the first time creation */
persistentObject
= new
PersistentObject(...)
session.setObject("name", persistentObject);
}
........
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);
/*
start data access session */
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
......
/*
get the object in memory */
persistentObject
= session.getObject("name");
/*
use of persitent object */
persistentObject.setXxx(...);
......
/*
close data access session , saving modifications, waiting save
done*/
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);
/*
start data access session */
IExclusiveDataAccessSession session=filePersistence.createExclusiveDataAccessSession();
session.open();
......
/*
get the object in memory */
persistentObject
= session.getObject("name");
/*
use of persitent object */
persistentObject.setXxx(...);
......
/* save (do not
close) */
session.save();
......
/* some other operation */
......
/* close data access
session , saving modifications */
session.close();
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
..........
/*
remove object from the persitente objet pool
*/
session.removeObject("name");
..........
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);
/*
start data access session */
IExclusiveDataAccessSession session=filePersistence.createExclusiveDataAccessSession();
session.open();
......
/*
remove object from the persitente objet pool
*/
session.removeObject("name");
..........
/* save (do not
close) */
session.save();
......
/* some other operation */
......
/* close data access session , saving
modifications */
session.close();
ce protéger des modifications des objets n'est pas possible avec une session d'accès aux données "exclusive"
/*
start data
access session */
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
......
/*
get the object in memory */
persistentObject
= session.getObject("name");
/*
use of persitent object */
persistentObject.getXxx(...);
......
/*
no save action */
session.closeAndWait(EnumFilePersistenceCloseAction.DO_NOT_SAVE);
n'est pas possible avec une session d'accès aux données "exclusive"
/*
start data
access session */
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
......
/*
get the object in memory */
persistentObject
= session.getObject("name");
/*
use of persitent object */
persistentObject.setXxx(...);
......
/*
close data access session, do not save modifications
*/
session.closeAndWait(EnumFilePersistenceCloseAction.DO_NOT_SAVE);
/*
start a new data access session */
session.open();
/*
persitentObject have the previous state saved on file
*/
persistentObject = session.getObject("name");
Il est possible de déclaré des champs dans les enum, donc l'état de chaque constantes enum doi être sauvegardé. voir IFilePersistence#storedMutableEnum, IFilePersistence#storedImmutableEnum ou configuration via le fichier de propriétés. storedMutableEnum and storedImmutableEnum sont accessible via l'interface IDataAccessSession.
IFilePersistence#storedEnum est obselète.
/* must
inform file persistence manager of enum class stored
* see
also setup facility for stored
enum
declaration
*/
filePersistence.storedMutableEnum(EnumForTest.class)
final
BobContainer bob1 = new
BobContainer();
bob1.setObject1(EnumForTest.VAL1);
EnumForTest.VAL1.setObject(Integer.valueOf(0));
/*
save the created object*/
session.open();
/* inform that
EnumForTest class instance are persisted */
session.setObject("bob1",
bob1);
session.close(EnumFilePersistenceCloseAction.SAVE);
session.open(); /* enum state is
automaticaly set from file */
/*
deprecated: reload enum state */
/* session.setEnumState(EnumForTest.VAL1);*/
il est possible de seréialiser un objet dans une seul enregistrement, trois façons de faire cela:
annotation de la classe avec:
StoreSerializeAndZippedInOneRecord (les objets sont toujours réecrit même si l'état n'a pas changé)
en configurant le gestionnaire de persistance en invoquant l'une des méthodes de IFilePersistence
setStoreSerializeAndGZippedInOneRecord
setStoreSerializeAndZippedInOneRecord (les objets sont toujours réecrit même si l'état n'a pas changé)
setStoreSerializeInOneRecord
utilisation de la configuration via fichier de propriétés
Effet de bords de la sérialisation dans une seule enregistrement.
l'instance
o1
reference o3 et o4, o2 reference o4 et o5.
En
faiant session.setObject("key1",o1) , session.setObject("key2",o2)
pour sauvegarder les objets o1 et o2 serialisés dans une
enregistrement.
Les objets
o1 et o2
sont stockés serialisés comme ci-dessous dans deux
enregistrements.
Relisant o1 et o2 en
utilisant o1=session.getObject("key1") ,
et o2=session.getObject("key2")
Après lecture de o1 et
o2, il y aura 6 objets en mémoire: o4' et o4'' ont le même
état
que l'objet o4 originel, mais ne sont pas deux même instance
en
mémoire.
Depuis
la
version 2.0.0,
le stockage de joafip dans un enregistrement dans un fichier peut être
fait autravers du mecanisme standard de serialisation de java. Cela
signifie que joafip puet invoquer les méthodes readObject
et writeObject des classes implémentant
l'interface Serializable, mais aussi
invoquer les méthodes readExternal et
writeExternal des classes impémentant
l'interface Externalizable.
Pour les classe implémentant l'interface Serializable
mais n'implémentant pas les méthodes readObject
et writeObject,
joafip utilise la lecture et ecriture par defaut des objets.
Invoquer setStoreNotUseStandardSerialization de IFilePersistence
desactive l'utilisation du mécanisme de sérialisation standard de java
( ou utiliser l'annotation
StoreNotUseStandardSerialization)
La persistance est thread safe et permet d'ouvrir de multiples sessions d'accès aux données en même temps en mode asynchrone. Il est aussi possible d'avoir un accès synchrone.
Comme
les objets
accédés au travers de la persistance, il n'y a pas de concept de
transaction comme pour les bases de données relationnelle, les mêmes
objets sont partagés.
Le modèle de données doit être thread-safe.
La
seule chose spécifique de JOAFIP est la façon de fermer et
sauvegarder quand plusieurs sessions d'accès aux données sont ouvertes.
En fait la sauvegarde ne se fait que quand la dernière session ouverte
est fermée.
Attention: en environement multi-tâche peut se
produire la
situation où il n'y a jamais toutes les sessions fermées en même temps.
La méthode
IDataAccessSession.closeAndWait(...) peut être utilisé pour attendre
que la sauvegarde soit faite, ou un observateur (listener) de la
session peut être utilsié pour être notifié de la fin de l'operation de
sauvegarde.
La session est ouverte en
utilisant IDataAccessSession.open() ; qui ouvre en
mode asynchrone.
Au sujet rollback:
Il est possible de ne pas sauvegarder les modifications faites, en fermant avec l'option "ne pas sauvegarder", cela annulera les demandes de sauvegardes demandé lors de la fermeture des autres sessions. La méthode close et le "listener" permet de savoir si l'operation demandée a été réalisée ou non.
En utilisant IDataAccessSession.openSynchronized, qui est le l'ouverture en mode synchrone, aucune session d'accès aux données ne partage les objets avec une autre session.
un seul enregistrement
dans le "tas fichier" par instance d'objet.
exemple d'objets en relation
Sauvegarder object1 créra 4 enregistrements
pour object1, object2, object3, et object4
Rend possible de gérer une
grande quantité d'objets sans nécessité de tout recharger en mémoire..
Dans l'exemple ci-dessous on peut imaginer que la map contient quelques
milliers d'enregistrements.
/*
start data access session */
IDataAccessSession
session =
filePersistence.createDataAccessSession();
session.open();
/*
get the map to update */
PTreeMap<String,ClassData>
map=(PTreeMap<String,ClassData>)session.getObject("map1");
/*
do modification */
map.put("key1",data1);
/*
close data access session, saving modifications
*/
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);
L'appel de getObject ne lira pas toues les enregistrements du
fichier.
L'appel de put ne chargera que les noeuds de l'arbre nécessaire pour la
lecture et la modification pour insertion.
L'appel
de close fera un commit seulement des objets chargés
et modifiés,
et des objets ajoutés. Si des objets sont déférencés par d'autres
objets ( sujets au nettoyage ) alors ils seront supprimés du fichier en
laissant un emplacement libre pour un future usage.
Il est possible de forcer l'enrichissement par un proxy d'une
classe même si elle a une méthode "public final" method, ou de
forcer la non utilisation du lazy load:
voir l'interafce IFilePersistence ou les facilités de configuration.
Il y a un système de
réplication des données dans un deuxième fichier There is a system of
replication of
data with altered file reconstruction to be crash tolerant.
The files are never maintained always opened during the
application live.
File is marked "unstable" before
modification and marked "stable" after writing and closing it.
At
start-up the files state are checked and if one is not in stable
state it is restored.
This guarantees that the last
successful commit in file is not lost.
FilePersistence
filePersistence = builder.build();
......
try
{
} catch(FilePersistenceException exception) {
switch (.fileState(exception)) {
case STATE_RESTORED_DATA_LOST:
/* restoration of data from backup: data lost */
case STATE_RESTORED_NO_DATA_LOST:
/* restoration of backup from data: no data lost */
}
}
La gestion de l'état de "statbilité" du fichier est decrite ci-dessous.
Chaque enregistrement dans
le "tas fichier" à une clé de contrôle CRC32.
A la lecture du fichier une exception FileCorruptedException
est levée si le CRC32 lu n'est pas celui calculé.
FilePersistence
filePersistence = builder.build();
......
try {
} catch(FilePersistenceException exception) {
switch (.fileState(exception)) {
case STATE_CORRUPTED:
/* cause is file corruption */
................
}
}
Des vérifications additionnelles
peuvent être ajoutées en utilisant l'annotation AssertNotNull
sur
des champs de classes persistées qui ne peuvent prendre la valeur null.
Chauqe fois qu'un objet
est ajouté au graphe d'objet un enregistrement est ajouté dans le
fichier.
Quand un objet est détaché du graphe d'objet l'enregistrement associé
reste dans le fichier mais ne sera plus jamais accédé.
Le nettoyeur d'enregistrement détaché (garbage
collector) est nécessaire pour libérer la place utilisé par
ces enregistrements dtachés,
l'espace libre sera utilisé pour y placer de nouvel enregistrements.
Nettoyage rapide: quand il n'y a plus d'objets persistés une
nettoyage rapide est fait en effaçant le fichier et en créer un nouveau
vide.
Deux façons de faire le nettoyages:
grosse maintenance:
En appellant FilePersistence#garbageSweep
Cela peut prendre beaucoup de temps
En tâche de fond:
Le nettoyage en tâche de fond est désactivé par défaut. L'activation
prend une fréquence en paramètre.
Laisser
l'application s'exécuter continuellement même s'il n'y a pas d'accés
aux données permet le nettoyage en tâche de fond de se faire.
FilePersistence
filePersistence = builder.build();
......
/* this
can take a long time and block all other operation */
int
numberOfSweeped=filePersistence.garbageSweep();
FilePersistence
filePersistence
= builder.build();
......
/* enable
background garbage sweep, one pass every 100 miliSeconds
*/
filePersistence.enableBackgroundGarbageSweep(100);
......
/*
disable background garbage sweep
*/
filePersistence.disableBackgroundGarbageSweep()
voir aussi setup properties
des instance de classes annotatées DeprecatedInStoreClass peuvent exister stockées dans le fichier mais ne pourront plus être ajoutées
Un element du programme annoté Fortest est utilisé à des fins de test, ne doit pas être utilisé par l'application
Les instance de classes annotatées NotStorableClass ne peuvent être stockées dans le fichier, cela concerne tous les gestionnaire et entités utilisés pour la gestion de la persistance
Les instance des
classes annotatées StorableClass peuvent être
stockées dans le fichier,
voir FilePersistence#setStoreOnlyMarkedStorable
La méthode setStorable de IFilePersistence peut aussi être utilisée.
Pour marquer une type pour lequel le "lazy load" ne sera pas
utilisé
Les méthodes setNoLazyLoad de IFilePersistence peuvent aussi être
utilisées.
Pour marquer une classe serialisable pour que ses instances
soient
stockées sans passer par le mécanisme standard de sérialisation de java.
Les méthodes setStoreNotUseStandardSerialization de IFilePersistence
peuvent aussi être utilisées.
Pour marquer une classe serialisable pour que ses instances
soient stockées compressées gzip dans un enregistrement.
les méthodes setStoreSerializeAndGZippedInOneRecord de
IFilePersistence peuvent aussi être utilisées.
Pour marquer une classe serialisable pour que ses instances
soient
stockées compressées zip dans un enregistrement. Le niveaiu de
compression (0-9) peut être optionellement définit.
Les méthodes setStoreSerializeAndZippedInOneRecord de
IFilePersistence peuvent aussi être utilisées. Voir
aussi IFilepersistence#setZipCompressionLevel
Pour marquer un type stockable sous forme sérialisé dans un
enregistrement.
les méthodes setStoreSerializeAndZippedInOneRecord de
IFilePersistence peuvent aussi être utilisées.