brent's hut

关于Hibernate的记录

我和hibernate的第一次亲密:

我用的数据库是mssql,用middlegen稀里哗啦生成一堆文件。编译运行,hibernate报告一堆异常,大概是说,MSSQL JDBC抗议说他不能读取已经读过的字段,所以hibernate不能生成需要的collection。最后icepeak传给我一个JSQLConnect,终于把这个问题解决了。

设置hibernate生成定制的主键ID

在和hibernate亲密接触之后,我想按照自己的想法来设计组织机构.对于Organ,Department,Post和Stuff.我希望这样设计主键的ID,类型都是char,格式为:
Organ0000000001
Dept0000000001
Post0000000001
Stuff0000000002

修改middlegen产生的.hbm.xml文件:

......
<class
    name="com.abcdefg.hibernate.Organ"
    table="Organ"
>
    <id
        name="organId"
        type="java.lang.String"
        column="OrganId"
    >
        <generator class="hbtest.PkGenerator">
            <param name="table">PKGenerator_table</param>
            <param name="column">NextOrganId</param>
            <param name="max_lo">0</param>
 </generator>
    </id>
......
</class>

找到的资料说:"如果需要采用定制的主键产生算法,则在此处配置主键生成器,主键生成器必须实现net.sf.hibernate.id.IdentifierGenerator 接口".我找了半天都没找到net.sf.hibernate.id.IdentifierGenerator.其实只要从hibernate自身提供的一些主键生成器继承就可以,从hibernate的源文件可以看到这些主键生成器实现的接口是org.hibernate.id

我的PkGenerator.java文件:
package hbtest;

import org.hibernate.id.TableHiLoGenerator;
import java.io.Serializable;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.IntegerType;
import org.hibernate.util.PropertiesHelper;

public class PkGenerator
    extends TableHiLoGenerator {

  private String columnName;

  public void configure(Type type, Properties params, Dialect d) {
    super.configure(new IntegerType(), params, d);
    this.columnName = PropertiesHelper.getString(COLUMN, params,
                                                 DEFAULT_COLUMN_NAME);
  }

  public synchronized Serializable generate(SessionImplementor session,
                                            Object obj) throws
      HibernateException {
    Object result = super.generate(session, obj);
    String strNumber = result.toString();
    StringBuffer strResult = new StringBuffer("");
    if (columnName.compareToIgnoreCase("NextOrganId") == 0) {
      strResult.append("Organ");
    }
    else if(columnName.compareToIgnoreCase("NextDeptId") == 0){
      strResult.append("Dept");
    }
    ......

    if (strNumber.length() > 10)
      throw new HibernateException("The generated " + strResult +
                                   "Id is too long!");
    else if (strNumber.length() == 10) {
      strResult.append(strNumber);
    }
    else {
        int count = 10 - strNumber.length();
        while (count > 0) {
        strResult.append("0");
        count--;
      }
      strResult.append(strNumber);
    }
    return strResult.toString();
  }
}

实现了以后发现在这些主键前面添加"Organ","Dept"之类的并不是特别有用,而且我开始的出发点还是错的.也许我应该建立更多的映射表...?

Hibernate的初级指南

hibernate贴心的地方

验证了一下,我的担心终于被证实是多余的了,我担心的是hibernate会为同一条记录产生一堆不同的对象。
我觉得使hibernate真正实用的条件:为同一条记录产生的多个对象是同一个对象。当然前提是设置了主键。
hibernate也许是将当前读出的对象保存在一个哈希表,在读一个新对象的时候会先从这个哈希表中查找。好像这并不是多困难的事情。
 
但多线程情况下就有所不同了..
因为Hibernate说(对于Session):
It is not intended that implementors be threadsafe. Instead each thread/transaction should obtain its own instance from a SessionFactory
 
Session并没有要求是线程安全的,而通过实验表明记录和对象的一一对应只存在于同一个Session中。这也是hibernate的文档中HibernateUtil类使用ThreadLocal的原因吧。
所以对于多线程的情况,在不同的线程中,对应同一条记录的对象是不同的。

(另hibernate有二级缓存,是全局的缓存,可以用来减少数据库负载,二级缓存的功能需要设置才会启用)

Hibernate中修改多对多关系的属性:

<middlegen>项中有如下<table>配置:
   <table generate="true" name="Post"/>
   <table generate="true" name="Stuff"/>
   <many2many>
      <tablea generate="true" name="Post" />
      <jointable name="PostStuff" generate="true" />
      <tableb generate="true" name="Stuff" />
   </many2many>
即表Post与Stuff的关系是多对多,而且表PostStuff中一个字段时JoinDate:员工加入部门的时间。

我现在想加入一条Post记录,一条Stuff记录,然后设置Stuff加入Post的时间。经过多次尝试,代码如下:
   ...
   Dept dept = ....
   Post post = new Post("third post1",dept,new HashSet(),new HashSet());
   Stuff stuff = new Stuff("third stuff",new HashSet(),new HashSet());
   session.save(stuff);
   session.save(post);
   post.getStuffs().add(stuff);
   //stuff.getPosts().add(post);/*不能与上一行并存,会有异常,因为是一个死循环?*/
   PostStuff postStuff = (PostStuff)session.createQuery("from PostStuff where PostId = '" + post.getPostId() + "' AND StuffId = '" + stuff.getStuffId() + "'").iterate().next();
   postStuff.setJoinDate(new java.util.Date());
   tx.commit();
   HibernateUtil.closeSession();
   ...

post.getStuffs().add(stuff);在两个Save()之后,而且没有调用session.flush(),但是createQuery能够得到PostStuff说明HQL是先从内存中查询数据的。不需要调用session.flush()来写入数据库,因为tx.commit()会自动调用。

可以用session.persist(stuff)来替换session.save(stuff),我晕,明明save有说明会创建id,而persist没有说明会创建id的(但实际上是创建了)。

试图用以下替代post.getStuffs().add(stuff):
   PostStuff postStuff = new PostStuff();
   postStuff.setPost(post);
   postStuff.setStuff(stuff);
   postStuff.setJoinDate(new java.util.Date());
   postStuff.setComp_id(new PostStuffPK(post.getPostId(),stuff.getStuffId()));
   post.getPostStuffs().add(postStuff);
结果是虽然没有异常,但并不会添加一条关系。

或者

   session.flush();
   session.refresh(stuff);
   ((PostStuff)(stuff.getPostStuffs().iterator().next())).setJoinDate(new java.util.Date());
注意这些代码只是用来作试验用的

posted on 2005-12-19 09:23 brent 阅读(517) 评论(0)  编辑 收藏 引用 所属分类: Java


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理