浅论Hibernate分页的“真”与“假”

今天有学员问我:“hibernate是真分页么?我今天看了一些帖子,好像是真分页。……”

这个问题不能简单地用“真”或“假”来回答。Hibernate分页的“真”与“假”,取决于所指定的方言(Dialect)。分析了一下Hibernate中相关的源代码,目前的结论是:OracleDialect、Oracle9Dialect、MySQLDialect、MySQL5Dialect是真分页,SQLServerDialect是假分页。

两段简单的测试代码:

1、假设我想查询第1到第10条记录

	public static void main(String[] args) {
		int offset = 0;	//可以理解为起始行号
		int limit = 10;	//可以理解为每页的最大记录数
		String sql = "select * from MyTable";	//原始SQL语句
		Dialect dialect = null;
		//测试Oracle9方言的分页功能		
		dialect = new Oracle9Dialect();
		System.out.println( "Oracle9方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );
		//测试MySQL的分页功能
		dialect = new MySQLDialect();
		System.out.println( "MySQL方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );
		//测试SQLServer的分页功能
		dialect = new SQLServerDialect();
		System.out.println( "SQLServer方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );		
	}

以下是控制台输出:

Oracle9方言生成的SQL分页语句: select * from ( select * from MyTable ) where rownum <= ? MySQL方言生成的SQL分页语句: select * from MyTable limit ? SQLServer方言生成的SQL分页语句: select top 10 * from MyTable

2、现在我想改查第11到第20条记录

	public static void main(String[] args) {
		//现在我想查询第11到20条记录
		int offset = 10;	//可以理解为起始行号
		int limit = 10;		//可以理解为每页的最大记录数
		String sql = "select * from MyTable";	//原始SQL语句
		Dialect dialect = null;
		//测试Oracle9方言的分页功能		
		dialect = new Oracle9Dialect();
		System.out.println( "Oracle9方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );
		//测试MySQL的分页功能
		dialect = new MySQLDialect();
		System.out.println( "MySQL方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );
		//测试SQLServer的分页功能
		dialect = new SQLServerDialect();
		System.out.println( "SQLServer方言生成的SQL分页语句:" );
		System.out.println( dialect.getLimitString( sql, offset, limit ) );		
	}

以下是控制台输出:

Oracle9方言生成的SQL分页语句: select * from ( select row_.*, rownum rownum_ from ( select * from MyTable ) row_ where rownum <= ?) where rownum_ > ? MySQL方言生成的SQL分页语句: select * from MyTable limit ?, ? SQLServer方言生成的SQL分页语句: Exception in thread "main" java.lang.UnsupportedOperationException: sql server has no offset  at org.hibernate.dialect.SQLServerDialect.getLimitString(SQLServerDialect.java:53)  at test.Test.main(Test.java:30)

上述结果是如何产生的呢?让我们来查看一下Hibernate中相关的源代码:

1、Oracle9Dialect.java(OracleDialect.java中的代码与此类似)

	public String getLimitString(String sql, boolean hasOffset) {

		sql = sql.trim();
		boolean isForUpdate = false;
		if ( sql.toLowerCase().endsWith(" for update") ) {
			sql = sql.substring( 0, sql.length()-11 );
			isForUpdate = true;
		}
		
		StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
		if (hasOffset) {
			pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
		}
		else {
			pagingSelect.append("select * from ( ");
		}
		pagingSelect.append(sql);
		if (hasOffset) {
			pagingSelect.append(" ) row_ ) where rownum_ <= ? and rownum_ > ?");
		}
		else {
			pagingSelect.append(" ) where rownum <= ?");
		}

		if ( isForUpdate ) {
			pagingSelect.append( " for update" );
		}
		
		return pagingSelect.toString();
	}

2、MySQLDialect.java(MySQL5Dialect.java)

	public String getLimitString(String sql, boolean hasOffset) {
		return new StringBuffer( sql.length()+20 )
			.append(sql)
			.append( hasOffset ? " limit ?, ?" : " limit ?")
			.toString();
	}

3、SQLServerDialect.java

	public String getLimitString(String querySelect, int offset, int limit) {
		//以下这句就是第2段测试代码出异常的原因了,呵呵。由此可知,对于SQLServer的分页,Hibernate中对于SQLServer的分页,只会使用一次top子句取出前面的部分记录,来通过操作游标来获取指定页的数据,是假分页。
		if ( offset > 0 ) {
			throw new UnsupportedOperationException( "sql server has no offset" );
		}
		return new StringBuffer( querySelect.length()+8 )
			.append(querySelect)
			.insert( getAfterSelectInsertPoint(querySelect), " top " + limit )
			.toString();
	}

SQLServerDialect没有重写public String getLimitString(String sql, boolean hasOffset)方法,以下是该方法在其父类Dialect的实现。

	public String getLimitString(String querySelect, boolean hasOffset) {
		throw new UnsupportedOperationException( "paged queries not supported" );
	}

	public String getLimitString(String querySelect, int offset, int limit) {
		return getLimitString( querySelect, offset>0 );
	}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值