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 :

Hibernate et implémentation d'un IdentifierGenerator dont les paramètres dépendent de l'utilisateur connecté


Sujet :

Hibernate Java

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 42
    Par défaut Hibernate et implémentation d'un IdentifierGenerator dont les paramètres dépendent de l'utilisateur connecté
    Bonjour,

    pourriez-vous svp me confirmer la démarche pour ce contexte d'utilisation des générateurs d'identifiant :

    la clé primaire de plusieurs entités de ma ma base de données est issu d'une incrémentation manuelle d'un entier qui est stockée dans une table dédiée à cela.

    Cette table est donc similaire à un "gestionnaire de séquences" (sous MySQL).
    Pour identifier le nouvel incrément à utiliser lors d'une insertion dans l'une des tables citées ci-dessus, deux critères sont nécessaire :
    le nom de l'entité
    un numéro de "segment"

    Autrement dit le schéma de la table stockant les séquences ressemble à ceci:
    "entity_name" | "segment" | "new_increment"
    ________________________________________
    X | a | 10
    X | b | 5
    Y | a | 2
    Z | a | 3

    Donc à chaque nouvelle entité, j'aimerai qu'Hibernate aille rechercher la valeur de la PK dans cette table (avec deux criètres, entity_name et segment) et que "new_increment" soit incrémenté

    Je me suis donc documenté mais j'avoue avoir du mal à bien retrouver l'information qui me faut.

    Je suis tombé sur ceci:
    org.hibernate.id.enhanced.TableGenerator
    Le problème, c'est qu'on ne peut donner qu'un seul critère de recherche dans la table qui stocke les séquences, alors que j'en possède deux (entity_name et segment)

    D'où ma question, existe-t'il une implémentation de générateur d'IDs dans Hibernate qui puisse répondre à ce besoin?

    Dans le cas contraire, si je ne me trompe, je dois implémenter une classe héritant de IdentifierGenerator et redéfinir la méthode generate qui reçoit la session et l'objet : j'effectue alors moi même les requêtes vers ma table séquence et je retourne le nouvel id.

    Donc en résumé, si Hibernate ne propose pas de générateur collant à mon besoin, mieux vaut-il :
    1. implémenter une classe génératrice
    2. ou passer en mode "assigned" et laisser la couche "métier" le soin d'attribuer lui même l'identifiant aux objets qu'il souhaite persister?


    Je vous remercie de votre aide.

    Bonne journée

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 42
    Par défaut
    Suite de mes interrogations, j'ai testé la stratégie de créer une autre implémentation de IdentifierGenerator, dont voici le prototype :

    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
     
    package XXXX.demo.hibernate;
     
    import java.io.Serializable;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
     
    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.id.Configurable;
    import org.hibernate.id.IdentityGenerator;
     
    import org.hibernate.type.Type;
     
    import com.mysql.jdbc.PreparedStatement;
     
    public class Sequence extends IdentityGenerator implements Configurable
    {
    	/**
             * The sequence name parameter specifies the underlying ID generator to use.
             */
    	public static final String SEQUENCE_NAME = "sequence";
     
    	private String sequenceName;
     
    	public Serializable generate(SessionImplementor session, Object object)
    			throws HibernateException
    	{
    		Serializable id = null;
    		String sql = "SELECT sequence_centre_nextval FROM sequences_centres"
    				+ " WHERE sequence_table = '" + sequenceName + "'"
    				+ " AND centre_id = 604";
     
    		PreparedStatement stt = null;
    		try
    		{
    			stt = (PreparedStatement) session.getBatcher()
    					.prepareSelectStatement(sql);
    			ResultSet rs = stt.executeQuery();
    			while (rs.next())
    			{
    				id = Long.parseLong(rs.getString(1));
    			}
     
    			sql = "UPDATE sequences_centres sc, sequences s"
    				+" SET sequence_centre_nextval = sequence_centre_nextval + sequence_step "
    				+ " WHERE sc.sequence_table = '" + sequenceName + "'"
    				+ " AND centre_id = 604"
    				+ " AND sc.sequence_table = s.sequence_table";
    			stt.execute(sql);
    		}
    		catch (SQLException sqle)
    		{
     
    			throw JDBCExceptionHelper.convert(session.getFactory()
    			.getSQLExceptionConverter(), sqle,
    			"could not fetch initial value for increment generator",
    			sql);
     
    		}
    		return id;
     
    	}
     
    	public void configure(Type type, Properties params, Dialect d)
    			throws MappingException
    	{
    		sequenceName = params.getProperty(SEQUENCE_NAME);
    		if (sequenceName == null)
    		{
    			throw new MappingException(
    					"param named \"sequence\" is required for object generation strategy");
    		}
    	}
    }
    Sur la continuité de la problématique, on observe donc que mon système de séquence se base sur deux informations pour récupérer le prochain ID:
    • sequenceName : cette information est statique et est récupéré depuis le fichier de mapping de l'entité (param de generator)
    • centre_id : cette information est dynamique


    Donc pour le prototype, j'ai fixé la valeur de "centre_id" or cette information est directement dépendante de l'utilisateur de l'application.
    Selon la provenance de l'utilisateur, la séquence utilisée pour une entité donnée, n'est pas la même (du moins, l'état de l'incrémentation diffère).

    D'où est-ce possible de pouvoir gérer ce genre de situation, autrement dit, quels sont les moyens qui me sont offerts, pour récupérer par la session ou éventuellement par une programmation dynamique du fichier de mapping, la valeur de "centre_id"?

    Ou bien, ce que je souhaite est irréalisable, et il vaut mieux passer par un mode "generator assigned" et contrôler tout ceci manuellement par la couche métier?

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 42
    Par défaut
    Je pense être sur la bonne voie.

    En effet, mon problème étant d'avoir un comportement d'Hibernate en fonction de l'utilisateur connecté, j'ai donc joué sur la configuration de la sesssion:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Configuration config = new Configuration().configure();
    		Properties extraProperties = new Properties();
    		extraProperties.put("centreId", 200);
    		config.addProperties(extraProperties);
    		SessionFactory sessionFactory = config.buildSessionFactory();
    		Session session = sessionFactory.openSession();
    Chaque utilisateur va donc avoir sa propre session Hibernate.
    De ce fait, dans mon implémentation de IdentifierGenerator, je récupère mon centreId ainsi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public Serializable generate(SessionImplementor session, Object object)
    			throws HibernateException
    	{
    		Serializable id = null;
    centreId = session.getFactory().getProperties().get("centreId");
    La contrainte est donc que chaque utilisateur a sa propre session hibernate et que dans mon contexte, l'idéal aurait été de travailler sur une seule et unique session pour toute l'application.

    Ce qui m'amène à me poser cette question : est-ce vraiment Hibernate qui doit générer mes identifiants d'entités ceci impliquant d'avoir une session par utilisateur...

  4. #4
    Rédacteur/Modérateur
    Avatar de Logan Mauzaize
    Homme Profil pro
    Architecte technique
    Inscrit en
    Août 2005
    Messages
    2 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte technique
    Secteur : Transports

    Informations forums :
    Inscription : Août 2005
    Messages : 2 894
    Par défaut
    Normalement tu as une session par "transaction" ...
    A quoi correspond le "segment" ?

    Faire un nouveau générateur ne me paraît pas être une mauvaise idée mais en revanche il faut :
    • Ouvrir une nouvelle connexion / session
    • Locker l'enregistrement qui t'interresse
    • Récupérer sa valeur
    • Incrémenter la valeur
    • Committer (normalement ça enlève les locks)


    Si c'est trop compliqué à gérer via un générateur, je mettrais en "assigned" mais pas contre c'est ma couche DAO et non métier qui ferait le boulot que j'ai décrié plus haut.
    Java : Cours et tutoriels - FAQ - Java SE 8 API - Programmation concurrente
    Ceylon : Installation - Concepts de base - Typage - Appels et arguments

    ECM = Exemple(reproduit le problème) Complet (code compilable) Minimal (ne postez pas votre application !)
    Une solution vous convient ? N'oubliez pas le tag
    Signature par pitipoisson

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 42
    Par défaut
    Citation Envoyé par Nemek Voir le message
    Normalement tu as une session par "transaction" ...
    A quoi correspond le "segment" ?

    Faire un nouveau générateur ne me paraît pas être une mauvaise idée mais en revanche il faut :
    • Ouvrir une nouvelle connexion / session
    • Locker l'enregistrement qui t'interresse
    • Récupérer sa valeur
    • Incrémenter la valeur
    • Committer (normalement ça enlève les locks)


    Si c'est trop compliqué à gérer via un générateur, je mettrais en "assigned" mais pas contre c'est ma couche DAO et non métier qui ferait le boulot que j'ai décrié plus haut.
    "segment" est un terme que j'ai repris de la documentation d'hibernate pour org.hibernate.id.enhanced.TableGenerator, qui aurait pu correspondre à mon besoin, si la récupération de mon incrément ne dépendait que d'un seul critère:

    table_name (optional - defaults to hibernate_sequences): the name of the table to be used.

    value_column_name (optional - defaults to next_val): the name of the column on the table that is used to hold the value.

    segment_column_name (optional - defaults to sequence_name): the name of the column on the table that is used to hold the "segment key". This is the value which identifies which increment value to use.

    segment_value (optional - defaults to default): The "segment key" value for the segment from which we want to pull increment values for this generator.
    Dans mon cas, j'ai deux critères, et les "segments" correspondrait alors à :
    entityName => la valeur du segment peut donc être fixé dans le fichier de mapping
    centreId => cette valeur est dynamique en fonction de l'utilisateur

    Avec ces deux critères, je sais donc quel incrément utilisé.


    En ce qui concerne 1session=1transaction, c'est ok, je le gère de cette façon

    Par contre, merci pour tes remarques, je n'ai pas pensé à locker l'enregistrement que j'utilise pour la génération de mon ID.

    Bonne journée

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 17/12/2013, 15h46
  2. Activer toutes les clés primaires de l'utilisateur connecté
    Par SheikYerbouti dans le forum Contribuez
    Réponses: 0
    Dernier message: 30/12/2011, 15h07
  3. Désactiver les clés primaires de l'utilisateur connecté
    Par SheikYerbouti dans le forum Contribuez
    Réponses: 0
    Dernier message: 30/12/2011, 15h07
  4. [CAS] Récupérer les attributs LDAP de l'utilisateur connecté
    Par ericw78 dans le forum Développement Web en Java
    Réponses: 1
    Dernier message: 02/12/2011, 21h53

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