简介
我有一个奇怪的任务 - 编写Hibernate Criteria API(即数据库独立样式)SQL查询类似于
select * from branch b where '2/5/3/' like b.hier_path + '%'
其中+
是连接运算符。连接运算符在MS SQL中是数据库相关的'+','||'在Oracle等中。
我必须使用Criteria API(并且无法切换到HQL)。
问题#1 - 类似操作员
不幸的是,Hibernate只允许基于Java对象属性编写Criteria:
pCriteria.createCriteria(Branch.class).add(Restrictions.like("hierarchyPath", "2/5/3/%"));
相当于
select * from branch where 'hier_path like 2/5/3/%'
我不知道如何交换like运算符的操作数。
问题#2 - 数据库无关连接
SQL代码必须适用于Oracle,MS SQL Server,DB2,Postgres,Sybase,MySQL,HSQLDB,Firebird(以及其他一些新的关系数据库)。
我现在得到的是基于SQL的黑客攻击:
Restrictions.sqlRestriction("? like concat({alias}.hier_path,'%')", "2/5/3/", Hibernate.STRING)
不幸的是,concat
是数据库相关功能,大多数来自上述数据库(Postgres和Firebird除外)。该方法是一种解决方法,不能用作常量解决方案(我将尝试将自定义函数concat
添加到没有它的数据库中。)
结论
有人可以提出改进我的黑客(独立于数据库的SQL)或修改原始CriteriaAPI吗?
更新28.09.12
concat
个函数appears in Postgres 9.1
答案 0 :(得分:1)
您可以编写自己的Criterion实现,它会生成类似于您问题中的SQL子句,除非它使用与条件查询关联的方言来获取相应的concat函数并将连接委托给此数据库相关的concat函数。
答案 1 :(得分:0)
谢谢@JB Nizet。代码灵感来自他的想法:
private class InverseLikeExpression extends SimpleExpression{
private static final String CONST_HQL_FUNCTION_NAME_CONCAT = "concat";
private static final String CONST_LIKE = " like ";
private static final String CONST_LIKE_SUFFIX = "'%'";
private final String propertyName;
protected InverseLikeExpression(String pPropertyName, Object pValue) {
super(pPropertyName, pValue, CONST_LIKE);
propertyName = pPropertyName;
}
@Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
Dialect dialect = criteriaQuery.getFactory().getDialect();
SQLFunction concatFunction = (SQLFunction) dialect.getFunctions().get(CONST_HQL_FUNCTION_NAME_CONCAT);
StringBuffer fragment = new StringBuffer();
if (columns.length>1) fragment.append('(');
for ( int i=0; i<columns.length; i++ ) {
String fieldName = concatFunction.render(Arrays.asList(new Object[] {columns[i], CONST_LIKE_SUFFIX}), criteriaQuery.getFactory());
fragment.append("?").append( getOp() ).append(fieldName);
if ( i<columns.length-1 ) fragment.append(" and ");
}
if (columns.length>1) fragment.append(')');
return fragment.toString();
}
}