HibernateのSQLServer 2005方言(真のページングをサポート)
HibernateではデフォルトのSQL Server方言では真のページングがサポートされていないため、両者が結合するといつもそんなに調和がとれていないと感じます.Oracleにはrownumがあり、MySQLにもlimitとoffsetがありますが、SQL Serverにはtopしかないので、Hibernateは1ページあたりのストライプ数*ページ番号のデータをtopですべてロードしてメモリに偽のページングを行うポリシーを採用しています.しかし、SQL Server 2005以降のマイクロソフトでは、ROWNUMBER()のサポートが提供されているため、パフォーマンスを向上させるために方言を改善する必要があります.また、いくつかの関数を簡単に追加する必要があります.
/**
* <p>SQL Server 2005 。</p>
* <pre>
* 1. SQL Server 2005 , SQL Server 2000
* 2. , top , ROW_NUMBER() 。 sql
* </pre>
* @author Reker
* <p>2008-6-11</p>
*/
public class SQLServer2005Dialect extends CustomSQLDialect {
/**
* 。
*/
public SQLServer2005Dialect() {
super();
registerFunction("bitand", new BitAndFunction());
registerFunction("bitxor", new BitXorFunction());
registerFunction("bitor", new BitOrFunction());
setSupportsVariableLimit(false);
}
/**
* limit ?
* SQL Server top top , ROW_NUMBER() limit
*/
private ThreadLocal<Boolean> supportsVariableLimit = new ThreadLocal<Boolean>();
/**
* <p> limit 。</p>
* @author Reker
* @param first
*/
private void setSupportsVariableLimit(boolean first) {
supportsVariableLimit.set(first);
}
/**
* <p> sql select 。</p>
* @author Reker
* @param sql
* @return int
*/
protected static int getSqlAfterSelectInsertPoint(String sql) {
int selectIndex = sql.toLowerCase().indexOf("select");
final int selectDistinctIndex = sql.toLowerCase().indexOf("select distinct");
return selectIndex + (selectDistinctIndex == selectIndex ? 15 : 6);
}
/*
* @author Reker
* <code>SQLServerDialect<code> supportsLimit true,
* supportLimitOffset supportsVariableLimit false
*/
public boolean supportsLimitOffset() {
return true;
}
/*
* @author Reker
* @see org.hibernate.loader.Loader#bindLimitParameters(PreparedStatement ,int ,RowSelection)
* Hibernate Limit String( limit ) , true,
* (ROW_NUMBER() )( : offset , )
*/
public boolean supportsVariableLimit() {
return supportsVariableLimit.get();
}
/*
* @author Reker
* @see org.hibernate.loader.Loader#getMaxOrLimit(RowSelection,Dialect)
*/
public boolean useMaxForLimit() {
return true;
}
/*
* @author Reker
*/
public String getLimitString(String query, int offset, int limit) {
setSupportsVariableLimit(offset > 0);
if (offset == 0) { //no offset , use top , limit
return new StringBuffer(query.length() + 8).append(query).insert(getSqlAfterSelectInsertPoint(query),
" top " + limit).toString();
}
return getLimitString(query, offset > 0);
}
/*
* @author Reker
*/
public String getLimitString(String sql, boolean hasOffset) {
int orderByIndex = sql.toLowerCase().lastIndexOf("order by");
if (orderByIndex <= 0) {
throw new UnsupportedOperationException(
"must specify 'order by' statement to support limit operation with offset in sql server 2005");
}
String sqlOrderBy = sql.substring(orderByIndex + 8);
String sqlRemoveOrderBy = sql.substring(0, orderByIndex);
int insertPoint = getSqlAfterSelectInsertPoint(sql);
return new StringBuffer(sql.length() + 100).append("with tempPagination as(").append(sqlRemoveOrderBy).insert(
insertPoint + 23, " ROW_NUMBER() OVER(ORDER BY " + sqlOrderBy + ") as RowNumber,").append(
") select * from tempPagination where RowNumber between ? and ?").toString();
}
:
public class BitAndFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
@SuppressWarnings("unchecked")
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException("BitAndFunction requires 2 arguments!");
}
return args.get(0).toString() + " & " + args.get(1).toString();
}
}
public class BitOrFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
@SuppressWarnings("unchecked")
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException("BitOrFunction requires 2 arguments!");
}
return args.get(0).toString() + " | " + args.get(1).toString();
}
}
public class BitXorFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
@SuppressWarnings("unchecked")
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException("BitXorFunction requires 2 arguments!");
}
return args.get(0).toString() + " ^ " + args.get(1).toString();
}
}