BSTEK Development Framework2(BDF2) : 2.4. 使用HibernateDao

1.1. 准备

新建一个com.bstek.demo.dao包,在这个包中添加一个继承com.bstek.bdf2.core.orm.hibernate.HibernateDao类的子类HibernateDaoTest,其代码如下:

HibernateDaoTest类代码
package com.bstek.demo.dao;
import com.bstek.bdf2.core.orm.hibernate.HibernateDao;
public class HibernateDaoTest extends HibernateDao {

}

在dorado-home文件夹下,找到context.xml,添加如下配置:

context.xml部分配置源码
<bean id="hibernateDaoTest" class="com.bstek.demo.dao.HibernateDaoTest"></bean>

注意

在将这个名为HibernateDaoTest类配置到Spring时,我们不用考虑其数据源问题,因为HibernateDaoTest类继承了BDF2中的HibernateDao类,所以BDF2一旦发现这个Bean时会自动将相关数据源注入到这个Bean当中。但如果这个HibernateDaoTest类中需要注入其它资源,那么我们在配置这个Bean时就需要将对应的资源注入进来。

 

          新建一个视图文件HibernateDaoTest.view.xml,在HibernateDaoTest.view.xml文件中添加一个name为dataTypeDemoUser的DataType,并设置creationType属性为com.bstek.demo.model.DemoUser。再在视图view节点下添加一个id为dataSetDemoUser的DataSet,并设置dataType属性为dataTypeDemoUser,选择DataType时,勾选Collection选项。最后,在视图view节点下添加一个id为gridDemoUser的DataGrid。

1.2. 使用HibernateDao实现增、删、改

         在视图文件的view节点添加id为updateActionDemoUser的UpdateAction,将其dataResolver属性设为hibernateDaoTest#saveDemoUsers,并将为其添加一个属性dataSet为dataSetDemoUser的UpdateItem子项。然后,在view节点添加一个ToolBar,并为其添加三个按钮ToolBarBotton,分别是添加按钮、删除按钮和保存按钮,并在添加按钮的onClick事件中添加如下代码:

var data=view.get("#dataSetDemoUser.data");
data.insert();

         在删除按钮的onClick事件中添加如下代码:

var entity=view.get("#dataSetDemoUser.data:#");
var updateActionDemoUser=view.get("#updateActionDemoUser");
if(entity){
	entity.remove();
}else{
	dorado.MessageBox.alert("必须选中一行记录!");
}

         在保存按钮的onClick事件中添加如下代码:

var updateActionDemoUser=view.get("#updateActionDemoUser");
updateActionDemoUser.execute();

         最后,需要完成后台服务方法的编写,就是在HibernateDaoTest类中添加如下方法:

@DataResolver
public void saveDemoUsers(Collection<DemoUser> users){
	Session session = this.getSessionFactory().openSession();
	try{
		for (DemoUser user : users) {
			EntityState state=EntityUtils.getState(user);
			if (state.equals(EntityState.NEW)) {
				session.save(user);
			} else if (state.equals(EntityState.MODIFIED)) {
				session.update(user);
			} else if (state.equals(EntityState.DELETED)) {
				session.delete(user);
   			}
		}
	}finally{
		session.flush();
		session.close();
	}
}

         这样,我们就完成了对数据的增删改了。

1.3. doInHibernateSession的使用

         将视图文件的view节点下id为updateActionDemoUser的UpdateAction的dataResolver属性设为hibernateDaoTest#saveDemoUsers2,最后,我们需要完成后台服务方法的编写,就是在HibernateDaoTest类中添加如下方法:

@DataResolver
public void saveDemoUsers2(final Collection<DemoUser> users){
	this.doInHibernateSession(new ISessionCallback<Object>(){
		@Override
		public Object doInSession(Session session) {
			for (DemoUser user : users) {
				EntityState state = EntityUtils.getState(user);
				if (state.equals(EntityState.NEW)) {
					session.save(user);
				} else if (state.equals(EntityState.MODIFIED)) {
					session.update(user);
				} else if (state.equals(EntityState.DELETED)) {
					session.delete(user);
				}
			}
			return null;
		}
	});
}

         代码说明:使用doInHibernateSession方法的好处就是我们不用手动提交事务和关闭Session,这些工作都交由doInHibernateSession方法帮我做了,这样就可以防止程序员忘记或使用不安全可靠的方式提交事务和关闭Session。另外,我们就用doInHibernateSession方法实现了数据的增删改,看起来更加简洁和可靠,减少少了重复易错代码的编写。

