我和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());
注意这些代码只是用来作试验用的