by agate - Published: 2008-04-19 [2:44 下午] - Category: 程序编码

今天再配置hibernate的hbm配置文件的时候使用了one-to-one的关系,主对象使用cascade="all"属性,子对象使用constrained="true"属性。出乎我意料的是在使用hbm2ddl的自动数据库生成功能后发现:子表竟然没有主表的id字段,而主表也没有子表的id字段……为什么呢?那两个对象是如何找到对方的呢?我纳闷了……

呵呵,其实是我配置错了!主表负责生成id,子表有一个外键引用主表的主键,这样就建立了关系【主表子表都有一致的主键值】。但是我竟然在hbm中配置了两个主键生成器……傻了~~~

这个问题是解决了!了解了hibernate中的one-to-one的实现本质,但是我又出现了问题了:要是两个表都要有自己的主键生成器呢?两者可以独立存在,即:存在 (1,0) - to - (1,0) 这样的关联方式~上面那种方式是无法实现这种方式的呀!!!

通过老师的指点,得知需要配置 many-to-one 的属性
<many-to-one name="group" unique="true" column="group_id" fetch="join"/>
特别是这个unique="true"【指明是否该字段具有惟一约束:true具有唯一约束、false不具有唯一约束】就等同于等同于one-to-one了,只是这种是使用额外的一个字段,配置外键,这样就可以实现两个表不同外键的一一对应了!

Tags: [ , ] - Comments: View Comments
by agate - Published: 2008-04-07 [10:03 下午] - Category: 感想, 程序编码

其实也没有什么,内行的人觉得这个是小Case吧。呵呵,这里我不是想说具体的方法,主要是一个感想:
<map
  name="propertyName"
  table="table_name"
  lazy="true|false"
  inverse="true|false"
  cascade="all|none|save-update|delete|all-delete-orphan"
  sort="unsorted|natural|comparatorClass"
  order-by="column_name asc|desc"
  ... 其他属性
>

  <key ... />
  <map-key ... />
  <element ... />
</map>

很多人首先想到的是sort,而且sort已经给出了排序的实现,我们只管天上属性方可。可是我们大多程序员是从学 SQL 转到 Hibernate 这个 ORM 的持久层框架上来的,旧有的观念还是喜欢“order by xxxx desc”。不是说自己怀旧,我发现很多朋友都有这个习惯。所以,一般来说还是建议使用 order-by="column_name asc|desc" 这个属性进行对应表字段的排序配置。在一些特殊时候,我也不建议使用 sort 所自带的属性,建议根据需要来具体实现 java.util.Comparator 这个接口来具体实现自定义排序。

Tags: [ , ] - Comments: View Comments
by agate - Published: 2008-04-06 [4:38 下午] - Category: 程序编码

今天郁闷了2个小时,不停地试验,就是没成功……(试验什么呢?)就是这个,用HQL来查出一个List,条件是某个字段为空。

两个领域类:

public class User {
	private String username;
	private Group group;
	// ------------- setter
	// ------------- getter
}
public class Group {
	private String groupName;
	private List<user> users;
	// ------------- setter
	// ------------- getter
}

让你查数据库中所有group为空的(即加入组的)所有用户。
Read more...

Tags: [ , , , ] - Comments: View Comments
by agate - Published: 2008-03-27 [7:20 下午] - Category: 未分类

使用hibernate开发的朋友都知道,想在使用hibernate进行持久层操作的时候显示对应的sql语句,可以设置hibernate配置文件中的show_sql属性为true来实现这个需求。可是这个功能比较让人失望……他的语句是没错,但是参数值全是?这个带传入的参数符号,每个调试带来什么方便。

在这里,我介绍两个工具包来解决这个问题:[p6spy]和[sql_profiler],具体主页地址在下列给出
p6spy: http://www.p6spy.com
SQL Profile: http://www.jahia.net/jahia/page597.html

我们先来说说p6spy这个工具。在我理解上,他其实就是一个中间驱动,在数据库调用端和实际数据库驱动间做一个代理人的角色,进而加入了一些特有的辅助功能。在这里我们使用的调用端就是hibernate这个数据持久层框架,而数据库驱动我们用的是mysql的默认驱动(一般就是com.mysql.jdbc.Driver)。好,解释完理论开始实际试验:
* 将p6syp.zip(可能是tar文件包)下载好
* 将包中的p6syp.jar包放入你工程的classpath下(web项目就丢到WEB-INF/lib中)
* 将包中的spy.properties放入src目录中
* 修改hibernate配置文件中的数据库驱动为com.p6spy.engine.spy.P6SpyDriver这个中间驱动
* 修改刚才拷贝进src中的spy.properties文件,将realdriver的值设置成原来hibernate中的真正驱动(这里我是com.mysql.jdbc.Driver)

OK搞定!重新编译项目,以保证p6syp.jar和spy.properties编译进入目标地址。