1.4. 使用HibernateDao实现各类查询

1.4.1. 无查询条件的查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query1。并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public Collection<DefaultPosition> query1(){
	return this.query("from "+DemoUser.class.getName());
}

         代码说明:HibernateDao对象的query(String hql)方法,通过传入一个hql语句进行查询。该代码的结果是查询DemoUser对应表的所有的记录。

1.4.2. 带Map参数的条件查询

         在 HibernateDaoTest.view.xml视图的view节点中添加一个id为将textUsername的TextEditor和一个查询按钮,并在查询按钮的onClick事件中添加如下代码:

var username=view.get("#textUsername.value");
var dataSetDemoUser=view.get("#dataSetDemoUser");
if(username){
	dataSetDemoUser.set("parameter",{username:username}).flushAsync();
}

         接着需要将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query2。并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public Collection<DefaultPosition> query2(String username){
	Map<String,Object> map =new HashMap<String,Object>();
	String sql="from "+DomeUser.class.getName()+" du ";
	if(username!=null&&!"".equals(username)){
		map.put("username", username);
		sql+=" where du.username=:username";
	}
	return this.query(sql, map);
}

         代码说明:HibernateDao对象的query(String hql,Map<String,Object> parameterMap)方法,可以实现带参数的查询,参数放到Map集合当中,可以放入多查询参数。

1.4.3. 带Map参数的like条件查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query3。并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public Collection<DomeUser> query3(String username){
	Map<String,Object> map =new HashMap<String,Object>();
	String sql="from "+DomeUser.class.getName()+" du ";
	if(username!=null&&!"".equals(username)){
		map.put("username", "%"+username+"%");
		sql+="where du.username like :username";
	}
	return this.query(sql, map);
}

1.4.4. 带DetachedCriteria参数的条件查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query4。并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public Collection<DemoUser> query4(String username){
	DetachedCriteria detachedCriteria=DetachedCriteria.forClass(DemoUser.class);
	if(username!=null&&!"".equals(username)){
		detachedCriteria.add(Restrictions.eq("username", username));
	}
	return (Collection<DemoUser>)this.query(detachedCriteria);
}

         代码说明:此段代码的功能和上一节代码的功能是一样的,只是这里构造查询语句是通过DetachedCriteria对象来实现,这种方式更面向对象,可读性更强。

1.4.5. 无查询条件的分页查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query5,并将dataSetDemoUser的pageSize设置为10,再添加一个分页工具DataPilot,并设置dataSet属性为dataSetDemoUser,最后,在 HibernateDaoTest类里添加如下方法:

@DataProvider
public void query5(Page<DemoUser> page) throws Exception{
	this.pagingQuery(page, "from "+DemoUser.class.getName(), "select count(*) from "+DemoUser.class.getName());
}

         代码说明:HibernateDao对象的pagingQuery(Page<?> page,String queryString,String countQueryString)方法,可以实现无查询条件的查询。

1.4.6. 带Map参数的条件分页查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query6,并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public void query6(Page<DemoUser> page,String username) throws Exception{
	Map<String,Object> map =new HashMap<String,Object>();
	String sql="from "+DomeUser.class.getName()+" du ";
	String sqlCount="select count(*) from "+DomeUser.class.getName()+" du ";
	if(username!=null&&!"".equals(username)){
		map.put("username", username);
		sql+=" where du.username=:username";
		sqlCount+=" where du.username=:username";
	}
	this.pagingQuery(page, sql,sqlCount,map);
}

         代码说明:HibernateDao对象的pagingQuery(Page<?> page,String queryString,String countQueryString,Map<String,Object> parametersMap)方法,可以实现带查询条件的分页查询,条件参数放在Map集合中。

1.4.7. 带DetachedCriteria参数的条件分页查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query7,并在 HibernateDaoTest类里添加如下方法:

