Qu'est-ce que c'est que ce beans ? (SGBDR et immuabilité, la suite)

4 views
Skip to first unread message

Laurent Caillette

unread,
Jul 31, 2008, 7:26:45 AM7/31/08
to tec...@googlegroups.com
Salut,

Voici la suite promise de ce brainstorming épistolaire sur la
faisabilité d'un SGBDR où les données, une fois écrites, ne
changeraient plus d'état. Maintenant nous allons explorer l'API.

Partons de la notion de propriété, non pas au sens marxiste mais au
sens JavaBean. Déjà il faut revoir cette dernière puisque nous voulons
des objets immuables, et utilisons au passage les Generics qui
n'existaient pas quand les JavaBeans ont été créés.

<<<
public class Property< T, U > {
public T set( U value ) { ... }
}
>>>

Le premier argument T correspond au type de l'objet à laquelle la
propriété est rattaché. Le second argument U correspond au type de la
valeur manipulée. Comme les détenteurs des propriétés sont immuables,
l'appel à ``#set(...)`` se contente de renvoyer une copie modifiée.

A quoi ressemble l'utilisation d'une telle propriété ? Il suffit de
renvoyer une instance attachée à l'objet propriétaire. Comme tout cela
est compliqué, avec plein des paramètres dans le constructeur et tout,
notre objet utilisant une propriété sera une interface.

<<<
public interface Stuff {
Property< Stuff, String > nickname() ;
Property< Stuff, Stuff > other() ;
}
>>>

Après on peut écrire du code comme suit, en se basant sur une
``Factory`` imaginaire qui renvoit toujours les bons types grâce à
l'inférence de type de Java 5.

<<<
Stuff stuff = Factory.create() ;
stuff = stuff.nickname().set( "foo" ) ;
>>>

A elle toute seule, l'interface Stuff définit un schéma de base de
données, avec une relation "many-to-one" pour rester dans la
terminologie Hibernate.

Mais on a peut-être envie d'ajouter des contraintes. Comme le système
d'annotations d'Hibernate validator manque de typage statique, voici
une autre approche complètement typée :

<<<
public interface Stuff {

Property< Stuff, String > nickname() ;
Property< Stuff, Stuff > other() ;

Stuff PROTOTYPE = PrototypeTools.create( Stuff.class ) ;

PropertyConstraints CONSTRAINTS = PropertyConstraints.create(
Constraints.on( PROTOTYPE.other() ).notNull(),
Constraints.on( PROTOTYPE.nickname() ).regex( "\\w+" ).notNull()
) ;

}
>>>

Java ne permet pas directement de fournir des morceaux de code
impératif dans une interface mais il y a moyen de se débrouiller avec
des constantes. L'avantage est que le ``public static final`` est
implicite. Mais comme chaque instance d'objet Property sera rattachée
à une instance de Stuff, comment faire en sorte d'accéder d'une façon
typée statiquement à ces membres d'instance à partir d'un contexte
statique ? En utilisant une instance "spéciale" dont le seul rôle est
de se "souvenir" de ses méthodes qui ont été appelées. C'est bien sûr
ce que fait l'instance ``PROTOTYPE`` qui va se baser sur un dynamic
proxy.

Une fois muni de notre ``PROTOTYPE`` nous pouvons ajouter des
contraintes à la définition de la classe, ces contraintes étant
conservées dans une autre constante ``CONSTRAINTS``. La classe
utilitaire Constraints génère des objets Constraint fortement typés
autour de la propriété qu'ils manipulent, par exemple il n'est pas
possible d'utiliser ``regex()`` sur une propriété qui manipule un
Boolean.

Je n'insiste pas sur les collections qui nécessitent un vrai travail
de fond. Il faudra de toutes façons jeter les contrats muables autour
de ``java.util.Collection`` et on imagine déjà des choses genre
``Sequence`` et ``Bag``. Le système de contraintes permettrait au
passage d'exprimer des relations inverses mises à jour
automatiquement, style :

<<<
public interface Thing {

SequenceProperty< Thing, Stuff > otherStuff ;
Property< Thing, Stuff > special() ;

Thing PROTOTYPE = PrototypeTools.create( Thing.class ) ;

PropertyConstraints CONSTRAINTS = PropertyConstraints.create(
Constraints.on( PROTOTYPE.otherStuff(), PROTOTYPE.special() ).
linked().notNull()
)
) ;
}
>>>

Evidemment avec les First Class Methods on pourrait se passer de ces
acrobaties. Notons qu'exposer les propriétés d'une interface en
conservant le typage statique peut fournir un coup de jeune aux
mécanismes de binding avec les composants d'une interface graphique,
par exemple.

