Comment obtenir SQL à partir de L'API Hibernate Criteria (*pas* pour la journalisation)
Existe-t-il un moyen facile d'obtenir le sql (à générer) à partir d'un critère Hibernate?
Idéalement, j'aurais quelque chose comme:
Criteria criteria = session.createCriteria(Operator.class);
... build up the criteria ...
... and then do something like ...
String sql = criteria.toSql()
(But this of course does not exist)
L'idée serait alors d'utiliser le sql dans le cadre d'une énorme requête 'MINUS' (j'ai besoin de trouver les différences entre 2 schémas identiques-identiques dans la structure, pas dans les données-et le moins n'est pas supporté par Hibernate)
(BTW je sais que je peux vérifier le SQL à partir des fichiers journaux)
8 réponses
J'ai fait quelque chose comme ça en utilisant Spring AOP afin que je puisse récupérer le sql, les paramètres, les erreurs et le temps d'exécution pour toute requête exécutée dans L'application, que ce soit HQL, Criteria ou SQL natif.
C'est évidemment fragile, peu sûr, sujet à rupture avec les changements dans Hibernate, etc, mais cela illustre qu'il est possible d'obtenir le SQL:
CriteriaImpl c = (CriteriaImpl)query;
SessionImpl s = (SessionImpl)c.getSession();
SessionFactoryImplementor factory = (SessionFactoryImplementor)s.getSessionFactory();
String[] implementors = factory.getImplementors( c.getEntityOrClassName() );
CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable)factory.getEntityPersister(implementors[0]),
factory, c, implementors[0], s.getEnabledFilters());
Field f = OuterJoinLoader.class.getDeclaredField("sql");
f.setAccessible(true);
String sql = (String)f.get(loader);
Enveloppez la chose entière dans un essai / attraper et utiliser à vos risques et périls.
Voici "une autre" façon d'obtenir le SQL:
CriteriaImpl criteriaImpl = (CriteriaImpl)criteria;
SessionImplementor session = criteriaImpl.getSession();
SessionFactoryImplementor factory = session.getFactory();
CriteriaQueryTranslator translator=new CriteriaQueryTranslator(factory,criteriaImpl,criteriaImpl.getEntityOrClassName(),CriteriaQueryTranslator.ROOT_SQL_ALIAS);
String[] implementors = factory.getImplementors( criteriaImpl.getEntityOrClassName() );
CriteriaJoinWalker walker = new CriteriaJoinWalker((OuterJoinLoadable)factory.getEntityPersister(implementors[0]),
translator,
factory,
criteriaImpl,
criteriaImpl.getEntityOrClassName(),
session.getLoadQueryInfluencers() );
String sql=walker.getSQLString();
Pour ceux qui utilisent NHibernate, c'est un port du code de [ram]
public static string GenerateSQL(ICriteria criteria)
{
NHibernate.Impl.CriteriaImpl criteriaImpl = (NHibernate.Impl.CriteriaImpl)criteria;
NHibernate.Engine.ISessionImplementor session = criteriaImpl.Session;
NHibernate.Engine.ISessionFactoryImplementor factory = session.Factory;
NHibernate.Loader.Criteria.CriteriaQueryTranslator translator =
new NHibernate.Loader.Criteria.CriteriaQueryTranslator(
factory,
criteriaImpl,
criteriaImpl.EntityOrClassName,
NHibernate.Loader.Criteria.CriteriaQueryTranslator.RootSqlAlias);
String[] implementors = factory.GetImplementors(criteriaImpl.EntityOrClassName);
NHibernate.Loader.Criteria.CriteriaJoinWalker walker = new NHibernate.Loader.Criteria.CriteriaJoinWalker(
(NHibernate.Persister.Entity.IOuterJoinLoadable)factory.GetEntityPersister(implementors[0]),
translator,
factory,
criteriaImpl,
criteriaImpl.EntityOrClassName,
session.EnabledFilters);
return walker.SqlString.ToString();
}
Si vous utilisez Hibernate 3.6, vous pouvez utiliser le code dans la réponse acceptée (fournie par Brian Deterling) avec une légère modification:
CriteriaImpl c = (CriteriaImpl) criteria;
SessionImpl s = (SessionImpl) c.getSession();
SessionFactoryImplementor factory = (SessionFactoryImplementor) s.getSessionFactory();
String[] implementors = factory.getImplementors(c.getEntityOrClassName());
LoadQueryInfluencers lqis = new LoadQueryInfluencers();
CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable) factory.getEntityPersister(implementors[0]), factory, c, implementors[0], lqis);
Field f = OuterJoinLoader.class.getDeclaredField("sql");
f.setAccessible(true);
String sql = (String) f.get(loader);
J'aime ça si vous voulez obtenir quelques parties de la requête:
new CriteriaQueryTranslator(
factory,
executableCriteria,
executableCriteria.getEntityOrClassName(),
CriteriaQueryTranslator.ROOT_SQL_ALIAS)
.getWhereCondition();
Par exemple quelque chose comme ceci:
String where = new CriteriaQueryTranslator(
factory,
executableCriteria,
executableCriteria.getEntityOrClassName(),
CriteriaQueryTranslator.ROOT_SQL_ALIAS)
.getWhereCondition();
String sql = "update my_table this_ set this_.status = 0 where " + where;
Voici une méthode que j'ai utilisée et qui a fonctionné pour moi
public static String toSql(Session session, Criteria criteria){
String sql="";
Object[] parameters = null;
try{
CriteriaImpl c = (CriteriaImpl) criteria;
SessionImpl s = (SessionImpl)c.getSession();
SessionFactoryImplementor factory = (SessionFactoryImplementor)s.getSessionFactory();
String[] implementors = factory.getImplementors( c.getEntityOrClassName() );
CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable)factory.getEntityPersister(implementors[0]), factory, c, implementors[0], s.getEnabledFilters());
Field f = OuterJoinLoader.class.getDeclaredField("sql");
f.setAccessible(true);
sql = (String)f.get(loader);
Field fp = CriteriaLoader.class.getDeclaredField("traslator");
fp.setAccessible(true);
CriteriaQueryTranslator translator = (CriteriaQueryTranslator) fp.get(loader);
parameters = translator.getQueryParameters().getPositionalParameterValues();
}
catch(Exception e){
throw new RuntimeException(e);
}
if (sql !=null){
int fromPosition = sql.indexOf(" from ");
sql = "SELECT * "+ sql.substring(fromPosition);
if (parameters!=null && parameters.length>0){
for (Object val : parameters) {
String value="%";
if(val instanceof Boolean){
value = ((Boolean)val)?"1":"0";
}else if (val instanceof String){
value = "'"+val+"'";
}
sql = sql.replaceFirst("\\?", value);
}
}
}
return sql.replaceAll("left outer join", "\nleft outer join").replace(" and ", "\nand ").replace(" on ", "\non ");
}
Pour tous ceux qui souhaitent le faire en une seule ligne (par exemple dans la fenêtre Display/Immediate, une expression watch ou similaire dans une session de débogage), ce qui suit le fera et "imprimer joliment" le SQL:
new org.hibernate.jdbc.util.BasicFormatterImpl().format((new org.hibernate.loader.criteria.CriteriaJoinWalker((org.hibernate.persister.entity.OuterJoinLoadable)((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getEntityPersister(((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getImplementors(((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName())[0]),new org.hibernate.loader.criteria.CriteriaQueryTranslator(((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),((org.hibernate.impl.CriteriaImpl)crit),((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),org.hibernate.loader.criteria.CriteriaQueryTranslator.ROOT_SQL_ALIAS),((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),(org.hibernate.impl.CriteriaImpl)crit,((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),((org.hibernate.impl.CriteriaImpl)crit).getSession().getEnabledFilters())).getSQLString());
...ou Voici une version plus facile à lire:
new org.hibernate.jdbc.util.BasicFormatterImpl().format(
(new org.hibernate.loader.criteria.CriteriaJoinWalker(
(org.hibernate.persister.entity.OuterJoinLoadable)
((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getEntityPersister(
((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getImplementors(
((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName())[0]),
new org.hibernate.loader.criteria.CriteriaQueryTranslator(
((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),
((org.hibernate.impl.CriteriaImpl)crit),
((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),
org.hibernate.loader.criteria.CriteriaQueryTranslator.ROOT_SQL_ALIAS),
((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),
(org.hibernate.impl.CriteriaImpl)crit,
((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),
((org.hibernate.impl.CriteriaImpl)crit).getSession().getEnabledFilters()
)
).getSQLString()
);
Notes:
- , La réponse est basée sur la solution posté par ramdane.j'.
- Il suppose que l'objetCriteria est nommé
crit
. si le nom est différent, effectuez une recherche et remplacer. - Il suppose que la version Hibernate est postérieure à 3.3.2.GA mais plus tôt que 4.0 afin d'utiliser BasicFormatterImpl pour" imprimer " le HQL. Si vous utilisez une version différente, voir cette réponse comment modifier. Ou peut-être simplement supprimer la jolie impression entièrement car c'est juste un "agréable à avoir".
- Il utilise
getEnabledFilters
plutôt quegetLoadQueryInfluencers()
pour la rétrocompatibilité puisque ce dernier a été introduit dans une version ultérieure version de Hibernate (3.5???) - Il n'affiche pas les valeurs de paramètre réelles utilisées si la requête est paramétrée.
Cette réponse est basée sur la réponse de user3715338 (avec une petite erreur d'orthographe corrigée) et mélangée avec la réponse de Michael pour Hibernate 3.6 - basée sur la réponse acceptée de Brian Deterling. Je l'ai ensuite étendu (pour PostgreSQL) avec quelques autres types remplaçant les questionmarks:
public static String toSql(Criteria criteria)
{
String sql = "";
Object[] parameters = null;
try
{
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
SessionImpl sessionImpl = (SessionImpl) criteriaImpl.getSession();
SessionFactoryImplementor factory = sessionImpl.getSessionFactory();
String[] implementors = factory.getImplementors(criteriaImpl.getEntityOrClassName());
OuterJoinLoadable persister = (OuterJoinLoadable) factory.getEntityPersister(implementors[0]);
LoadQueryInfluencers loadQueryInfluencers = new LoadQueryInfluencers();
CriteriaLoader loader = new CriteriaLoader(persister, factory,
criteriaImpl, implementors[0].toString(), loadQueryInfluencers);
Field f = OuterJoinLoader.class.getDeclaredField("sql");
f.setAccessible(true);
sql = (String) f.get(loader);
Field fp = CriteriaLoader.class.getDeclaredField("translator");
fp.setAccessible(true);
CriteriaQueryTranslator translator = (CriteriaQueryTranslator) fp.get(loader);
parameters = translator.getQueryParameters().getPositionalParameterValues();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
if (sql != null)
{
int fromPosition = sql.indexOf(" from ");
sql = "\nSELECT * " + sql.substring(fromPosition);
if (parameters != null && parameters.length > 0)
{
for (Object val : parameters)
{
String value = "%";
if (val instanceof Boolean)
{
value = ((Boolean) val) ? "1" : "0";
}
else if (val instanceof String)
{
value = "'" + val + "'";
}
else if (val instanceof Number)
{
value = val.toString();
}
else if (val instanceof Class)
{
value = "'" + ((Class) val).getCanonicalName() + "'";
}
else if (val instanceof Date)
{
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS");
value = "'" + sdf.format((Date) val) + "'";
}
else if (val instanceof Enum)
{
value = "" + ((Enum) val).ordinal();
}
else
{
value = val.toString();
}
sql = sql.replaceFirst("\\?", value);
}
}
}
return sql.replaceAll("left outer join", "\nleft outer join").replaceAll(
" and ", "\nand ").replaceAll(" on ", "\non ").replaceAll("<>",
"!=").replaceAll("<", " < ").replaceAll(">", " > ");
}