@DataProvider
public void query7(Page<DemoUser> page,String username) throws Exception{
	DetachedCriteria detachedCriteria=DetachedCriteria.forClass(DemoUser.class);
	if(username!=null&&!"".equals(username)){
		detachedCriteria.add(Restrictions.eq("username", username));
	}
	this.pagingQuery(page, detachedCriteria);
}

         代码说明:HibernateDao对象的pagingQuery(Page<?> page,DetachedCriteria detachedCriteria)方法,可以实现带条件分页查询,条件参数放在DetachedCriteria集合中。

1.4.8. 结合DataGrid的过滤工具栏实现分页查询

         将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query8,并将gridDemoUser的filterMode和showFilterBar属性分别设置为serverSide和true。接着在 HibernateDaoTest类里添加如下方法:

@DataProvider
public void query8(Page<DemoUser> page,Criteria criteria) throws Exception{
	DetachedCriteria detachedCriteria=this.buildDetachedCriteria(criteria, DemoUser.class);
	this.pagingQuery(page, detachedCriteria);
}

         代码说明:query7的criteria参数是由视图中的DataGrid的filterBar生成传回来的,通过HibernateDao的buildDetachedCariteria方法,可以把criteria类型的对象转换为hibernate能识别的DetachedCariteria对象。

1.4.9. 单值查询queryForInt的使用

         接下来,我们将通过queryForInt方法实现username唯一性验证功能需求。首先,将我们为dataTypeDemoUser的username属性添加一个AjaxVilidator验证,设置其server属性值为hibernateDaoTest#hasUsername,最后,在 HibernateDaoTest类里添加如下方法:

@Expose
public String hasUsername(String username){
	Map<String,Object> map =new HashMap<String,Object>();
	map.put("username", username);
	String sql="select count(du) from "+DemoUser.class.getName()+" du where du.username=:username";
	int count=this.queryForInt(sql, map);
	if(count>0){
		return "用户名已经存在了!";
	}
	return null;
}

         完成后,当编辑username数据列时,如果username的值在数据库中已经存在,将会出现验证不通过。

1.4.10. 大数据量的优化分页查询单实现

         在数据量很大的时候,普通的分页查询的性能瓶颈就是计算机数据的总条数,这时候我们就可以通过query(DetachedCriteria detachedCriteria,int pageIndex,int pageSize)和queryCount(DetachedCriteria detachedCriteria,int pageIndex,int pageSize)相结合来优化我们的分页查询。主要原理:先通过query把用户关心的当前页数据查询出来并返回给客户端供用户使用,当前页数据加载完成后,我们通过AjaxAction向后台发出异步请求,取得数据的总记录数,并更新分页控件。为了实现上述功能,首先,在视图文件的view节点下添加一个id为ajaxActionQueryCount的AjaxAction控件,并设置属性service的值为hibernateDaoTest#queryCount,接着给dataSetDemoUser的onLoadData事件添加如下代码:

var ajaxActionQueryCount=view.get("#ajaxActionQueryCount");
var entityList=self.getData();
ajaxActionQueryCount.execute(function(count){
	entityList.pageCount=count;
});

         完成后,将dataSetDemoUser的dataProvider属性设置为hibernateDaoTest#query9。并在 HibernateDaoTest类里添加如下两个方法:

@DataProvider
public void query9(Page<DemoUser> page,Criteria criteria){
	HttpSession session=DoradoContext.getAttachedRequest().getSession();
	DetachedCriteria detachedCriteria=this.buildDetachedCriteria(criteria, DemoUser.class);
	session.setAttribute("criteria", criteria);
	Collection<DemoUser> users= (Collection<DemoUser>)this.query(detachedCriteria,page.getPageNo(),page.getPageSize());
	page.setEntities(users);
}
	
@Expose
public int queryCount(){
	HttpSession session=DoradoContext.getAttachedRequest().getSession();
	Criteria criteria=(Criteria)session.getAttribute("criteria");
	DetachedCriteria detachedCriteriaCount=this.buildDetachedCriteria(criteria, DemoUser.class);
	int count=this.queryCount(detachedCriteriaCount,0,0);
	session.removeAttribute("criteria");
	return count;
}

注意事项

在对数据总记录条数进行查询的时候,一定要用Criteria对象通过buildDetachedCriteria方法重新生产DetachedCriteria对象传给queryCount的相应的形成。