Maintenant remplaçons notre ``Factory`` par une base de donnée
conservant ces objets immuables et leurs révisions successives. Il
faut à l'évidence faire figurer cette notion de révision dans l'API,
ce qui donne :

<<<
final Revision last = database.getLastRevision() ;
final Stuff stuff1 = last.query(
Stuff.PROTOTYPE.nickname(), "foo" ).first() ;
>>>

On effectue une modification :

<<<
final Stuff stuff2 = stuff1.nickname().set( "bar" ) ;
final Revision updated = Database.upgrade( stuff2 ) ;
>>>

Comme chaque objet connaît sa propre révision d'origine et les
changements apportés, on peut savoir à tout moment où il en est. A
l'intérieur du système de persistance local (équivalent à la session
Hibernate) on conserverait une information sur la requête d'origine
qui permettrait de mettre à jour notre ``stuff`` après écriture dans
la base. Par exemple l'instruction ci-dessous débouche sur un null ou
une exception (devinette : pourquoi ?) :

<<<
final Stuff stuff3 = updated.requery( stuff1 ) ; // Plus rien !
>>>

Ensuite il faut envisager les cas de conflits. Heureusement on peut
s'aider de la notion de branche acceptant des données qui dérogent aux
contraintes :

<<<
final Stuff stuff4 = stuff1.nickname().set( null ) ; // Interdit !
final Revision sick = Database.upgradeOrKeepConflicts( stuff4 ) ;
if( sick.isConflictBranch() {
...
}
>>>

C'est là une façon d'exposer toutes les transactions en cours, les
tripes à l'air, et de les faire durer indéfiniment sans verrouiller
toute la base !

Tout cela diverge complètement des standards Java, mais une fois le
principe de base intégré la syntaxe me semble assez naturelle. On peut
reprocher l'adhérence à Java, mais on doit pouvoir obtenir des
équivalents dans d'autres langages. Au moins là on a zéro "impedance
mismatch" entre le langage et le système de persistance.

Si je suis courageux je réfléchirai à comment intégrer les versions
successives de schémas.


Enjoy !

c.


=== Références

Une alternative aux propriétés JavaBeans dans un contexte JDK 1.4 :

http://joda.sourceforge.net/beans.html

Une déclinaison en Scala :

http://www.scala-lang.org/docu/examples/files/properties.html

Un vieux projet qui repose sur de la génération de bytecode pour
"attraper" la référence à la méthode appelée :

http://tweed.sourceforge.net/tutorial/bindings.html

La dernière spec des First Class Methods :

http://docs.google.com/View?docid=ddhp95vd_6hg3qhc

Laurent Caillette

unread,
Aug 4, 2008, 5:02:47 AM8/4/08
to techos

Thomas Mueller, l'auteur de H2, est plein de bonnes idées. Il a
notamment trouvé une alternative au LINQ de Microsoft qui tient en
quelques centaines de lignes.

Tandis que LINQ permet d'embarquer du simili-SQL dans du C# :

<<<
var sundayWork =
from t in timesheet where t.WeekDay == Day.SUN select t ;
>>>

Avec JaQu, on écrit du pur Java :

<<<
Timesheet t = new Timesheet() ;
List< Timesheet > sundayWork =
db.from( t ).where( t.weekDay ).is( Day.SUN ).select() ;

// ...

public class Timesheet {
public Day weekDay ;
// ...
}
>>>

Bon sang comment fait-il ? Tout simplement grâce à des valeurs uniques
connues par JaQu, injectées dans l'instance de ``Timesheet`` passée
dans ``db.from( t )``. Plus léger comme métadonnées je ne vois pas.

Son approche fonctionne parce que les objets manipulés reflètent les
structures de tables, ils sont plats et surtout ne décrivent pas de
graphe. Il faut aussi trouver des hacks pour les enums qui ne sont pas
faites pour supporter l'ajout d'éléments post-compilation.

Si on veut supporter une approche plus "navigationnelle" (parcours de
graphe en pur java) l'utilisation du PROTOTYPE décrite précédemment
demeure une façon décente d'obtenir des métadonnées typées
statiquement.


Enjoy !

c.


JaQu, un sous-projet de H2 :
http://www.h2database.com/html/frame.html?jaqu.html&main


L'article sur TSS qui présente un des hacks impliqués :
http://www.theserverside.com/news/thread.tss?thread_id=50190

Des métadonnées sur du code Java pour un SGBDR basé sur
l'immuabilité :
http://groups.google.com/group/techos/browse_thread/thread/73d371a9045cc8df
Reply all
Reply to author
Forward
0 new messages