转自:http://my.oschina.net/lsw90/blog/61816
今天封装了自己的hibernateDao,主要是基于hql的,期间遇到一个问题,最后解决了,和大家分享分享
其他hibernate的查询,像QBE,QBC,这些就不说了,如果用hibernate还是hql最强大,也很容易上手
先上代码,这里只列出主要代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/**
* hql分页查询
*
* @param hql
* 查询字符串
* @param queryResult
* 查询结果
* @param values
* 查询值
* @return
*/
@SuppressWarnings
(
"unchecked"
)
public
QueryResult hqlQueryPage(
final
String hql,
final
QueryResult queryResult,
final
Map values) {
return
hibernateTemplate.execute(
new
HibernateCallback() {
@Override
public
QueryResult doInHibernate(Session session)
throws
HibernateException, SQLException {
Query query = session.createQuery(hql).setFirstResult(
(queryResult.getCurrentPage().intValue() -
1
)
* queryResult.getPageSize().intValue()).setMaxResults(
queryResult.getPageSize().intValue());
Set keys = values.keySet();
for
(Object o : keys) {
query.setParameter((String)o, values.get(o));
}
queryResult.setResultList(query.list());
String countHql =
"select count(*) "
+hql;
//封装查询总记录条数
Long allCount = getAllCount(countHql, values);
//总记录条数
queryResult.setAllCount(allCount);
queryResult.calcuatePage();
//计算总页数
return
queryResult;
}
});
}
/**
* hql查询得到总查询数目
*
* @param hql
* 查询字符串
* @param values
* 查询值
* @return
*/
@SuppressWarnings
(
"unchecked"
)
public
Long getAllCount(
final
String hql,
final
Map values) {
return
hibernateTemplate.execute(
new
HibernateCallback() {
@Override
public
Object doInHibernate(Session session)
throws
HibernateException, SQLException {
Query query = session.createQuery(hql);
Set keys = values.keySet();
for
(Object o : keys) {
query.setParameter((String)o, values.get(o));
}
return
query.uniqueResult();
}
});
}
|
其中QueryResult 是我对结果集的封装,比较简单,根据个人需要还可以添加一些属性
其主要代码如下,省略了get和set方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
com.lsw.permission.common;
import
java.util.List;
public
class
QueryResult
implements
java.io.Serializable {
private
static
final
long
serialVersionUID = 1L;
private
Long allCount;
//总记录条数
private
Long allPage;
//总页数
private
Long currentPage;
//当前页数
private
Long pageSize;
//一页包含的记录条数
private
List<?> resultList;
//查询结果集
public
QueryResult() {
this
.currentPage = 1l;
this
.pageSize = 2l;
// 默认每页显示10条记录
}
//计算总页数
public
void
calcuatePage() {
if
(allCount >
0
) {
allPage = allCount % pageSize ==
0
? allCount / pageSize
: (allCount / pageSize +
1
);
}
}
}
|
分页方法中的values 是参数值
在这里说说使用使用绑定参数的优势:
1.可以利用数据库实施性能优化,因为对Hibernate来说在底层使用的是PrepareStatement来完成查询,因此对于语法相同参数不同的SQL语句,可以充分利用预编译SQL语句缓存,从而提升查询效率。
2.可以防止SQL Injection安全漏洞的产生:
SQL Injection是一种专门针对SQL语句拼装的攻击方式,比如对于我们常见的用户登录,在登录界面上,用户输入用户名和口令,这时登录验证程序可能会生成如下的HQL语句:
1
|
"from User user where user.name='"
+name+
"' and user.password='"
+password+
"'"
|
这个HQL语句从逻辑上来说是没有任何问题的,这个登录验证功能在一般情况下也是会正确完成的,但是如果在登录时在用户名中输入”zhaoxin' or 'x'='x”,这时如果使用简单的HQL语句的字符串拼装,就会生成如下的HQL语句:
1
|
"from User user where user.name='lsw' or 'x'='x' and user.password='admin'"
;
|
显然这条HQL语句的where字句将会永远为真,而使用户口令的作用失去意义,这就是SQL Injection攻击的基本原理。
而使用绑定参数方式,就可以妥善处理这问题,当使用绑定参数时,会得到下面的HQL语句:
1
|
from User user where user.name=
'lsw'
' or '
'x'
'='
'x'
' '
and user.password=
'admin'
;
|
由此可见使用绑定参数会将用户名中输入的单引号解析成字符串(如果想在字符串中包含单引号,应使用重复单引号形式),所以参数绑定能够有效防止SQL Injection安全漏洞
说说我遇到的问题吧,主要是用到
1
|
public
Query setParameter(String name, Object val)
throws
HibernateException;
|
这个方法,hibernate会根据查询语句然后确定插入的值的类型的,看看这个方法的实现吧
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
Query setParameter(String name, Object val)
throws
HibernateException {
if
(val ==
null
) {
Type type = parameterMetadata.getNamedParameterExpectedType( name );
if
( type ==
null
) {
type = Hibernate.SERIALIZABLE;
}
setParameter( name, val, type );
}
else
{
setParameter( name, val, determineType( name, val ) );
}
return
this
;
}
|
其实最后也是调用这个方法:
1
|
public
Query setParameter(String name, Object val, Type type);
|
现数据库中有三张表,角色,用户,角色-用户表;
其中角色-用户表java代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
class
RoleInUser
implements
java.io.Serializable{
private
static
final
long
serialVersionUID = 1L;
@Id
@ManyToOne
@JoinColumn
(name =
"user_id"
)
private
User user;
@Id
@ManyToOne
@JoinColumn
(name =
"role_id"
)
private
Role role;
@Column
(name =
"remark"
, nullable =
true
, length =
100
, columnDefinition =
"varchar(100)"
)
private
String remark;
}
|
现在已知用户,需到数据库查询用户拥有哪些角色,我起初写的查询语句为:
1
2
3
4
|
String hql =
"from Role r where r.roleId in (select riu.role from RoleInUser riu where riu.user = :user)"
;
Map<String, Object> values =
new
HashMap<String, Object>();
values.put(
"user"
, user.getUserId());
roleDao.hqlQueryPage(hql, queryResult, values);
|
执行查询,却一直查询不到,最后一直做测试,原来发现是hibernate设置值进查询语句出现的问题,根据上述查询语句,hibernate会将:user设置为User类型,而不是想要的整型,请仔细看代码的变化,最后将查询语句修改为
1
2
3
4
|
String hql =
"from Role r where r.roleId in (select riu.role from RoleInUser riu where riu.user.userId = :userId)"
;
Map<String, Object> values =
new
HashMap<String, Object>();
values.put(
"userId"
, user.getUserId());
roleDao.hqlQueryPage(hql, queryResult, values);
|
解决问题。
总结,hql是面向对象的查询语言,与我们平时用的关系型数据库语言不一样,需仔细,多看看hibernate的文档和源码