[英]How to get SQL from Hibernate Criteria API (*not* for logging)
有没有办法从 Hibernate Criteria
获取(要生成的)SQL?
理想情况下,我会有类似的东西:
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)
然后的想法是将SQL用作巨大的“MINUS”查询的一部分(我需要找到2个相同模式之间的差异 - 结构相同,而不是数据 - 并且Hibernate不支持MINUS)
(顺便说一句,我知道我可以从日志文件中检查 SQL)
这是获取 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();
我已经使用 Spring AOP 完成了类似的操作,因此我可以获取应用程序中运行的任何查询的 sql、参数、错误和执行时间,无论是 HQL、Criteria 还是本机 SQL。
这显然是脆弱的、不安全的,容易因 Hibernate 等的变化而中断,但它说明了可以获得 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);
将整个内容包装在 try/catch 中,并自担风险使用。
对于那些使用 NHibernate 的人,这是 [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();
}
如果您使用的是 Hibernate 3.6,则可以使用已接受的答案(由 Brian Deterling 提供)中的代码稍加修改:
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);
如果您只想获取查询的某些部分,我喜欢这个:
new CriteriaQueryTranslator(
factory,
executableCriteria,
executableCriteria.getEntityOrClassName(),
CriteriaQueryTranslator.ROOT_SQL_ALIAS)
.getWhereCondition();
例如这样的事情:
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;
这是我使用并为我工作的方法
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 ");
}
对于希望在一行中执行此操作的任何人(例如,在显示/立即窗口、监视表达式或调试会话中的类似内容中),以下将执行此操作并“漂亮地打印”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());
...或者这里有一个更容易阅读的版本:
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()
);
笔记:
crit
。 如果命名不同,请搜索并替换。getEnabledFilters
而不是getLoadQueryInfluencers()
来实现向后兼容性,因为后者是在更高版本的 Hibernate (3.5???) 中引入的该答案基于 user3715338 的答案(更正了一个小的拼写错误)并与 Michael 对 Hibernate 3.6 的答案混合 - 基于 Brian Deterling 接受的答案。 然后我扩展了它(对于 PostgreSQL),用更多类型替换问号:
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(">", " > ");
}
迈克尔的回答是完美的,只是缺少进口:
import java.lang.reflect.Field;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.SessionImpl;
import org.hibernate.loader.OuterJoinLoader;
import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.persister.entity.OuterJoinLoadable;
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.