接着就运行你的程序吧,看看控制台……(不要打我啊,我又没说控制台会有变化=.=)。但是看看你的工程根目录下是不是出现了spy.log,这里面就是你要的数据库操作历史,包括了真正传入的参数的具体值。

如果需要改变spy.log日志的生成地址,可以对应设置spy.properties文件的logfile属性给出绝对地址。(logfile默认是spy.log可以加上路径比如logfile = c:\xxx\xxx.log)

好了,似乎hibernate的?参数问题解决了,但是我的好多朋友都抱怨:“怎么这个log格式这么难懂啊!看着好不爽啊!”的确!p6syp的日志格式让人确实分外头大,于是乎介绍个SQL Profile来帮帮痛苦的大众。

照惯例,介绍介绍理论知识。SQL Profiler是一个基于p6syp的一个辅助包,可以实现很多功能,比如根据你的query来帮你生成合适的索引功能。在这里我们使用它的GUI控制介面,说白了就是一个监听器,在你通过p6spy进行数据库操作时,他就会很清晰地跟踪并列出你的操作,比单单查看那个恶心的spy.log来得舒服!既然基于p6syp当然也要用到p6syp.jar这个包咯,只是必须使用我们下载到的sqlprofiler.zip中的spy.properties来替换原来的项目中的那个文件,因为这个文件是定制过的,所谓的监听器也是靠这个文件才起作用的。好,废话说多了毕竟会烦,来说说具体步骤:
* 将下载好的sqlprofiler.zip包中的 sqlprofiler.jar 和spy.properties两个文件解压出来
* 使用新的spy.properties文件替换工程中原来的那个文件
* 根据刚才添加p6syp的过程,修改 spy.properties 文件的参数
* 使用 java -jar sqlprofiler.jar 命令启动sqlprofiler的GUI介面
* 再次启动你的应用吧,看看是不是在进行数据持久层操作的时候sqlprofiler的GUI介面中便出现了对应的操作记录,很清晰的列了出来:

Tags: [ , , , , ] - Comments: View Comments
by agate - Published: 2008-01-31 [5:00 下午] - Category: 程序编码

其实这个问题一句话就可以讲清楚:过滤器气垫的顺序要有规则的!

在 Struts2 (或者 WebWork )这个展现层框架的配置中,我们使用的前端控制器也是使用 Filter 这种方式管理的。一样是在 web.xml 中配置 Filter 的属性,但是由于我们需要由 Spring 托管整个 web 项目的东西,就拿我们这个 OpenSessionInView 的主角 Session 来说吧,如果这个 Session 是由 Spring 在整个程序运行过程中全权负责的话,它啥时候建立,啥时候消亡是 Spring 说的算的,固然必须由 Spring 先建立好在负责发配个对应的适用对象。啥时候建立呢?当时是当请求来的时候咯,当然得让 Spring 先知道吧!所以啊,这个 Spring 的 OpenSessionInViewFilter 的过滤器必须在 Struts2 的 FilterDispatcher 过滤器前配置。

部分代码大致如下:

<!-- Spring的OpenSessionInView实现 -->
<filter>
	<filter-name>openSessionInViewFilter</filter-name>
	<filter-class>
		org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
	</filter-class>
</filter>
<filter-mapping>
	<filter-name>openSessionInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 添加 Struts2 过滤器 -->
<filter>
	<filter-name>struts2</filter-name>
	<filter-class>
		org.apache.struts2.dispatcher.FilterDispatcher
	</filter-class>
</filter>
<filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

这样,Spring 的过滤器就可以先于 Struts2 的过滤器得知请求的到来,提前做好开启 Session 转载 bean 的各项准备任务。

by agate - Published: 2008-01-31 [4:44 下午] - Category: 程序编码

所谓 OpenSessionInView 就是真对 Hibernate 这个数据持久层框架的一个Web下的使用技巧。我们在日常 MVC 程序开发的过程中,如果使用的是 Hibernate 的数据持久层框架,一定常常在业务逻辑层中出现 Session is closed 的 laze 异常,这是由于 Hibernate 的Session 在上一个操作中我们为了保证 Session 已经及时的回收时被我们关掉了。

我们常常被放在一个很尴尬的地带:我们的 DAO 成负责 CRUD 在执行完成后根据我上面说的那个“好习惯”,肯定要把本次操作的Session关掉,或者说在一个点统一被关掉。那么导致的结果是,当我拿到我们需要的对象时,该对象虽然说是数据完整的,但是他可能有一个 Link 对象,这个对象可是不完整的!顶多是一个包含 ID 的空壳。那很自然的,当我们要访问这个对象中的 Link 对象的某个非ID值的时候,必然抛出“Laze”异常。如果我们之前不关闭那个 Session 似乎一般的解决方案没法告诉在下一个使用层的代码何时关闭,虽然可以成功访问 Link 对象,但是长此以往 Session 就大大占用内存了。

