hibernate hql 书写 投影查询 问题
开发万步网后台管理系统的用户详情和改名功能时用到了hibernate 其中有多出异常都是由hibernate引起的,下面我就把我遇到的
问题和解决方法一一列出供大家参考。。。
1. hql书写问题
1.1 当我们写hql的时候如果用?占位符方式传参切记如果是字符串的千万不要再hql中对?左右加' ' 我们只写? 下面有那个query
.setString(); 来赋值就行了。
1.2 用hql更新或插入的时候不要用”++“来拼接中文参数那样是不会起到作用的而要用:参数名称 或 ? 这两种方式来赋值英文的参数是可以的 当然这只是针对于hibernate3来讲的 听说 >=hibernate3.1的已经支持用“++”方式操作中文了。
1.3. 用hql执行update 时候不能再为表起别名,在对涉及到复合主键的表update时候也不能用别名.id.属性方式操作属性了。
错误代码:hql="update WanbuRankStar w set w.id.username=:username where w.id.username=:beforename";
正确代码:hql="update WanbuRankStar set username=:username where username=:beforename";
1.4 当用hql进行多表联查时候记住hibernate的hql是不支持inner join on 或 left join on 的 只要带on就不对不管是内,左,右,交叉,全 什 么形式的连接所以我们只能用sql来进行了。
2. 投影查询问题
/**
*根据参数,hql语句查询
*@paramhql
*@paramparams
*@return
*/
publicListgetList(Stringhql,String[]params){
Sessionsession=getSession();
Queryquery=null;
try{
query=session.createQuery(hql);
if(params!=null)
{
for(inti=0;i<params.length;i++){
query.setString(i,params[i]);
}
}
Listlist=query.list();
if(list!=null&&list.size()!=0){
returnlist;
}
}catch(Exceptione){
e.printStackTrace();
returnnull;
}finally{
try{
if(session!=null&&session.isOpen()){
closeSession();
}
}catch(Exceptione){
e.printStackTrace();
}
}
returnnull;
}1.投影查询如果只查询一个字段query.ist() 是返回list<.object> 列表的而每一个object对象里面放的就是改字段的实际类型需要我们转换一下
来用 用例代码:
public List<String> getUsername() {
Stringhql="selectu.usernamefromPreUcenterMembersu";
List<String>ulist=super.getList(hql,null);//getList();方法返回一个包含object对象的List<object>列表。
if(ulist!=null){
returnulist;
}
returnnull;
}//结果输出
public static void main(String[] args) throws UnsupportedEncodingException {
IUserManageDaoum=newUserManageImpl();
List<String>s=um.getUsername();
System.out.println(s);//输出[得实陈其,得实陈庆义,得实陈尚岩,得实陈世华,得实陈薇薇]String类型的数组。
}2. 投影查询查询多个字段(无论是一个表中多个字段还是多个表中的不同字段 有或者是不同库中不同表的一些字段 都适用 我们只需将
涉及到的表和类 映射好就行了)
2.1 当查询多个字段的时候query.list() 返回list<object [] > 列表而每一个object[]就是查询的这几个字段的集合并且也类型也对应着实际字段的类型 我们用的时候可以遍历list<object []> 将其元素封装成我们的实体列表当然这个实体类中的属性类型应该和查询返回字段类型一一对应。 用例代码:
public UserDetailData getUserInfo(String uname) {
String hql="select p.address,u.email,p.gender,p.mobile,j.nickname,p.realname,FROM_UNIXTIME(u.regdate),u.regip,u.username,p.resideprovince,p.residecity,p.residecommunity,p.residesuite,p.residedist " +
"fromPreUcenterMembersu"
+",PreCommonMemberProfilep"
+",JishigouMembersjwhereu.uid=j.uidandu.uid=p.uidandu.username=?";
List<Object[]>uinfo=super.getList(hql,newString[]{uname});
UserDetailDataud=null;
if(uinfo!=null){
for(Object[]u:uinfo){
ud=newUserDetailData();
ud.setAddress((String)u[0]);
ud.setEmail((String)u[1]);
ud.setGender((Byte)u[2]);
ud.setMobile((String)u[3]);
ud.setNickname((String)u[4]);
ud.setRealname((String)u[5]);
ud.setRegdate(u[6].toString());
ud.setRegip((String)u[7]);
ud.setUsername((String)u[8]);
ud.setResideprovince((String)u[9]);
ud.setResidecity(u[10].toString());
ud.setResidedist(u[11].toString());
ud.setResidecommunity(u[12].toString());
ud.setResidesuite(u[13].toString());
}
returnud;
}
returnnull;
}public class UserDetailData {
// Fields 这些类型要与查询后返回的字段类型一致 查询返回字段类型并不是数据库中的字段或你映射好的属性类型而是实实在在返回的类型 例: regdate 在PreUcenterMembers 中为Interger 而此处要为timestamp因为当查询regdate时 FROM_UNIXTIME(u.regdate) 返回的是timestamp 但因为 ud.setRegdate(u[6].toString()); 做了转换所以该类regdate在此处为String,当然因为此查询返回的list<object>所以可以这么写,要是你想用new 方式来实现则该类属性是必须与数据库字段类型对应的。
private String address;
privateStringemail;
privateBytegender;
privateStringmobile;
privateStringnickname;
privateStringrealname;
privateStringregdate;
privateStringregip;
privateStringusername;
privateStringresideprovince;
privateStringresidecity;
privateStringresidedist;
privateStringresidecommunity;
privateStringresidesuite;
publicUserDetailData(Stringaddress,Stringemail,Bytegender,
Stringmobile,Stringnickname,Stringrealname,Stringregdate,
Stringregip,Stringusername){
super();
this.address=address;
this.email=email;
this.gender=gender;
this.mobile=mobile;
this.nickname=nickname;
this.realname=realname;
this.regdate=regdate;
this.regip=regip;
this.username=username;
}//此处省略get/set 方法
}
}
上面这种形式完全可以解决对多表联查的投影查询返回结果封装问题而且对要封装返回结果的实体类属性类型没什么严格限制我们可以根据我们要在页面上展示的数据形式来决定他的类型相对new方式比较灵活,所以推荐使用。
2.2 一种更简单的方式我们可以事先建立好一个类用来封装查询结果,这个类中的属性和数据库的字段类型一致并且有无参构造和对应查询字段的构造,然后采用select new(放入字段对应的属性)from 类名 方式为其实例化,这个query.list()返回的就是list<这个类> 列表,用例代码:
public UserDetailData getUserInfo(String uname) {
/*
*privateStringresideprovince;
privateStringresidecity;
privateStringresidedist;
privateStringresidecommunity;
privateStringresidesuite;
*/
Stringhql="selectnewUserDetailData(p.address,u.email,p.gender,p.mobile,j.nickname,p.realname,u.regdate,u.regip,u.username,p.resideprovince,p.residecity,p.residedist,p.residecommunity,p.residesuite)"+
"fromPreUcenterMembersu"
+",PreCommonMemberProfilep"
+",JishigouMembersjwhereu.uid=j.uidandu.uid=p.uidandu.username=?";
List<UserDetailData>u=super.getList(hql,newString[]{uname});
if(u!=null){
returnu.get(0);
}
returnnull;
}public class UserDetailData {
// Fields 此处regdate 必须为Integer 也就是要和数据库表的字段类型一致了。。。
private String address;
privateStringemail;
privateBytegender;
privateStringmobile;
privateStringnickname;
privateStringrealname;
privateIntegerregdate;
privateStringregip;
privateStringusername;
privateStringresideprovince;
privateStringresidecity;
privateStringresidedist;
privateStringresidecommunity;
privateStringresidesuite;
publicUserDetailData(Stringaddress,Stringemail,Bytegender,
Stringmobile,Stringnickname,Stringrealname,Stringregdate,
Stringregip,Stringusername){
super();
this.address=address;
this.email=email;
this.gender=gender;
this.mobile=mobile;
this.nickname=nickname;
this.realname=realname;
this.regdate=regdate;
this.regip=regip;
this.username=username;
}//此处省略get/set 方法
}
}
这里说明一下吧,我在使用这种方式的时候出现过问题 1.org.hibernate.hql.ast.QuerySyntaxError: Unable to locate class [UserDetailData]
2.在涉及到复合主键的类时也存在问题。