Hibernate 学习3

来源:互联网 发布:拍照打字软件 编辑:程序博客网 时间:2024/05/17 01:05

    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;
  }
 }