IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Hibernate Java Discussion :

cache et generateur de type increment


Sujet :

Hibernate Java

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut cache et generateur de type increment
    Bonjour,
    Voila j'ai une application web multiclient et multibase (une base par client, un client ayant lui-même plusieurs utilisateurs pouvant se connecter ) reposant sur l'usage d'hibernate. J'utilise une seule session factory qui fournit des sessions permettant d'attaquer la base désirée.

    J'utilise pour mes objets des id qu'hibernate génère par la méthode increment.
    Malheureusement, il conserve en cache la valeur de l'incrément qu'il récupère une première fois par une requete sql (il interroge alors la base concernant le client en cours ). L'incrémentation se fait donc par la suite en cache sans nouvelle requete de la part d'hibernate.
    Le problème c'est qu'il prend cette valeur indépendamment de la session en cours et donc du client et de la base choisie.


    Je me trouve donc dans le cas ou j'ai pour un client c1 une table t avec 10 tuples ainsi qu'un client c2 avec une même table t mais n'ayant que 2 tuples.

    Prenons le cas où Hibernate initialise l'incrément pour la table t sur le client c2 il récupère la valeur 2.

    Pour le client c1, la création d'une nouvelle ligne dans la table t entrainera une exception de violation de contrainte car hibernate essaie alors de créer une nouvelle ligne avec un id = 3 déjà attribué dans la table.

    C'est ballot ...
    A priori, Hibernate gère un cache lié à la sessionFactory alors que je n'ai défini aucun cache de second niveau.


    Y aurait-il moyen de configurer via le fichier de description hibernate pour qu'il évite d'utiliser son cache lorsqu'il doit générer un nouvel id ?(j'aimerais garder la génération par increment malgré tout).

    Merci pour toutes vos réponses

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Points : 635
    Points
    635
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    J'utilise une seule session factory
    Voilà ton problème ... Le générateur est propre à la session factory.

    Le mieux c'est de laisser faire la base (auto incrément, identity, sequence) etc.

  3. #3
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    je ne suis personnellement déjà pas sur qu'il soit prévu, par hibernate, qu'une sessionfactory soit partagée entre plusieurs bases. Ne serait-il pas préférable de construire une session factory par base? Ainsi, chaque base serait isolée.

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Les bases ont évidemment la même structure ce n'est que les connexions aux bases qui diffèrent. Une sessionFactory c'est gourmand en mémoire (contient toutes les descriptions des mappings objet relationnels), si bien que que d'avoir 20 bases nécessiterait d'avoir 20 sessionFactorys ce qui devient rédhibitoire alors que la seule chose qui change d'une session à l'autre c'est la connexion à la base.

    Il est possible d'associer une datasource particuliere à chaque session créé par la sessionFactory, c'est ce que j'utilise pour router les requetes vers la bonne base.

    Le seul problème c'est d'avoir utiliser l'auto increment pour la génération d'id qui manifestement est géré au niveau de la sessionFactory plutôt que de la session.
    C'est dommage que la doc hibernate ne le précise pas. En fait la sessionFactory garde en cache, pour chaque classe de mapping concernée, la première valeur retournée par le générateur d'id associé puis incrémente cette valeur en cache plutot que d'appeler le générateur d'id.
    Implementer moi-même un générateur ne changerait donc rien.

    Je me disais qu'il y avait sans doute moyen de paramétrer hibernate via son fichier de config pour éviter que la sessionFactory utilise son cache pour associer un id aux nouveaux objets. Pour l'instant c'est choux blancs ...

    Utiliser des séquences au lieu de l'auto incrément semble être une solution mais j'aurais aimé éviter cela ...

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Précision : sans ce problème l'isolation entre les bases est bien garantie ...

  6. #6
    Membre averti Avatar de ZeRevo
    Inscrit en
    Avril 2007
    Messages
    302
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2007
    Messages : 302
    Points : 343
    Points
    343
    Par défaut
    Si tu veux renseigner toi même les id des classes, tu peux utiliser <generator class="assigned" />

    "When you define generator-class="as*signed" this means that you will
    assign the primary key of the table"

  7. #7
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    voilàe la code de l'increment, il ne devrais pas être très dur de le convertir pour qu'il fasse à chauque fois un select plutot qu'uniquement initialement.... ou qu'il gère peut etre les différentes connections.... http://www.koders.com/java/fidAA0375...FFD382C92.aspx
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
     
    package org.hibernate.id;
     
    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
     
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.hibernate.HibernateException;
    import org.hibernate.MappingException;
    import org.hibernate.dialect.Dialect;
    import org.hibernate.engine.SessionImplementor;
    import org.hibernate.exception.JDBCExceptionHelper;
    import org.hibernate.mapping.Table;
    import org.hibernate.type.Type;
    import org.hibernate.util.StringHelper;
     
    /**
     * <b>increment</b><br>
     * <br>
     * An <tt>IdentifierGenerator</tt> that returns a <tt>long</tt>, constructed by
     * counting from the maximum primary key value at startup. Not safe for use in a
     * cluster!<br>
     * <br>
     * Mapping parameters supported, but not usually needed: tables, column.
     * (The tables parameter specified a comma-separated list of table names.)
     *
     * @author Gavin King
     */
    public class IncrementGenerator implements IdentifierGenerator, Configurable {
     
    	private static final Log log = LogFactory.getLog(IncrementGenerator.class);
     
    	private long next;
    	private String sql;
    	private Class returnClass;
     
    	public synchronized Serializable generate(SessionImplementor session, Object object) 
    	throws HibernateException {
     
    		if (sql!=null) {
    			getNext( session );
    		}
    		return IdentifierGeneratorFactory.createNumber(next++, returnClass);
    	}
     
    	public void configure(Type type, Properties params, Dialect dialect)
    	throws MappingException {
     
    		String tableList = params.getProperty("tables");
    		if (tableList==null) tableList = params.getProperty(PersistentIdentifierGenerator.TABLES);
    		String[] tables = StringHelper.split(", ", tableList);
    		String column = params.getProperty("column");
    		if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK);
    		String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
    		String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG);
    		returnClass = type.getReturnedClass();
     
     
    		StringBuffer buf = new StringBuffer();
    		for ( int i=0; i<tables.length; i++ ) {
    			if (tables.length>1) {
    				buf.append("select ").append(column).append(" from ");
    			}
    			buf.append( Table.qualify( catalog, schema, tables[i] ) );
    			if ( i<tables.length-1) buf.append(" union ");
    		}
    		if (tables.length>1) {
    			buf.insert(0, "( ").append(" ) ids_");
    			column = "ids_." + column;
    		}
     
    		sql = "select max(" + column + ") from " + buf.toString();
    	}
     
    	private void getNext( SessionImplementor session ) {
     
    		log.debug("fetching initial value: " + sql);
     
    		try {
    			PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
    			try {
    				ResultSet rs = st.executeQuery();
    				try {
    					if ( rs.next() ) {
    						next = rs.getLong(1) + 1;
    						if ( rs.wasNull() ) next = 1;
    					}
    					else {
    						next = 1;
    					}
    					sql=null;
    					log.debug("first free id: " + next);
    				}
    				finally {
    					rs.close();
    				}
    			}
    			finally {
    				session.getBatcher().closeStatement(st);
    			}
     
    		}
    		catch (SQLException sqle) {
    			throw JDBCExceptionHelper.convert(
    					session.getFactory().getSQLExceptionConverter(),
    					sqle,
    					"could not fetch initial value for increment generator",
    					sql
    				);
    		}
    	}
     
    }
    edit: je dis un bétise, attention que si tu fais un select à chaque fois, tu va avoir un problème de concurrence lorsque plusieurs id doivent etre générés pour la même base dans des transaction en parallèle. Exemple:

    TX1: start
    TX2: start
    TX1: récupérer le "next" dans
    la DB pour table X = 23
    TX2: récupérer le next pour la meme table, toujours 23, évidement, elle a pas changé
    TX1: insert row
    TX1: commit
    TX2: insert row
    TX2: erreur, violation de clé primaire.

    et je parle meme pas, au sein d'une meme transaction, si hibernate a besoin de plusieurs ids avant les inserts......

  8. #8
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    1
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 1
    Points : 1
    Points
    1
    Par défaut Ou sinon (peut-être) …
    Cet article qui explique le fonctionnement du cache …

    http://www.devx.com/dbzone/Article/29685/0/page/2

  9. #9
    Membre averti Avatar de ZeRevo
    Inscrit en
    Avril 2007
    Messages
    302
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2007
    Messages : 302
    Points : 343
    Points
    343
    Par défaut
    nn ca c'est différent c'est pour mettre des classes "static" en cache pour gagner en performance alors que le problème d'Olivier se situe au niveau du générateur d'id.

    Il voudrait qu'à chaque génération d'un id, hibernate aille chercher le dernier id en bdd pour être synchro entre ses bdd

    Son architecture me semble bizarre car il a des bases de données désynchro qui utilise le même schéma, il aurait été préférable de centraliser les tables de même nom sur la même base, car si c'est pour avoir ce genre de connerie, ca risque d'etre dur a gerer :
    bdd 1 => table client => id : 1 toto, id 5 titi, id 6 tutu, id 10 tata
    bdd 2 => table client => id : 2 popo, id 3 pipi, id 11 pupu
    bdd 3 => table client => id : 4 roro, id 7 riri, id 8 ruru
    ....

    il serait préférable d'avoir un serveur de bdd central qui possède les données utilisées par les autres clients avec l'utilisation d'un discriminator ou de filtres hibernate pour filtrer les données

  10. #10
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    c'est pas si bizzare en fait, j'ai re-regardé la doc hibernate, et ils mentionnent à plusieurs endroit effectivement, comme quelque chose de tout à fait naturel, de fournir toi meme tes connections à la db lors de la création de session. Son architecture ne me semble pas si mauvaise, il peux ainsi facilement gérer les backups et les quotas de chaque client au niveau de la db. Par contre, personellement, j'aurais plutot envisagé un serveur par client histoire de tout bien isoler :p

    Ceci dit, avec cette architecture, je ne vois que la génération des id par une séquence de la db comme solution fiable.

  11. #11
    Membre averti Avatar de ZeRevo
    Inscrit en
    Avril 2007
    Messages
    302
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2007
    Messages : 302
    Points : 343
    Points
    343
    Par défaut
    un serveur par client histoire de tout bien isoler :p
    et tu dis que son architecture est bonne lol

  12. #12
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Merci pour toutes vos réponses.
    Je ne pense pas qu'implementer mon propre générateur soit une bonne solution, il fera à peu près la même chose que le générateur incrémental développé par hibernate et risque d'être appeler une fois pour toutes puis la valeur mise en cache par hibernate sera utilisé pour les créations futurs.

    Comme semble le suggérer tchize_, une bonne raison de mettre en cache au niveau de la sessionFactory (alors qu'aucun cache de second niveau n'est en place) c'est effectivement pour gérer correctement l'attribution d'id pour des transactions différentes sans risquer la violation de contrainte.

    Pour ce qui est du choix mutlibase, ce n'est pas un mauvais choix (isolation des clients, sauvegardes et restitutions des données des clients facilité, les temps des requetes dépendent bien de la volumétrie de chaque client , on ne pénalise donc pas un client par rapport à un autre).

  13. #13
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    Citation Envoyé par olivier_44 Voir le message
    et risque d'être appeler une fois pour toutes puis la valeur mise en cache par hibernate sera utilisé pour les créations futurs.
    Si tu regarde le code du générateur increment, tu verra que c'est lui même qui fait la mise en cache de 'next' dans son instance.... Il met sql à null pour s'assurer qu'on ne ferra plus de requetes par la suite sur la db.

  14. #14
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    Citation Envoyé par ZeRevo Voir le message
    et tu dis que son architecture est bonne lol
    Tout dépend de la tarification faite au client, c'est pas toujours possible de mettre en place les ressources pour filer 256M de ram à chaque client :p

    Edit: en plus, en utilisant les réattachement à des session, il peux copier des données d'un client à l'autre et çà passera d'une base à l'autre ^^

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Edit: en plus, en utilisant les réattachement à des session, il peux copier des données d'un client à l'autre et çà passera d'une base à l'autre ^^
    Sans cache de second niveau, les tests sont concluants...

  16. #16
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Si tu regarde le code du générateur increment, tu verra que c'est lui même qui fait la mise en cache de 'next' dans son instance.... Il met sql à null pour s'assurer qu'on ne ferra plus de requetes par la suite sur la db.
    Tu as raison
    Mais je ne pense pas que la réécriture du générateur serait une bonne solution comme je l'ai évoqué dans un message précédent.

    Autant utiliser des séquences

  17. #17
    Membre averti Avatar de ZeRevo
    Inscrit en
    Avril 2007
    Messages
    302
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2007
    Messages : 302
    Points : 343
    Points
    343
    Par défaut
    si une bdd est hs est-ce qu'hibernate va réussir à gérer ?

    Tu as raison
    Mais je ne pense pas que la réécriture du générateur serait une bonne solution comme je l'ai évoqué dans un message précédent.

    Autant utiliser des séquences
    Les séquences sont uniques à une bdd nn? comment hibernate sait que tel objet appartient à telle bdd ?

  18. #18
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    si une bdd est hs est-ce qu'hibernate va réussir à gérer ?
    Je pense que oui, chaque session est propre à une seule base. si une base est hs je pense que cela n'empeche pas la sessionfactory à fournir des sessions pour les autres bases.

    Les séquences sont uniques à une bdd nn? comment hibernate sait que tel objet appartient à telle bdd ?
    Chaque session étant lié à une seule base, il pioche la bonne sequence associé à la bonne base. A condition qu'il n'y ait pas de cache sur les sequence.s A priori, chaque sequence est bien mise à jour en base à chaque création.

  19. #19
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    Citation Envoyé par ZeRevo Voir le message
    Les séquences sont uniques à une bdd nn? comment hibernate sait que tel objet appartient à telle bdd ?
    C'est marqué dans la doc du SequenceGenerator, il ne nécessite pas de session additionelle, il utilise celle de la session en cours, donc la connection associée avec la demande de persist(), update(), etc et donc, pas voie de conséquence, la bonne base.

  20. #20
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 21
    Points : 13
    Points
    13
    Par défaut
    Merci.
    J'ai commencé à mettre en place les séquences et je vais tester pour ...

    Point final dans quelques jours.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Incrementer un type à chaque nouvelle saisie
    Par SOPSOU dans le forum Langage
    Réponses: 3
    Dernier message: 23/08/2007, 14h33
  2. Incrementer une variable du type mysql_fetch_array
    Par furtif1 dans le forum Langage
    Réponses: 2
    Dernier message: 10/03/2007, 15h23
  3. Réponses: 1
    Dernier message: 25/09/2006, 10h18
  4. colonnes de type auto incremental
    Par Bill_Baroud dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 30/12/2004, 12h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo