图书管理系统的一个需求:需要对图书进行分类,分类之间有隶属关系,这样所有的分类就形成了一棵“分类树”。为了快速地找到某个分类下的所有子分类(包括孙子,曾孙分类),
每个分类需要保存一个序列号,这个序列号反应了本分类在分类树中的位置。举个例子,有如下四个分类“食物,水果,肉类,香蕉”,其中,香蕉属于水果,水果属于食物,肉类也属于食物。构成的分类树是这样的
食物--水果--香蕉
|--肉类
可以这样分配序列号(每一代分类用两位数字表示,可以根据需要用更多或更少的位数):
食物(00)--水果(0000)--香蕉(000000)
|--肉类(0001)
每个分类都继承父分类的序列号,在加上两位的按递增的序号。如果要找到食物的所有子分类,就可以用 类似“from 分类表 where 序列号 like 00%”的sql语句快速找到。
这种设计方法也可以适用于其他有树结构的领域模型,如组织机构。
用EJB3.0的实体Bean来实现这个模型基本上是很简单,详细的代码就不列出来。比较麻烦的是这个序列号字段不能使用数据库提供的序列号生成器来生成,必须得我们自己来生成。生成序列号的步骤:
1,得到父分类的序列号supercsn
2,根据supercsn到数据库中查找兄弟分类的最大序列号maxcsn
3,把maxcsn的最后两位的数字加一,作为本分类的序列号。
生成序列号的最好时机就是在实体bean的PrePersist事件中,也就需要在PrePersist事件中访问数据库。这时,你是不是很自然得想到用这样的代码得到EntityManager:
@Entity
public class Category implements Serializable {
@PersistenceContext
EntityManager em;
..........
}
当时,很不幸的是:这样得不到EntityManager,每次都是Null.(如果哪位大哥能这样获得的,请告诉我一下)。经过查询Jboss的文档,发现了另外一个变通的办法。Jboss可以把EntityManagerFactory保存到jndi中去,因此就可以从jndi查找出EntityManagerFacotry来,再用它来创建出EntityManager来。
为了达到上述目的,需要修改EJB工程的Persistence.xml文件
<persistence-unit name="BookStorePU" transaction-type="JTA">
<jta-data-source>java:bookshopDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<!--将entity manager factory 绑定到 java:/BookStorePUEMF-->
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/BookStorePUEMF" />
</properties>
</persistence-unit>
在实体bean中就可以得到EntityManagerFactory了。
@PrePersist
public void setUpCSN() {
log.info("Persist "+getCname()+" begin");
String supercsn = "";
String mycsn="";
Properties env=new Properties();
if (this.getSuperCategory() != null) {
supercsn = getSuperCategory().getCsn();
log.info("super category "+getSuperCategory().getCname()+" CSN:"+supercsn);
}
if (supercsn==null){
supercsn="";
}
EntityManager em=null;
EntityManagerFactory emf=null;
int csnlen = supercsn.length()+Consts.CATEGORY_CSN_LEVELLENTH;
try {
env.load(getClass().getResourceAsStream("/jndi.properties"));
Context ctx= new InitialContext(env);
Object o=ctx.lookup("java:/BookStoreEM");
emf=(EntityManagerFactory)o;
em=emf.createEntityManager();
String eql="select max(c.csn) as m from Category c where length(c.csn)="+csnlen+" and c.csn like'"+supercsn+"%'";
Query qry=em.createQuery(eql);
List rstList=qry.getResultList();
String maxcsn=(String) rstList.get(0);
log.info(getCname()+" maxcsn:"+maxcsn);
if (maxcsn==null){
mycsn=supercsn+supplyZero(Consts.CATEGORY_CSN_LEVELLENTH);
}else{
mycsn=genNextCSN(maxcsn,csnlen);
}
this.setCsn(mycsn);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
em.close();
}
//log.info("Persist "+getCname()+" begin.");
}
补充一下,需要在类路径下放一个jndi.properties的文件,以保证能够访问到应用服务器的jndi