Hibernate果真有点“冬天”的感觉, 这不,我被一个小小的问题缠了大半天还是没有解决, 额外的收获是对hibernate有了更深的认识。
1、 我的问题:
public static List queryUser(String accountid){
String hql="select a from Account a where accountid=?";
Object[] paramValues = {accountid};
List list = queryObject(hql,paramValues);
Account acc = (Account)list.get(0);
String id = acc.getAccountid();
String pwd = acc.getPassword();
Double remain = acc.getRemaining();
System.out.println("得到的list为: "+id+","+pwd+","+remain);
return list;
}
我写的这个方法经测试可以返回我想要的答案, 但问题是我从show_sql中查看其有两条SQL语句, 分别为:
a、 Hibernate: select account0_.ACCOUNTID as ACCOUNTID0_, account0_.PASSWORD as PASSWORD0_, account0_.REMAINING as REMAINING0_ from SYSTEM.ACCOUNT account0_
b、Hibernate: select trade0_.ID as ID1_, trade0_.ACCOUNTID as ACCOUNTID1_, trade0_.TRADETYPE as TRADETYPE1_, trade0_.TRADEMONEY as TRADEMONEY1_, trade0_.TRADETIME as TRADETIME1_, trade0_.TRADEDIGEST as TRADEDIG6_1_ from SYSTEM.TRADE trade0_
但是我只想要其返回一条sql语句, 不要其去查询从表。 而且我在Account.hbm.xml中的<set>元素中将lazy设置了为"true".以往我也是采取延迟检索机制实现的, 但是这次其还是去查询了从表, 为什么????
由于我的配置文件是使用了反转自动生成的, 其中many端也即Trade端的Trade.hbm.xml中自动生成了一个fetch="select"属性。 我知道fetch="select"是会返回两条sql语句, 但那是在one端的lazy="false"的前提下吧? 更何况我将fetch的值修改为join,运行结果却还是如此........
2、 hibernate的<set>元素的inverse属性如何理解?
引用了《深入浅出 Hibernate》:在Hibernate 语义中,inverse指定了关联关系中的方向。关联关系中,inverse=false 的为主动方,由主动方负责维护关联关系。
这里所说的关联关系按我理解指的是:为了维持两个实体类(表)的关系,而添加的一些属性,该属性可能在两个实体类(表)或者在一个独立的表里面,这个要看这双方直接的对应关系了:
ps:这里的维护指的是当主控放进行增删改查操作时,会同时对关联关系进行对应的更新。
一对多:该属性在多的一方。应该在一方的设置 inverse=true ,多的一方设置 inverse=false(多的一方也可以不设置inverse属性,因为默认值是false),这说明关联关系由多的一方来维护。原因:该关联关系的属性在多的一方的表中,在维护关联关系的时候在多的一方容易维护。
多对多:属性在独立表中。在任意一方设置inverse=true,另一方inverse=false;原因:如果两个多设置为true 的话,表示两方都对关联关系表(独立表)进行了同样的维护,其实只要一方维护就行了,效率上来说,这样设置是合理点的。
一对一:其实是一对多的一个特例,inverse 的设置也是一样的,主要还是看关联关系的属性在哪一方,这一方的inverse=false。
多对一:也就是一对多的反过来,没什么区别。
3、hibernate中的fetch属性?
fetch参数指定了关联对象抓取的方式是select查询还是 join查询,select方式时先查询返回要查询的主体对象(列表),再根据关联外键id,每一个对象发一个select查询,获取关联的对象,形成 n+1次查询;而join方式,主体对象和关联对象用一句外键关联的sql同时查询出来,不会形成多次查询。如果同时fetch="join" lazy="true"经过测试发现fetch有效。另外,在hql查询中配置文件中设置的join方式某些情况下是不起作用的(而在所有其他查询方式如get、criteria或再关联获取等等都是有效的),会使用select方式,除非你在hql中指定join fetch某个关联对象。
4、 关于设计数据时是否要建立主外键约束的问题?
我建议,在编码阶段可以先不加外键限制,在进入测试阶段可以把外键加上,这样可以更好测试,万一代码里没有处理好的地方,比较容易看出来的。
至于主键还是非常有必要的。如果你的项目极特殊的话,当然也可以说在理论什么约束都可以没有。
外键有时不建,能大大减少代码复杂程度。
就像我前几天做的学生管理系统,每个表之间都没建外键。
只在数据上有关联,这的确能加快系统性能,也便于数据添加。
但我建议还是在编码的时候不加,在测试的时候加。
毕竟要为系统的长久性打算嘛!
5、 关于手动实现 延迟加载
其实手动延迟加载比较好,又提高了系统的性能,想要的时候就加载,不要就不用具体如下:在取数据的时候代码中写一段就行:
Hibernate.initialize(s.getGradeclass());就行,就代表对象s中加载gradeclass这张表的数据!
6、 使用hibernate查询时出现中文乱码?
String hql = "select p from Products p where p.category.categoryName=:'笔记本'";
查询出来没有结果, 查看sql语句出现了中文乱码.
上网搜索得知:hibernate的hql不支持中文查询参数
于是当我改成如下的方式时乱码问题解决了:
query.setString("name", "笔记本"); 或者
query.setParameter("name", "笔记本");
7、 如何查看Oracle数据库的编码格式?
select userenv('language') from dual;
8、 在hibernate.cfg.xml中设置数据库的编码格式?
<!-- 设置数据库的编码 -->
<property name="connection.useUnicode">true</property>
<property name="connection.characterEncoding">GBK</property>
9、 关于myEclipse下补全键的设置?
比如打印sysout 按下 alt+/ 就出现System.out.println()。你要找到自定义模板库。加入你自己的定义就可以了
设置方式:
Window - Preferences - Java - Editor - Templates,这里你可以看到所有的eclipse的快捷方式
在这里进行设置, 以后使用时就输入你自己设置的再按 alt+/就OK
10、 关于HQL查询与QBC查询的初步探讨
HQL和QBC都是查询检索的方式,最开始,我一直使用的都是HQL,因为以前一直用的都是SQL,觉得这东西和SQL差不多,上手很快。后来又用QBC,因为QBC是基于接口和类的对象化查询,使代码变得很清晰,很整洁。
下面是查询用户表中,id为2,年龄等于21,并且名字以J开头的两种查询语句,它们的结果都是一样,只是不同的表现方式。
HQL:
Query query = session
.createQuery("from User u where u.id = 2 and u.age = 21 and u.name like 'J%'");
List list = query.list(); QBC:
Criteria criteria = session.createCriteria(User.class);
List list = criteria.add(Expression.eq("id", 2)).add(
Expression.eq("age", 21)).add(Expression.like("name", "J%"))
.list();
如果查询再复杂一点,需要关联多张表,那上面这个HQL语句就会显得很复杂,比较难以阅读。对于QBC来说,需要再加一个createCriteria(),返回一个criteria新实例,比如说,用户表与帐号表关联,需要根据帐号表中的帐号,开户时间,金额等信息进行查询,可以写成下面的形式:
List list = criteria.add(Expression.eq("id", 2)).add(
Expression.eq("age", 21)).add(Expression.like("name", "J%"))
.createCriteria("account", "a").add(
Expression.eq("a.account_id", 112546)).add(
Expression.eq("a.start_date", "2008-8-30")).add(
Expression.eq("a.money_sum", 1000)).list();
account是用户表中建的与帐号表的关联对象属性,a是别名。我为了便于说明,用的都是固定值,并且条件判断也都是eq(等于),其实在实际开发中,这是不可能的,这些值全都会被变量所代替。add方法也不用写在一起,可以分开来,特别是在查询中,需要对传入的参数进行检验,这时就需要一个条件一个条件的往上加。
这样看来,好像QBC比HQL要好一些,大象是这么认为的:HQL简单、灵活,QBC整洁、方便,不能说谁好谁不好,否则大名鼎鼎的Hibernate为什么要支持这两种检索方式呢?
根据本人做开发的情况来看,在需要多表关联查询的时候,如果POJO类之间建立一对多或多对多这样的关联关系,效率是很低下的,就算你设置了延迟检索,也会感觉很慢。而且在实际开发中,我还发现,在数据库中建立外键是一件非常吃力不讨好的事情,因为很多时候出错都是跟外键有关系,主要体现在修改和删除。而POJO之间建立对象关系,则会增加编码的复杂程度,提高出错机率,另外还会增加用户等待的时间。这是大象以前开发时所经历过的,所以后来的项目中,对于数据库中的每个表,只给一个流水号主键,不在建立其它的外键关系,而在POJO中,只设定最原始的属性与表中的字段对应,对于需要做多表查询的情况,建立视图,把需要查询的字段属性与要在列表中显示的字段属性都放在视图POJO中,这样,不管是HQL还是QBC,一个类就可以解决问题,而且对视图查询可比使用表之间的关联关系查询要快很多。
在项目中,到底采取哪种检索方式,关键还得看项目负责人。比如大象现在做的这个项目就规定了,必须使用QBC,而且除报表外,不准使用视图。呵呵,这样的情况,只能在POJO之间建立关联关系了,不过能少建,我们还是会尽量的减少类之间的关联,好在现在开发用的JDK是5.0的,因此我们可以使用注解的方式定义一些临时对象属性,这样也省掉了以前必须得写的hbm.xml文件,有时也采用仿视图的方式用JavaBean来封装一些对象和属性。
11、 以前碰到一个延迟加载问题,就是lazy=true的时候出现session close后读取不到数据,出现错误,现在这里总结一下经验:
一:lazy=false,这种方式效率底,不合适.
二:fetch=join,这种方式和上一种差不多.
三:使用OpensessionInView,这种方式事务管理有很多不稳定性(主要存在事务边界的不确定性),此时lazy=true.
四:就是在DAO层把数据read出来,lazy=true,这样不符合ORM关联查询思想.
五:个人认为最佳方案,就是使用动态外连接查询,此时lazy=true.
12、 映射文档之延迟抓载?
通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
也许你喜欢仅仅通过条件查询,就可以改变get() 或 load()语句中的数据抓取策略。例如:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
也许你喜欢仅仅通过条件查询,就可以改变get() 或 load()语句中的数据抓取策略。例如:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
文档中的意思就是,你可以通过设置FetchMode来改变是否延迟加载关联关系,
FetchModel.JOIN表示在SELECT语句使用OUTER JOIN(外连接)来 获得对象的关联实例或者关联集合,这个显然不是延迟装载的,你也可以设置为FetchModel.SELECT,这个会另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"禁止 延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。明显这个是延迟装载的。
总结:
需要延迟装载,不用任何设置,默认就是这样
不需要延迟装载,直接在程序中查询之前设置FetchModel为FetchModel.JOIN
13、 老师以前说在调用save()保存对象时不需要用事物, 但是我今天的试验却发现假如不用事物的话, 即使调用隔了session.flush()与session.close()方法其也只在控制台打印书insert into...的sql语句, 但是并没有真正的插入数据库, 当我用了事物时才真正的插入了数据库.
解决方案, 在hibernate中配置<property name="connection.autocommit">true</property>
OK。
注意: System.out.println(session.getFlushMode());可以查看数据的提交方式
还有就是假如你首先开始了事物session.openTrasaction();的话而调用了save()方法后没有tx.commit()的话, 则即使配置了自动提交则还是不会保存进数据库。 开始了就要结束
14、 hibernate 查询表中最后一条语句的hql怎么写?!
select * from table
where rownum<(select count(*)+1 from table)
minus
select * from table
where rownum<(select count(*) from table)
也可以简化为
select * from table
minus
select * from table
where rownum<(select count(*) from table)
效果是一样的
切记rownum是伪列 只能用<
顺便给你求第X行的通用SQL语句
select * from table where rownum<X+1
minus
select * from table where rownum<X
15、 hibernate3的HQL 是面向对象的语法,已经支持 update ,delete from语句,但
目前还不支持insert into语句.
做项目有时候的确需要写原生的sql来方便web开发.
下面是在hibernate Dao类里
1) 调用jdbc的执行方法,最后可以在action里调用它...
比如:
public void InsertData(String MytblName,String Mytitle,String Mycode,String Mycoments){
log.debug("insert into dataitems ---drs");
//使用了原生sql语句
try{
//将传入的类名处理成表名 例如:CTable----〉C_Table
MytblName = "C_"+MytblName.substring(1,MytblName.length());
//插入表 的 sql
String Insert_SQL ="INSERT INTO "+MytblName+
" (C_Title,C_Code,C_Comments)"+
" VALUES ('"+Mytitle+"','"+Mycode+"','"+Mycoments+"')" ;
//获得当前session的数据库连接
Connection CurConn = getSession().connection();
//System.out.println(Insert_SQL);
//生成一个Statement对象
PreparedStatement ps = CurConn.prepareStatement(Insert_SQL);
//执行查询
ps.execute();
//关闭该对象
ps.close();
getSession().flush();
log.debug("insert into dataitems successful");
}
catch (SQLException Re ){ // 异常
log.error("insert into dataitems failed", Re);
Re.printStackTrace();
}
}
2) 用Hibernate本身支持的执行原生SQL:
public List excuteMySQL(String strTableName,String fieldName, Object value) {
log.debug("这里是日志");
try {
String queryString = "select top 1 "+strTableName+"_GUID,"+fieldName+" from "+strTableName+" as model where model."
+ fieldName + "= ?";
Query queryObject = getSession().createSQLQuery(queryString);
//注意是 createSQLQuery 不是createQuery
//后者是执行HQL
queryObject.setParameter(0, value);
return queryObject.list();
} catch (RuntimeException re) {
log.error("execute failed", re);
throw re;
}
}