题库实际是一个资料库,里面分门别类的存放着一些主题和内容。主题通常是一个问句,所以被称为题库。这个系统主要是对数据的创建,删除,修改,和查询。我主要做的是查询,修改和删除。
在业务对象的设计上有一个有趣的问题。在我之前,业务对象已经设计好了,而且hibernate的映射文件也已经完成了。最初的设计者把这些表的所有字段,包括外键全部用property映射了。这样做就是放弃了hibernate的对象关联的功能。当我开始工作后,新的需求要求对一些关联数据进行访问,为了让其它已有的程序能够正常工作,我在hibernate映射文件里添加了many-to-one的映射,并且在类中添加了引用。已有的部分没有任何改动。代码和映射文件如下:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Subject.java<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public class Subject {
private String id;

private String baseCode; //外键

private String itemCode; //外键
private SubjectBaseCode base; //引用
private SubjectItemCode item; //引用
private String subDate;

private String aboutUser;

private String aboutProduct;
private Blob subjectTitle;

private Blob subjectContent;
private String createUser;
private Timestamp createTime;

//其它略
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Subject.hbm.xml<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping
>
<class
name="com.xxxx.model.question.Subject"
table="mis_tb1031_subject"
>

<id
name="id"
column="ID"
type="java.lang.String"
length="32"
unsaved-value="null"
>
<generator class="uuid.hex">
<!--
To add non XDoclet generator parameters, create a file named
hibernate-generator-params-Subject.xml
containing the additional parameters and place it in your merge dir.
-->
</generator>
</id>

<property
name="createTime"
type="java.sql.Timestamp"
update="true"
insert="true"
column="CRT_TIME"
/>

<property
name="createUser"
type="java.lang.String"
update="true"
insert="true"
column="CRT_USER"
length="5"
/>

<property
name="aboutProduct"
type="java.lang.String"
update="true"
insert="true"
column="ABOUT_PRODUCT"
length="500"
/>

<property
name="aboutUser"
type="java.lang.String"
update="true"
insert="true"
column="ABOUT_USER"
length="500"
/>

<property
name="baseCode"
type="java.lang.String"
update="true"
insert="true"
column="BASE_CODE"
length="32"
/>

<property
name="itemCode"
type="java.lang.String"
update="true"
insert="true"
column="ITEM_CODE"
length="32"
/>

<property
name="subDate"
type="java.lang.String"
update="true"
insert="true"
column="SUB_DATE"
length="10"
/>

<property
name="subjectContent"
type="java.sql.Blob"
update="true"
insert="true"
column="SUBJECT_CONTENT"
/>

<property
name="subjectTitle"
type="java.sql.Blob"
update="true"
insert="true"
column="SUBJECT_TITLE"
/>

<many-to-one
name="item"
class="com.xxxx.model.question.SubjectItemCode"
cascade="none"
outer-join="true"
update="false"
insert="false"
column="ITEM_CODE"
/>

<many-to-one
name="base"
class="com.xxxx.model.question.SubjectBaseCode"
cascade="none"
outer-join="true"
update="false"
insert="false"
column="BASE_CODE"
/>

<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Subject.xml
containing the additional properties and place it in your merge dir.
-->

</class>

</hibernate-mapping>
由于对外键的重复映射,给以后的开发带来了一点小小的麻烦。为了保持原来程序的完整,对象之间的关联的建立,还是用原来的baseCode,itemCode这两个String类型的属性。而关联的对象的属性base,item仅仅在查询时有用。所以我把base,和item的映射属性update和insert都设为false,否则hibernate在初始化时会报错。如下:
<many-to-one
name="item"
class="com.xxxx.model.question.SubjectItemCode"
cascade="none"
outer-join="true"
update="false"
insert="false"
column="ITEM_CODE"
/>

<many-to-one
name="base"
class="com.xxxx.model.question.SubjectBaseCode"
cascade="none"
outer-join="true"
update="false"
insert="false"
column="BASE_CODE"
/>
在开发的过程中,我使用了Spring的HibernateTemplate。我们的Service对象的所有方法都使用了Spring的申明式事务。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="unlock*">PROPAGATION_REQUIRED</prop>
<prop key="persist*">PROPAGATION_NEVER</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

<bean id="questionService" parent="txProxyTemplate" >
<property name="target">
<bean class="com.xxxx.service.impl.QuestionServiceImpl">
<property name="questionDao">
<ref bean="questionDao"/>
</property>
</bean>
</property>
</bean>
</beans>
一切基本上没有什么问题。当我在做一个修改功能时问题出现了。最早完成的查询功能,查询结果用一个列表返回。后来为了使其可以删除和更新记录,所以在显示结果的表格里又新增了一列用来放删除和修改按钮。当记录被更新后,重新返回到这个列表中。所以我的action要完成两个功能,更新记录,再查询。也就是要调用2次service中的方法。一次更新,一次查询。但是问题出现了,当对象的外键属性的内容更新后,重新返回列表时,里面的内容并没有变化,但是重新查询后,结果却是正确的,而其它的字段都正常。
于是我对程序进行跟踪,发现返回的结果中的Subject 对象,并不是Subject类,而是hibernate的proxy类。而且里面的数据都是旧的数据。这使我白思不得其解,难道是事务没有提交。我一开始怀疑是事务的问题,可能更新的方法和查询的方法处于同一个事务中了。所以查询的时候事务还没提交,但是,我换了许多属性都不行。最后,我决定去看spring的HibernateTemplate的源码。这是我才发现的HibernateTemplate有一个方法
setAlwaysUseNewSession,它可以决定是不是每次execute都使用一个新的session。
这是,我才明白,造成这个问题的原因是2个service方法都用了同一个session。再加上由于hibernate映射的问题,导致更新的时候外键更新了,但关联对象的引用没有更新,导致第2次查询时的关联对象是旧的。
于是我在QuestionDaoHibernateImpl类的构造函数里setAlwaysUseNewSession(true),但是却报错,应该是因为当QuestionDaoHibernateImpl被spring初始化时,它的HibernateTemplate还没初始化。
最后无奈,我只好用spring的initinit-method。
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Generic DAO - can be used when doing standard CRUD -->
<bean id="baseDao" class="com.xxxx.dao.hibernate.BaseDaoHibernateImpl">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="questionDao"
class="com.xxxx.dao.hibernate.QuestionDaoHibernateImpl"
parent="baseDao"
init-method="setUseNewSessionTrue" />
</beans>
public class QuestionDaoHibernateImpl extends BaseDaoHibernateImpl implements QuestionDao{
/**
* HibernateTemplate每次使用新的session.
*/
public void setUseNewSessionTrue(){
getHibernateTemplate().setAlwaysUseNewSession(true);
}
//略
}
这样才把这个问题给解决了。
在业务对象的设计上有一个有趣的问题。在我之前,业务对象已经设计好了,而且hibernate的映射文件也已经完成了。最初的设计者把这些表的所有字段,包括外键全部用property映射了。这样做就是放弃了hibernate的对象关联的功能。当我开始工作后,新的需求要求对一些关联数据进行访问,为了让其它已有的程序能够正常工作,我在hibernate映射文件里添加了many-to-one的映射,并且在类中添加了引用。已有的部分没有任何改动。代码和映射文件如下:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Subject.java<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<




























>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Subject.hbm.xml<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<









































































































































由于对外键的重复映射,给以后的开发带来了一点小小的麻烦。为了保持原来程序的完整,对象之间的关联的建立,还是用原来的baseCode,itemCode这两个String类型的属性。而关联的对象的属性base,item仅仅在查询时有用。所以我把base,和item的映射属性update和insert都设为false,否则hibernate在初始化时会报错。如下:



















在开发的过程中,我使用了Spring的HibernateTemplate。我们的Service对象的所有方法都使用了Spring的申明式事务。


































一切基本上没有什么问题。当我在做一个修改功能时问题出现了。最早完成的查询功能,查询结果用一个列表返回。后来为了使其可以删除和更新记录,所以在显示结果的表格里又新增了一列用来放删除和修改按钮。当记录被更新后,重新返回到这个列表中。所以我的action要完成两个功能,更新记录,再查询。也就是要调用2次service中的方法。一次更新,一次查询。但是问题出现了,当对象的外键属性的内容更新后,重新返回列表时,里面的内容并没有变化,但是重新查询后,结果却是正确的,而其它的字段都正常。
于是我对程序进行跟踪,发现返回的结果中的Subject 对象,并不是Subject类,而是hibernate的proxy类。而且里面的数据都是旧的数据。这使我白思不得其解,难道是事务没有提交。我一开始怀疑是事务的问题,可能更新的方法和查询的方法处于同一个事务中了。所以查询的时候事务还没提交,但是,我换了许多属性都不行。最后,我决定去看spring的HibernateTemplate的源码。这是我才发现的HibernateTemplate有一个方法
setAlwaysUseNewSession,它可以决定是不是每次execute都使用一个新的session。
这是,我才明白,造成这个问题的原因是2个service方法都用了同一个session。再加上由于hibernate映射的问题,导致更新的时候外键更新了,但关联对象的引用没有更新,导致第2次查询时的关联对象是旧的。
于是我在QuestionDaoHibernateImpl类的构造函数里setAlwaysUseNewSession(true),但是却报错,应该是因为当QuestionDaoHibernateImpl被spring初始化时,它的HibernateTemplate还没初始化。
最后无奈,我只好用spring的initinit-method。





























这样才把这个问题给解决了。