如何设计才好呢?在很多 Hibernate 的使用者中就有人很早地提出了 Open Session In View 这个Web开发的重要理念,即让 Hibernate 的 Session 在整个请求周期中生存(包括建立和消亡)。当然这个是理念,实现手段有很多种了,包括什么参数传递、建立辅助包等等等等。这里我们专门提及 Spring 这个万能的整合型框架是如何处理的。

Spring 为我们提供了一个叫做 OpenSessionInViewFilter 的过滤器,他是标准的 Servlet Filter 所以我们把它按照规范配置到 web.xml 中方可使用。使用中我们必须配合使用 Spring 的 HibernateDaoSupport 来进行开发,也就是说,我们的dao层的类都要继承于 HibernateDaoSupport,从中由 Spring 来控制 Hibernate 的 Session 在请求来的时候开启,走的时候关闭,保证了我们访问数据对象时的稳定性。

下面我大概描述一下使用的方法:

1. 在 web.xml 中加入对应过滤器配置文件

<!-- Spring的OpenSessionInView实现 -->
<filter>
	<filter-name>openSessionInViewFilter</filter-name>
	<filter-class>
		org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
	</filter-class>
</filter>
<filter-mapping>
	<filter-name>openSessionInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

2. 在我们访问持久层数据是使用 Spring 为我们的 HibernateDaoSupport 的支持,并使用其中的对应方法操作我们的持久层数据

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class XxxDAO extends HibernateDaoSupport {

	public void save(Xxx transientInstance) {
		try {
			getHibernateTemplate().save(transientInstance);
		} catch (RuntimeException re) {
			throw re;
		}
	}

	public void delete(Xxx persistentInstance) {
		try {
			getHibernateTemplate().delete(persistentInstance);
		} catch (RuntimeException re) {
			throw re;
		}
	}

	public Xxx findById(Integer id) {
		try {
			Role instance = (Xxx) getHibernateTemplate().get("Xxx", id);
			return instance;
		} catch (RuntimeException re) {
			throw re;
		}
	}

	public List findByProperty(String propertyName, Object value) {
		try {
			String queryString = "from Xxx as model where model."
					+ propertyName + "= ?";
			return getHibernateTemplate().find(queryString, value);
		} catch (RuntimeException re) {
			throw re;
		}
	}
}

其实啊,就是调用 HibernateDaoSupport 中的 getHibernateTemplate() 这个模板方法,再使用里头的对应对持久层操作的方法。

这样,Spring 这个优秀的整合型框架就是轻松地帮我们把烦恼已久的问题解决了。

by agate - Published: 2007-09-13 [8:05 下午] - Category: 程序编码

这几天学习几大框架的整合运用,挺简单的,但是有点搞人耐性……出了一些包啊,tag啊之类的小问题。其中就有这个奇怪的问题,在网上找了些资料贴到这里!

一下引用自"吴星"的MSN空间:

很多人在做webwork+spring+hibernate整合时遇到"严重:Error listenerStart"问题
tomcat启动时报如下错误:

2007-5-31 14:27:13 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2007-5-31 14:27:13 org.apache.catalina.core.StandardContext start
严重: Context [/testWSH] startup failed due to previous errors

有一种解决方案是把web.xml文件中的
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
<listener-class>
<listener>
改为
<servlet>
<servlet-name>SpringContextServlet<servlet-name>
<servlet-class>.....<servlet-class>
<load-on-startup>1<load-on-startup>
<servlet>

但这种方法可能会出现其他问题(网上又说会导致其他文件无法打开)。

最终解决方案如下:
我用的是tomcat5.5,配置了日志之后打印出下列信息:

ERROR main org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
Caused by:
java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2328)
at java.lang.Class.getConstructor0(Class.java:2640)
at java.lang.Class.getDeclaredConstructor(Class.java:1953)
……

从日志信息看问题已经很明显了,是applicationContext.xmldataSource问题。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
改为
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

出现"Error listenerStart"一般是applicationContext.xml中的bean加载有问题。在用eclipse做webwork + spring + hibernate 的项目时一般都是用Myeclipse自动生成那些配置文件,而有些相关的jar<!--[if !supportEmptyParas]-->或者文件并没有加载在至项目中,以至引出奇怪的问题,又因为是自动生成的东西所以往往会忽略一些文件,而问题确恰恰是这些生成的文件所致,所以自动化的东西也未必一定是正确的,呵呵……

org.springframework.jdbc.datasource.DriverManagerDataSource 不可以使用连接池。org.apache.commons.dbcp.BasicDataSource作为注入的DataSource源,为了使用 DBCP的功能,必须要将commons-dbcp.jar加入CLASSPATH中,另外还需要commons-pool.jar和commons- collections.jar,这些都可以在Spring的lib目录下找到。
org.springframework.jdbc.datasource.DriverManagerDataSource并没有提供连接池的功能,只能作作简单的单机连接测试。
使用org.apache.commons.dbcp.BasicDataSource时缺少commons-pool.jar所以会出现如题的问题。