2 votes

JPA Changer le nom de la table de l'entité en fonction du paramètre de la requête

Notre base de données a actuellement 2 schémas, un pour la production, un pour les tests, des tables identiques mais des données différentes.

Côté serveur, nous utilisons RoutingDatasource pour gérer les données. Chaque requête a un paramètre pour savoir à quelle base de données accéder, comme "prod" ou "test". Le paramètre est ensuite stocké dans ThreadLocal. Notre RoutingDatasource va récupérer ce paramètre, dépendre de la valeur et retourner la bonne source de données. Nous n'avons donc besoin que d'une seule entité de mappage et d'un seul CrudRepository pour la même table dans les deux schémas.

Mais pour une raison (stupide), on nous a demandé de fusionner les deux schémas en un seul, avec un préfixe devant chaque table, comme PROD_TABLE1, TEST_TABLE1, PROD_TABLE2, TEST_TABLE2, etc.

Je voudrais donc savoir comment conserver une seule entité, un seul référentiel pour chaque table avec cette exigence ? Nous ne voulons pas cloner le code, créer trop de classes qui héritent de celles qui existent déjà juste pour les adapter à la deuxième table. @SecondaryTable ne semble pas être ce que nous recherchons.

\===============================

UPDATE 1 :

Je regarde PhysicalNamingStrategy, capable d'ajouter un préfixe au nom de la table. Mais cela n'est exécuté qu'une seule fois lors de l'initialisation de l'EntityManager. Existe-t-il un autre moyen d'ajouter un préfixe au nom de la table à chaque demande ?

\===============================

MISE À JOUR 2 : J'essaie avec quelque chose comme ça :

@Table(name = "##schema##TABLE1")

Puis ajouter un intercepteur Hibernate à notre projet. Lors de la préparation de la déclaration, nous récupérons le paramètre de ThreadLocal, puis nous remplaçons la partie "##schema##" par le préfixe correct. En utilisant cette méthode, tout fonctionne, mais je n'aime pas vraiment cette solution.

Quelqu'un a t-il une meilleure façon de le faire ?

1voto

Marmite Bomber Points 6604

Du point de vue de la sécurité Je ne recommanderais jamais de mélanger des données de production et des données de test dans un même schéma, mais si vous envisagez de le faire, voici une façon simple de procéder prétendre vous accédez aux données avec différents PROPRIÉTAIRES .

Supposons que vous ayez un schéma d'application APP avec deux tables TEST_TAB1 y PROD_TAB1 .

-- connect as APP
create table app.prod_tab1 as
select 'prod' col1 from dual;

create table app.test_tab1 as
select 'test' col1 from dual;

Vous avez également deux utilisateurs, disons PROD_USR y TEST_USR qui accèdent aux données comme suit :

--- connect with test_usr
select * from app.test_tab1;
COL1
----
test 

--- connect with prod_usr
select * from app.prod_tab1;
COL1
----
prod 

Le problème est donc que les tables ont des noms différents selon les environnements.

Ce dont vous avez besoin, c'est d'avoir pour les deux utilisateurs test_usr et prod_usr les privilèges suivants create synonym et définissez les synonymes suivants :

-- test_usr
create synonym test_usr.tab1 for app.test_tab1;
-- prod_usr
create synonym prod_usr.tab1 for app.prod_tab1;

Maintenant, l'accès porte un nom unique et un schéma différent :

--- connect with test_usr
select * from test_usr.tab1;
COL1
----
test

--- connect with prod_usr
select * from prod_usr.tab1;
COL1
----
prod 

Si vous dites, non nous n'avons pas les deux utilisateurs d'accès différents, nous accédons aux deux environnements avec le propriétaire de schéma APP s'il vous plaît, relisez ma première phrase.

0voto

mameo Points 589

Je mets ma propre solution de contournement ici pour tous ceux qui en ont besoin :

Mettez d'abord un préfixe " ##prefix## " dans chaque objet de l'entité :

@Table(name = "##prefix##TEST_TABLE_1")

Créer une classe d'intercepteur hibernate, étendre EmptyInterceptor et surcharger la méthode onPrepareStatement . Ici, on récupère le préfixe de ThreadLocal (défini lors de la réception de la requête au préalable) et on le remplace par "##prefix##" dans le nom de la table.

public class HibernateInterceptor extends EmptyInterceptor {

    @Override
    public String onPrepareStatement(String sql) {
        String prefix = ThreadLocal.getDbPrefix();
        sql = sql.replaceAll("##schema##", prefix + "_");
        return super.onPrepareStatement(sql);
    }
}

Comme ceci, la requête pointera vers la bonne table.

Et la configuration pour EntityManagerFactory : (Vous pouvez aussi la définir dans le fichier xml)

properties.put("hibernate.ejb.interceptor", "mypackage.HibernateInterceptor");

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X