Archive for the ‘SSH’ Category
May 13, 2007
key word : hibernate,dao
一。简化hibernate
首先hibernate是一个优秀的ORM工具(此话鉴定为废话:))
其次,按照20/80的规律,有80%的问题不需要那20%学习起来比较麻烦的东西。
很多人在用hibernate的时候一定把关系作为精华来学习,比如一对多,多对一,以及多对多,认为只有熟练运用这个才是对hibernate的真正掌握。
说实话,这个想法从技术的角度讲有点道理,问题是很多人对这些始终是一头雾水,反正不要好看,我现在仍然是一头雾水,今天我终于给自己
解放了出来,我回头去看自己的项目,发现N多的时候100%的不要关系(在 hibernate中)也不会出人命,而且效果很好。
现在,每个mapping文件中没有了set,list,map,我相信一定有N多人说我这个不符合OO什么的,不过大家反过来想,在我的项目组中会有一个明显
的好处就是我将问题简化了,
大家都会做,比如Org与User的关系,1:N的关系,我不用考虑这个,直接手工去维护,dao.save(org);user.setOrgId(“11″);dao.save(user);
有一段时间我依次问各个项目组,你们的hibernate中用到了关联操作了么,回答清一色的“NO”,看来大家都在无意识的回避这个陷阱,呵呵,
很合吾意。
二.简化dao
可能是受到appfuse的影响,一个service就有一个dao,现在从实际的角度来看仍然觉得没有太大的必要,当然,如果你是类似测试驱动的想法
那么可能另当别论。现在我更喜欢一个通用的dao全部搞定,确实舒服。
service中的操作全部调用通用dao(getObjects(),saveObject(),updateObject(),removeObject())

public BeanValue saveItem(RequestMap rMap)
{
BeanValue value = null;

try
{
log.info(“=====begin to save alexItem object“);
value = new BeanValue();
AlexItem alexItem = (AlexItem) BeanUtil.convertObject(AlexItem.class, rMap.getMap());
dao.saveObject(alexItem);
log.info(“=====end to save alexItem object“);
value.addRequestMap(“admin“,alexItem);
value.setForword(“success“);

} catch (BaseException e)
{
value.addErrorMap(“errorName“,e);
value.setForword(“error“);
}
return value;
}
愿意听听各位在项目中的做法。
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
March 30, 2007
key words: spring,dao
转一篇写得很不错的文章,特别简单易懂,赞一个
come from here
摘要
J2EE应用程序中的业务组件通常使用JDBC API访问和更改关系数据库中的持久数据。这经常导致持久性代码与业务逻辑发生混合,这是一种不好的习惯。数据访问对象(DAO)设计模式通过把持久性逻辑分成若干数据访问类来解决这一问题。
本文是一篇关于DAO设计模式的入门文章,突出讲述了它的优点和不足之处。另外,本文还介绍了Spring 2.0 JDBC/DAO框架并示范了它如何妥善地解决传统DAO设计中的缺陷。
传统的DAO设计
数据访问对象(DAO)是一个集成层设计模式,如Core J2EE Design Pattern 图书所归纳。它将持久性存储访问和操作代码封装到一个单独的层中。本文的上下文中所提到的持久存储器是一个RDBMS。
这一模式在业务逻辑层和持久存储层之间引入了一个抽象层,如图1所示。业务对象通过数据访问对象来访问RDBMS(数据源)。抽象层改善了应用程序代码并引入了灵活性。理论上,当数据源改变时,比如更换数据库供应商或是数据库的类型时,仅需改变数据访问对象,从而把对业务对象的影响降到最低。

图1. 应用程序结构,包括DAO之前和之后的部分
讲解了DAO设计模式的基础知识,下面将编写一些代码。下面的例子来自于一个公司域模型。简而言之,这家公司有几位员工工作在不同的部门,如销售部、市场部以及人力资源部。为了简单起见,我们将集中讨论一个称作“雇员”的实体。
针对接口编程
DAO设计模式带来的灵活性首先要归功于一个对象设计的最佳实践:针对接口编程(P2I)。这一原则规定实体必须实现一个供调用程序而不是实体自身使用的接口。因此,可以轻松替换成不同的实现而对客户端代码只产生很小的影响。
我们将据此使用findBySalaryRange()行为定义Employee DAO接口,IEmployeeDAO。业务组件将通过这个接口与DAO交互:
import java.util.Map;
public interface IEmployeeDAO {
//SQL String that will be executed
public String FIND_BY_SAL_RNG = "SELECT EMP_NO, EMP_NAME, "
+ "SALARY FROM EMP WHERE SALARY >= ? AND SALARY <= ?";
//Returns the list of employees who fall into the given salary
//range. The input parameter is the immutable map object
//obtained from the HttpServletRequest. This is an early
//refactoring based on "Introduce Parameter Object"
public List findBySalaryRange(Map salaryMap);
}
提供DAO实现类
接口已经定义,现在必须提供Employee DAO的具体实现,EmployeeDAOImpl:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import com.bea.dev2dev.to.EmployeeTO;
public class EmployeeDAOImpl implements IEmployeeDAO{
public List findBySalaryRange(Map salaryMap)
{
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List empList = new ArrayList();
//Transfer Object for inter-tier data transfer
EmployeeTO tempEmpTO = null;
try{
//DBUtil - helper classes that retrieve connection from pool
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(FIND_BY_SAL_RNG);
pstmt.setDouble(1, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
pstmt.setDouble(2, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
rs = pstmt.executeQuery();
int tmpEmpNo = 0;
String tmpEmpName = "";
double tmpSalary = 0.0D;
while (rs.next()){
tmpEmpNo = rs.getInt("EMP_NO");
tmpEmpName = rs.getString("EMP_NAME");
tmpSalary = rs.getDouble("SALARY");
tempEmpTO = new EmployeeTO(tmpEmpNo,
tmpEmpName,
tmpSalary);
empList.add(tempEmpTO);
}//end while
}//end try
catch (SQLException sqle){
throw new DBException(sqle);
}//end catch
finally{
try{
if (rs != null){
rs.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (pstmt != null){
pstmt.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (conn != null){
conn.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
}//end of finally block
return empList;
}//end method findBySalaryRange
}
上面的清单说明了DAO方法的一些要点:
- 它们封装了所有与JDBC API的交互。如果使用像Kodo或者Hibernate的O/R映射方案,则DAO类可以将这些产品的私有API打包。
- 它们将检索到的数据打包到一个与JDBC API无关的传输对象中,然后将其返回给业务层作进一步处理。
- 它们实质上是无状态的。唯一的目的是访问并更改业务对象的持久数据。
- 在这个过程中,它们像SQLException一样捕获任何底层JDBC API或数据库报告的错误(例如,数据库不可用、错误的SQL句法)。DAO对象再次使用一个与JDBC无关的自定义运行时异常类DBException,通知业务对象这些错误。
- 它们像Connection和PreparedStatement对象那样,将数据库资源释放回池中,并在使用完ResultSet游标之后,将其所占用的内存释放。
因此,DAO层将底层的数据访问API抽象化,为业务层提供了一致的数据访问API。
构建DAO工厂
DAO工厂是典型的工厂设计模式实现,用于为业务对象创建和提供具体的DAO实现。业务对象使用DAO接口,而不用了解实现类的具体情况。DAO工厂带来的依赖反转(dependency inversion)提供了极大的灵活性。只要DAO接口建立的约定未改变,那么很容易改变DAO实现(例如,从straight JDBC实现到基于Kodo的O/R映射),同时又不影响客户的业务对象:
public class DAOFactory {
private static DAOFactory daoFac;
static{
daoFac = new DAOFactory();
}
private DAOFactory(){}
public DAOFactory getInstance(){
return daoFac;
}
public IEmployeeDAO getEmployeeDAO(){
return new EmployeeDAOImpl();
}
}
与业务组件的协作
现在该了解DAO怎样适应更复杂的情形。如前几节所述,DAO与业务层组件协作获取和更改持久业务数据。下面的清单展示了业务服务组件及其与DAO层的交互:
public class EmployeeBusinessServiceImpl implements
IEmployeeBusinessService {
public List getEmployeesWithinSalaryRange(Map salaryMap){
IEmployeeDAO empDAO = DAOFactory.getInstance()
.getEmployeeDAO();
List empList = empDAO.findBySalaryRange(salaryMap);
return empList;
}
}
交互过程十分简洁,完全不依赖于任何持久性接口(包括JDBC)。
问题
DAO设计模式也有缺点:
- 代码重复:从EmployeeDAOImpl清单可以清楚地看到,对于基于JDBC的传统数据库访问,代码重复(如上面的粗体字所示)是一个主要的问题。一遍又一遍地写着同样的代码,明显违背了基本的面向对象设计的代码重用原则。它将对项目成本、时间安排和工作产生明显的副面影响。
- 耦合:DAO代码与JDBC接口和核心collection耦合得非常紧密。从每个DAO类的导入声明的数量可以明显地看出这种耦合。
- 资源耗损:依据EmployeeDAOImpl类的设计,所有DAO方法必须释放对所获得的连接、声明、结果集等数据库资源的控制。这是危险的主张,因为一个编程新手可能很容易漏掉那些约束。结果造成资源耗尽,导致系统停机。
- 错误处理:JDBC 驱动程序通过抛出SQLException来报告所有的错误情况。SQLException是检查到的异常,所以开发人员被迫去处理它,即使不可能从这类导致代码混乱的大多数异常中恢复过来。而且,从SQLException对象获得的错误代码和消息特定于数据库厂商,所以不可能写出可移植的DAO错误发送代码。
- 脆弱的代码:在基于JDBC的DAO中,两个常用的任务是设置声明对象的绑定变量和使用结果集检索数据。如果SQL where子句中的列数目或者位置更改了,就不得不对代码执行更改、测试、重新部署这个严格的循环过程。
让我们看看如何能够减少这些问题并保留DAO的大多数优点。
进入Spring DAO
先识别代码中发生变化的部分,然后将这一部分代码分离出来或者封装起来,就能解决以上所列出的问题。Spring的设计者们已经完全做到了这一点,他们发布了一个超级简洁、健壮的、高度可伸缩的JDBC框架。固定部分(像检索连接、准备声明对象、执行查询和释放数据库资源)已经被一次性地写好,所以该框架的一部分内容有助于消除在传统的基于JDBC的DAO中出现的缺点。
图2显示的是Spring JDBC框架的主要组成部分。业务服务对象通过适当的接口继续使用DAO实现类。JdbcDaoSupport是JDBC数据访问对象的超类。它与特定的数据源相关联。Spring Inversion of Control (IOC)容器或BeanFactory负责获得相应数据源的配置详细信息,并将其与JdbcDaoSupport相关联。这个类最重要的功能就是使子类可以使用JdbcTemplate对象。

图2. Spring JDBC框架的主要组件
JdbcTemplate是Spring JDBC框架中最重要的类。引用文献中的话:“它简化了JDBC的使用,有助于避免常见的错误。它执行核心JDBC工作流,保留应用代码以提供SQL和提取结果。”这个类通过执行下面的样板任务来帮助分离JDBC DAO代码的静态部分:
- 从数据源检索连接。
- 准备合适的声明对象。
- 执行SQL CRUD操作。
- 遍历结果集,然后将结果填入标准的collection对象。
- 处理SQLException异常并将其转换成更加特定于错误的异常层次结构。
利用Spring DAO重新编写
既然已基本理解了Spring JDBC框架,现在要重新编写已有的代码。下面将逐步讲述如何解决前几节中提到的问题。
第一步:修改DAO实现类- 现在从JdbcDaoSupport扩展出EmployeeDAOImpl以获得JdbcTemplate。
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.core.JdbcTemplate;
public class EmployeeDAOImpl extends JdbcDaoSupport
implements IEmployeeDAO{
public List findBySalaryRange(Map salaryMap){
Double dblParams [] = {Double.valueOf((String)
salaryMap.get("MIN_SALARY"))
,Double.valueOf((String)
salaryMap.get("MAX_SALARY"))
};
//The getJdbcTemplate method of JdbcDaoSupport returns an
//instance of JdbcTemplate initialized with a datasource by the
//Spring Bean Factory
JdbcTemplate daoTmplt = this.getJdbcTemplate();
return daoTmplt.queryForList(FIND_BY_SAL_RNG,dblParams);
}
}
在上面的清单中,传入参数映射中的值存储在双字节数组中,顺序与SQL字符串中的位置参数相同。queryForList()方法以包含Map(用列名作为键,一项对应一列)的List(一项对应一行)的方式返回查询结果。稍后我会说明如何返回传输对象列表。
从简化的代码可以明显看出,JdbcTemplate鼓励重用,这大大削减了DAO实现中的代码。JDBC和collection包之间的紧密耦合已经消除。由于JdbcTemplate方法可确保在使用数据库资源后将其按正确的次序释放,所以JDBC的资源耗损不再是一个问题。
另外,使用Spring DAO时,不必处理异常。JdbcTemplate类会处理SQLException,并根据SQL错误代码或错误状态将其转换成特定于Spring异常的层次结构。例如,试图向主键列插入重复值时,将引发DataIntegrityViolationException。然而,如果无法从这一错误中恢复,就无需处理该异常。因为Spring DAO的根异常类DataAccessException是运行时异常类,所以可以这样做。值得注意的是Spring DAO异常独立于数据访问实现。如果实现是由O/R映射解决方案提供,就会抛出同样的异常。
第二步:修改业务服务- 现在业务服务实现了一个新方法setDao(),Spring容器使用该方法传递DAO实现类的引用。该过程称为“设置方法注入(setter injection)”,通过第三步中的配置文件告知Spring容器该过程。注意,不再需要使用DAOFactory,因为Spring BeanFactory提供了这项功能:
public class EmployeeBusinessServiceImpl
implements IEmployeeBusinessService {
IEmployeeDAO empDAO;
public List getEmployeesWithinSalaryRange(Map salaryMap){
List empList = empDAO.findBySalaryRange(salaryMap);
return empList;
}
public void setDao(IEmployeeDAO empDAO){
this.empDAO = empDAO;
}
}
请注意P2I的灵活性;即使极大地改动DAO实现,业务服务实现也只需少量更改。这是由于业务服务现在由Spring容器进行管理。
第三步:配置Bean Factory- Spring bean factory需要一个配置文件进行初始化并启动Spring框架。这个配置文件包含所有业务服务和带Spring bean容器的DAO实现类。除此之外,它还包含用于初始化数据源和JdbcDaoSupport的信息:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Configure Datasource -->
<bean id="FIREBIRD_DATASOURCE"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiEnvironment">
<props>
<prop key="java.naming.factory.initial">
weblogic.jndi.WLInitialContextFactory
</prop>
<prop key="java.naming.provider.url">
t3://localhost:7001
</prop>
</props>
</property>
<property name="jndiName">
<value>
jdbc/DBPool
</value>
</property>
</bean>
<!-- Configure DAO -->
<bean id="EMP_DAO" class="com.bea.dev2dev.dao.EmployeeDAOImpl">
<property name="dataSource">
<ref bean="FIREBIRD_DATASOURCE"></ref>
</property>
</bean>
<!-- Configure Business Service -->
<bean id="EMP_BUSINESS"
class="com.bea.dev2dev.sampleapp.business.EmployeeBusinessServiceImpl">
<property name="dao">
<ref bean="EMP_DAO"></ref>
</property>
</bean>
</beans>
这个Spring bean容器通过调用JdbcDaoSupport提供的setDataSource()方法,设置包含DAO实现的数据源对象。
第四步:测试- 最后是编写JUnit测试类。依照Spring的方式,需要在容器外部进行测试。然而,从第三步中的配置文件可以清楚地看到,我们一直在使用WebLogic Server连接池。
package com.bea.dev2dev.business;
import java.util.*;
import junit.framework.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class EmployeeBusinessServiceImplTest extends TestCase {
private IEmployeeBusinessService empBusiness;
private Map salaryMap;
List expResult;
protected void setUp() throws Exception {
initSpringFramework();
initSalaryMap();
initExpectedResult();
}
private void initExpectedResult() {
expResult = new ArrayList();
Map tempMap = new HashMap();
tempMap.put("EMP_NO",new Integer(1));
tempMap.put("EMP_NAME","John");
tempMap.put("SALARY",new Double(46.11));
expResult.add(tempMap);
}
private void initSalaryMap() {
salaryMap = new HashMap();
salaryMap.put("MIN_SALARY","1");
salaryMap.put("MAX_SALARY","50");
}
private void initSpringFramework() {
ApplicationContext ac = new FileSystemXmlApplicationContext
("C:/SpringConfig/Spring-Config.xml");
empBusiness =
(IEmployeeBusinessService)ac.getBean("EMP_BUSINESS");
}
protected void tearDown() throws Exception {
}
/**
* Test of getEmployeesWithinSalaryRange method,
* of class
* com.bea.dev2dev.business.EmployeeBusinessServiceImpl.
*/
public void testGetEmployeesWithinSalaryRange() {
List result = empBusiness.getEmployeesWithinSalaryRange
(salaryMap);
assertEquals(expResult, result);
}
}
使用绑定变量
到目前为止,我们搜索了工资介于最低值和最高值之间的雇员。假设在某种情形下,业务用户想要颠倒这一范围。DAO代码很脆弱,将不得不通过更改来满足要求的变化。这个问题在于使用了静态的位置绑定变量(用“?”表示)。Spring DAO通过支持命名的绑定变量来挽救这个情况。修改的IEmployeeDAO清单引入了命名的绑定变量(用“:<some name>”表示)。注意查询中的变化,如下所示:
import java.util.Map;
public interface IEmployeeDAO {
//SQL String that will be executed
public String FIND_BY_SAL_RNG = "SELECT EMP_NO, EMP_NAME, "
+ "SALARY FROM EMP WHERE SALARY >= :max AND SALARY <= :min";
//Returns the list of employees falling into the given salary range
//The input parameter is the immutable map object obtained from
//the HttpServletRequest. This is an early refactoring based on
//- "Introduce Parameter Object"
public List findBySalaryRange(Map salaryMap);
}
多数JDBC驱动程序仅支持位置绑定变量。所以,Spring DAO在运行时将这个查询转换成位置绑定、基于变量的查询,并且设置正确的绑定变量。现在,为了完成这些任务,需要使用NamedParameterJdbcDaoSupport类和NamedParameterJdbcTemplate类,以代替JdbcDaoSupport和JdbcTemplate。下面就是修改后的DAO实现类:
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class EmployeeDAOImpl extends NamedParameterJdbcDaoSupport
implements IEmployeeDAO{
public List findBySalaryRange(Map salaryMap){
NamedParameterJdbcTemplate tmplt =
this.getNamedParameterJdbcTemplate();
return tmplt.queryForList(IEmployeeDAO.FIND_BY_SAL_RNG
,salaryMap);
}
}
NamedParameterJdbcDaoSupport的getNamedParameterJdbcTemplate()方法返回一个 NamedParameterJdbcTemplate实例,该实例由数据源句柄进行了预初始化。Spring Beanfactory执行初始化任务,从配置文件获得所有的详细信息。在执行时,一旦将命名的参数替换成位置占位符, NamedParameterJdbcTemplate就将操作委托给JdbcTemplate。可见,使用命名的参数使得DAO方法不受底层SQL声明任何更改的影响。
最后,如果数据库不支持自动类型转换,需要如下所示,对JUnit测试类中的initSalaryMap()方法稍做修改。
private void initSalaryMap() {
salaryMap = new HashMap();
salaryMap.put("MIN_SALARY",new Double(1));
salaryMap.put("MAX_SALARY",new Double(50));
}
Spring DAO回调函数
至此,已经说明为了解决传统DAO设计中存在的问题,如何封装和概括JdbcTemplate类中JDBC代码的静态部分。现在了解一下有关变量的问题,如设置绑定变量、结果集遍历等。虽然Spring DAO已经拥有这些问题的一般化解决方案,但在某些基于SQL的情况下,可能仍需要设置绑定变量。
在尝试向Spring DAO转换的过程中,介绍了由于业务服务及其客户机之间的约定遭到破坏而导致的隐蔽运行时错误。这个错误的来源可以追溯到原始的DAO。 dbcTemplate.queryForList()方法不再返回EmployeeTO实例列表。而是返回一个map表(每个map是结果集的一行)。
如您目前所知,JdbcTemplate基于模板方法设计模式,该模式利用JDBC API定义SQL执行工作流。必须改变这个工作流以修复被破坏的约定。第一个选择是在子类中更改或扩展工作流。您可以遍历 JdbcTemplate.queryForList()返回的列表,用EmployeeTO实例替换map对象。然而,这会导致我们一直竭力避免的静态代码与动态代码的混合。第二个选择是将代码插入JdbcTemplate提供的各种工作流修改钩子(hook)。明智的做法是在一个不同的类中封装传输对象填充代码,然后通过钩子链接它。填充逻辑的任何修改将不会改变DAO。
编写一个类,使其实现在Spring框架特定的接口中定义的方法,就可以实现第二个选择。这些方法称为回调函数,通过JdbcTemplate向框架注册。当发生相应的事件(例如,遍历结果集并填充独立于框架的传输对象)时,框架将调用这些方法。
第一步:传输对象
下面是您可能感兴趣的传输对象。注意,以下所示的传输对象是固定的:
package com.bea.dev2dev.to;
public final class EmployeeTO implements Serializable{
private int empNo;
private String empName;
private double salary;
/** Creates a new instance of EmployeeTO */
public EmployeeTO(int empNo,String empName,double salary) {
this.empNo = empNo;
this.empName = empName;
this.salary = salary;
}
public String getEmpName() {
return this.empName;
}
public int getEmpNo() {
return this.empNo;
}
public double getSalary() {
return this.salary;
}
public boolean equals(EmployeeTO empTO){
return empTO.empNo == this.empNo;
}
}
第二步:实现回调接口
实现RowMapper接口,填充来自结果集的传输对象。下面是一个例子:
package com.bea.dev2dev.dao.mapper;
import com.bea.dev2dev.to.EmployeeTO;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class EmployeeTOMapper implements RowMapper{
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException{
int empNo = rs.getInt(1);
String empName = rs.getString(2);
double salary = rs.getDouble(3);
EmployeeTO empTo = new EmployeeTO(empNo,empName,salary);
return empTo;
}
}
注意实现类不应该对提供的ResultSet对象调用next()方法。这由框架负责,该类只要从结果集的当前行提取值就行。回调实现抛出的任何SQLException也由Spring框架处理。
第三步:插入回调接口
执行SQL查询时,JdbcTemplate利用默认的RowMapper实现产生map列表。现在需要注册自定义回调实现来修改 JdbcTemplate的这一行为。注意现在用的是NamedParameterJdbcTemplate的query()方法,而不是 queryForList()方法:
public class EmployeeDAOImpl extends NamedParameterJdbcDaoSupport
implements IEmployeeDAO{
public List findBySalaryRange(Map salaryMap){
NamedParameterJdbcTemplate daoTmplt =
getNamedParameterJdbcTemplate();
return daoTmplt.query(IEmployeeDAO.FIND_BY_SAL_RNG, salaryMap,
new EmployeeTOMapper());
}
}
Spring DAO框架对执行查询后返回的结果进行遍历。它在遍历的每一步调用EmployeeTOMapper类实现的mapRow()方法,使用EmployeeTO传输对象填充最终结果的每一行。
第四步:修改后的JUnit类
现在要根据返回的传输对象测试这些结果。为此要对测试方法进行修改。
public class EmployeeBusinessServiceImplTest extends TestCase {
private IEmployeeBusinessService empBusiness;
private Map salaryMap;
List expResult;
// all methods not shown in the listing remain the
// same as in the previous example
private void initExpectedResult() {
expResult = new ArrayList();
EmployeeTO to = new EmployeeTO(2,"John",46.11);
expResult.add(to);
}
/**
* Test of getEmployeesWithinSalaryRange method, of
* class com.bea.dev2dev.business.
* EmployeeBusinessServiceImpl
*/
public void testGetEmployeesWithinSalaryRange() {
List result = empBusiness.
getEmployeesWithinSalaryRange(salaryMap);
assertEquals(expResult, result);
}
public void assertEquals(List expResult, List result){
EmployeeTO expTO = (EmployeeTO) expResult.get(0);
EmployeeTO actualTO = (EmployeeTO) result.get(0);
if(!expTO.equals(actualTO)){
throw new RuntimeException("** Test Failed **");
}
}
}
优势
Spring JDBC框架的优点很清楚。我们获益很多,并将DAO方法简化到只有几行代码。代码不再脆弱,这要感谢该框架对命名的参数绑定变量的“开箱即用”支持,以及在映射程序中将传输对象填充逻辑分离。
Spring JDBC的优点应该促使您向这一框架移植现有的代码。希望本文在这一方面能有所帮助。它会帮助您获得一些重构工具和知识。例如,如果您没有采用P2I Extract Interface,那么可以使用重构,从现有的DAO实现类创建接口。除此之外,查看本文的参考资料可以得到更多指导。
下载
可以下载本文用到的源代码。
结束语
在此篇文章中,我讲述了数据访问对象(DAO)设计模式的基础知识,并从正反两方面进行了讨论。引入Spring DAO或JDBC框架来克服传统DAO的不足。然后,根据Spring框架提供的“开箱即用”命名参数支持对脆弱的DAO代码进行了改进。最后,回调功能展示了如何在指定点修改框架行为。
参考资料

| 作者简介 |
| |
Dhrubojyoti Kayal 是Capgemini Consulting的高级顾问。在利用企业Java技术开发和设计应用程序和产品方面,拥有5年以上的经验。 |
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
January 25, 2007
key words : hibernate spring dao
come from : http://lpacec.javaeye.com/blog/46220
1. package infoweb.dao;
2.
3. import java.util.List;
4. import java.util.Iterator;
5.
6. import infoweb.pojo.Info;
7.
8. import net.sf.hibernate.HibernateException;
9. import net.sf.hibernate.Query;
10. import net.sf.hibernate.Session;
11.
12. import org.springframework.orm.hibernate.HibernateCallback;
13. import org.springframework.orm.hibernate.support.HibernateDaoSupport;
14.

15. /** *//**
16. *

Title:

17. *

Description:

18. *

Copyright: Copyright (c) 2004

19. *

Company:

20. * @author 段洪杰
21. * @version 1.0
22. */
23.

24. public class InfoDAOImpl extends HibernateDaoSupport implements IInfoDAO
{

25. /** *//**
26. * 构造函数
27. */

28. public InfoDAOImpl()
{
29. super();
30. }
31.

32. /** *//**
33. * 增加记录
34. * @param info Info
35. */

36. public void setInfo(Info info) throws Exception
{
37. getHibernateTemplate().save(info);
38. }
39.

40. /** *//**
41. * 通过ID取得记录
42. * @param id String
43. * @return Info
44. */

45. public Info getInfoById(String id) throws Exception
{
46. Info info = (Info) getHibernateTemplate().load(Info.class, id);
47. return info;
48. }
49.

50. /** *//**
51. * 修改记录
52. * @param Info info
53. */

54. public void modifyInfo(Info info) throws Exception
{
55. getHibernateTemplate().update(info);
56. }
57.

58. /** *//**
59. * 删除记录
60. * @param Info info
61. */

62. public void removeInfo(Info info) throws Exception
{
63. getHibernateTemplate().delete(info);
64. }
65.
66. ////////////////////////////////////////////////////////
67. ///// ///
68. /////以下部份不带审核功能 ///
69. ///// ///
70. ////////////////////////////////////////////////////////
71.

72. /** *//**
73. * 取记录总数
74. * @return int
75. */

76. public int getInfosCount() throws Exception
{
77. int count = 0;
78. String queryString = “select count(*) from Info“;
79. count = ((Integer) getHibernateTemplate().iterate(queryString).next()).
80. intValue();
81. return count;
82. }
83.

84. /** *//**
85. * 取所有记录集合
86. * @return Iterator
87. */

88. public Iterator getAllInfos() throws Exception
{
89. Iterator iterator = null;
90. String queryString = “ select info from Info as info order by info.id desc“;
91. List list = getHibernateTemplate().find(queryString);
92. iterator = list.iterator();
93. return iterator;
94. }
95.

96. /** *//**
97. * 取记录集合
98. * @return Iterator
99. * @param int position, int length
100. */

101. public Iterator getInfos(int position, int length) throws Exception
{
102. Iterator iterator = null;
103. String queryString = “ select info from Info as info order by info.id desc“;
104. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
105. //设置游标的起始点
106. query.setFirstResult(position);
107. //设置游标的长度
108. query.setMaxResults(length);
109. //记录生成
110. List list = query.list();
111. //把查询到的结果放入迭代器
112. iterator = list.iterator();
113. return iterator;
114. }
115.

116. /** *//**
117. * 取第一条记录
118. * @throws Exception
119. * @return Station
120. */

121. public Info getFirstInfo() throws Exception
{
122. Iterator iterator = null;
123. Info info = null;
124. String queryString = “select info from Info as info order by info.id desc“;
125. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
126. //记录生成
127. List list = query.list();
128. //把查询到的结果放入迭代器
129. iterator = list.iterator();

130. if (iterator.hasNext())
{
131. info = (Info) iterator.next();
132. }
133. return info;
134. }
135.

136. /** *//**
137. * 取最后一条记录
138. * @throws Exception
139. * @return Station
140. */

141. public Info getLastInfo() throws Exception
{
142. Iterator iterator = null;
143. Info info = null;
144. String queryString = “select info from Info as info order by info.id asc“;
145. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
146. //记录生成
147. List list = query.list();
148. //把查询到的结果放入迭代器
149. iterator = list.iterator();

150. if (iterator.hasNext())
{
151. info = (Info) iterator.next();
152. }
153. return info;
154.
155. }
156.
157. ////////////////////////////////////////////////////////
158. ///// ///
159. ///// 以下部份表中要有特定字段才能正确运行 个人和企业 ///
160. ///// ///
161. ////////////////////////////////////////////////////////
162.

163. /** *//**
164. * 取符合条件记录总数, [表中要有 isperson 字段]
165. * @return int
166. * @param int isPerson
167. */
168.

169. public int getInfosCountByIsperson(int isPerson) throws Exception
{
170. int count = 0;
171. String queryString =
172. “select count(*) from Info as info where info.isperson =“ + isPerson;
173. count = ((Integer) getHibernateTemplate().iterate(queryString).next()).
174. intValue();
175. return count;
176. }
177.

178. /** *//**
179. * 取所有符合条件记录集合, 模糊查询条件.[表中要有 isperson 字段]
180. * @return Iterator
181. * @param int isPerson
182. */
183.

184. public Iterator getAllInfosByIsperson(int isPerson) throws Exception
{
185. Iterator iterator = null;
186. String queryString = “ select info from Info as info where info.isperson =“ +
187. isPerson + “ order by info.id desc“;
188. List list = getHibernateTemplate().find(queryString);
189. //把查询到的结果放入迭代器
190. iterator = list.iterator();
191. return iterator;
192. }
193.

194. /** *//**
195. * 取符合条件记录集合, 模糊查询条件.[表中要有 isperson 字段]
196. * @return Iterator
197. * @param int isPerson,int position, int length
198. */
199.
200. public Iterator getInfosByIsperson(int isPerson, int position, int length) throws

201. Exception
{
202. Iterator iterator = null;
203. String queryString = “ select info from Info as info where info.isperson =“ +
204. isPerson + “ order by info.id desc“;
205. //创建查询
206. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
207. //设置游标的起始点
208. query.setFirstResult(position);
209. //设置游标的长度
210. query.setMaxResults(length);
211. //记录生成
212. List list = query.list();
213. //把查询到的结果放入迭代器
214. iterator = list.iterator();
215. return iterator;
216. }
217.
218. ////////////////////////////////////////////////////////
219. ///// ///
220. ///// 以下部份表中要有特定字段才能正确运行 查询部份 ///
221. ///// ///
222. ///////////////////////////////////////////////////////

223. /** *//**
224. * 取符合条件记录总数, 模糊查询条件.[表中要有 title 字段]
225. * @return int
226. * @param String text
227. */

228. public int getInfosCount(String text) throws Exception
{
229. int count = 0;
230. count = ((Integer) getHibernateTemplate().iterate(
231. “select count(*) from Info as info where info.title like ’%“ + text +
232. “%’“).next()).intValue();
233. return count;
234. }
235.

236. /** *//**
237. * 取所有符合条件记录集合, 模糊查询条件.[表中要有 title 字段]
238. * @return Iterator
239. * @param String text
240. */
241.

242. public Iterator getAllInfos(String text) throws Exception
{
243. Iterator iterator = null;
244. String queryString =
245. “ select info from Info as info where info.title like ’%“ + text +
246. “%’ order by info.id desc“;
247. //创建查询
248. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
249. //记录生成
250. List list = query.list();
251. //把查询到的结果放入迭代器
252. iterator = list.iterator();
253. return iterator;
254. }
255.

256. /** *//**
257. * 取符合条件记录集合, 模糊查询条件.[表中要有 title 字段]
258. * @return Iterator
259. * @param String text,int position, int length
260. */
261. public Iterator getInfos(String text, int position, int length) throws

262. Exception
{
263. Iterator iterator = null;
264. String queryString =
265. “ select info from Info as info where info.title like ’%“ + text +
266. “%’ order by info.id desc“;
267.
268. //创建查询
269. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
270. //设置游标的起始点
271. query.setFirstResult(position);
272. //设置游标的长度
273. query.setMaxResults(length);
274. //记录生成
275. List list = query.list();
276. //把查询到的结果放入迭代器
277. iterator = list.iterator();
278. return iterator;
279. }
280.
281. ////////////////////////////////////////////////////////
282. ///// ///
283. ///// 以下部份表中要有特定字段才能正确运行 注册相关 ///
284. ///// ///
285. ////////////////////////////////////////////////////////
286.

287. /** *//**
288. * 取符合条件记录总数.[ 表中要有 registername 字段]
289. * @return int
290. * @param String text
291. */

292. public int getInfosCountByRegisterName(String registerName) throws Exception
{
293. int count = 0;
294. count = ((Integer) getHibernateTemplate().iterate(
295. “select count(*) from Info as info where info.registername = ’“ +
296. registerName + “‘“).next()).intValue();
297. return count;
298. }
299.

300. /** *//**
301. * 通过注册名取得一条记录,如有多条,只取第一条.[表中要有 registername字段]
302. * @param registername String
303. * @return Info
304. */

305. public Info getInfoByRegisterName(String registerName) throws Exception
{
306. Iterator iterator = null;
307. Info info = null;
308. String queryString =
309. “ select info from Info as info where info.registername=’“ +
310. registerName + “‘ order by info.id desc“;
311. //创建查询
312. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
313. //记录生成
314. List list = query.list();
315. //把查询到的结果放入迭代器
316. iterator = list.iterator();

317. if (iterator.hasNext())
{
318. info = (Info) iterator.next();
319. }
320. return info;
321. }
322.

323. /** *//**
324. * 通过注册名取得所有记录集合.[表中要有 registername字段]
325. * @param registername String
326. * @return Iterator
327. */
328. public Iterator getAllInfosByRegisterName(String registerName) throws

329. Exception
{
330. Iterator iterator = null;
331. String queryString =
332. “ select info from Info as info where info.registername=’“ +
333. registerName + “‘ order by info.id desc“;
334. //创建查询
335. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
336. //记录生成
337. List list = query.list();
338. //把查询到的结果放入迭代器
339. iterator = list.iterator();
340. return iterator;
341. }
342.

343. /** *//**
344. * 通过注册名取得记录列表.[表中要有 registername字段]
345. * @param registername String
346. * @return Iterator
347. */
348. public Iterator getInfosByRegisterName(String registerName, int position,

349. int length) throws Exception
{
350. Iterator iterator = null;
351. String queryString =
352. “ select info from Info as info where info.registername=’“ +
353. registerName + “‘ order by info.id desc“;
354. //创建查询
355. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
356. //设置游标的起始点
357. query.setFirstResult(position);
358. //设置游标的长度
359. query.setMaxResults(length);
360. //记录生成
361. List list = query.list();
362. //把查询到的结果放入迭代器
363. iterator = list.iterator();
364. return iterator;
365. }
366.
367. ////////////////////////////////////////////////////////
368. ///// ///
369. ///// 以下部份表中要有特定字段才能正确运行 树型版块 ///
370. ///// ///
371. ////////////////////////////////////////////////////////
372.

373. /** *//**
374. * 取记录总数.[ 表中要有 board_id 字段]
375. * @return int
376. * @param String boardId
377. */

378. public int getInfosCountByBoard(String boardId) throws Exception
{
379. int count = 0;
380.
381. count = ((Integer) getHibernateTemplate().iterate(
382. “select count(*) from Info as info where info.boardId = ’“ + boardId +
383. “‘“).next()).intValue();
384.
385. return count;
386. }
387.

388. /** *//**
389. * 通过版块名取得所有记录集合.[表中要有 board_id字段]
390. * @param BoardId String
391. * @return Iterator
392. */

393. public Iterator getAllInfosByBoard(String boardId) throws Exception
{
394. Iterator iterator = null;
395. String queryString = “ select info from Info as info where info.boardId=’“ +
396. boardId + “‘ order by info.id desc“;
397. //创建查询
398. Query query = getHibernateTemplate().createQuery(getSession(), queryString);
399. //记录生成
400. List list = query.list();
401. //把查询到的结果放入迭代器
402. iterator = list.iterator();
403. return iterator;
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
December 7, 2006
key words: MiddleGen,hibernate,many to many,多对多 如果你有如下表结构 user(user_id,user_name) role(role_id,role_name) user_role(id,user_id,role_id) 那么默认MiddleGen生成的是两个一对多,但我们更多的情况是用many to many 需要修改middlegen的build.xml文件
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
December 1, 2006
越来越发现其实掌握 hibernate并不容易,Spring用起来其实简单多了,但是在用hibernate的时候真的是需要一定的时间积累,对一个项目组来说如果采用hibernate最好有一个对hibernate比较清楚的人否则碰到问题就会成为项目的风险。
我想告诉各位的是,掌握hibernate可能比你预期的难多了,当你轻松的告诉我,hibernate很简单的时候该是你自己多反省了. (只有一种情况例外,你是一个牛人)
好了,一个引子废话那么多,其实今天只是想先说一说hibernate里的Fetch的作用.
大家都知道,在hibernate里为了性能考虑,引进了lazy的概念,这里我们以Parent和Child为模型来说明,
public class Parent implements Serializable {
/** identifier field */
private Long id;
/** persistent field */
private List childs;
//skip all getter/setter method
}
public class Child implements Serializable {
/** identifier field */
private Long id;
/** persistent field */
private net.foxlog.model.Parent parent;
//skip all getter/setter method
}
在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy=”false”的话才同时取出关联的所有childs内容.
问题是我既想要hibernate默认的性能又想要临时的灵活性该怎么办? 这就是fetch的功能。我们可以把fetch与lazy=”true”的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。
总值,fetch就是在代码这一层给你一个主动抓取得机会.
Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Query q = session.createQuery(
“from Parent as parent “+
“ left outer join fetch parent.childs “ +
“ where parent.id = :id“
);
q.setParameter(“id“,new Long(15));
return (Parent)q.uniqueResult();
}
});
Assert.assertTrue(parent.getChilds().size() > 0);
你可以在lazy=”true”的情况下把fetch去掉,就会报异常. 当然,如果lazy=”false”就不需要fetch了
有一个问题,使用Fetch会有重复记录的现象发生,我们可以理解为Fetch实际上不是为Parent服务的,而是为Child服务的.所以直接取Parent会有不匹配的问题.
参考一下下面的这篇文章
Hibernate集合初始化
======================================================================
update:以上有些结论错误,实际上在hibernate3.2.1版本下测试,可以不出现重复记录,
public void testNPlusOne() throws Exception{
List list = (List)hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Query q = session.createQuery(
“select distinct p from net.foxlog.model.Parent p inner join fetch p.childs“
);
return q.list();
}
});
//((Parent)(list.get(0))).getChilds();
System.out.println(“list size = “ + list.size());
for(int i=0;i<list.size();i++){
Parent p = (Parent)list.get(i);
System.out.println(“===parent = “ + p);
System.out.println(“===parent’s child’s length = “ + p.getChilds().size());
}
}
打印结果如下:
Hibernate: select distinct parent0_.id as id2_0_, childs1_.id as id0_1_, childs1_.parent_id as parent2_0_1_, childs1_.parent_id as parent2_0__, childs1_.id as id0__ from parent parent0_ inner join child childs1_ on parent0_.id=childs1_.parent_id
list size = 3
===parent = net.foxlog.model.Parent@1401d28[id=14]
===parent‘s child‘s length = 1
===parent = net.foxlog.model.Parent@14e0e90[id=15]
===parent‘s child‘s length = 2
===parent = net.foxlog.model.Parent@62610b[id=17]
===parent‘s child‘s length = 3
另外,如果用open session in view模式的话一般不用fetch,但首先推荐fetch,如果非用的话因为有N+1的现象,所以可以结合batch模式来改善下性能.
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
November 9, 2006
key words:hibernate,复合主键,composite-id
基于业务需求,您会需要使用两个字段来作复合主键,例如在User数据表中,您也许会使用“name”与“phone”两个字段来定义复合主键。
假设您这么建立User表格:
CREATE TABLE user (
name VARCHAR(100) NOT NULL,
phone VARCHAR(50) NOT NULL,
age INT,
PRIMARY KEY(name, phone)
);
在表格中,“name”与“age”被定义为复合主键,在映像时,您可以让User类别直接带有“name”与“age”这两个属性,而Hibernate要求复合主键类别要实作Serializable接口,并定义equals()与hashCode()方法:
User.java
package onlyfun.caterpillar;
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
// 复合主键类的对应类别必须实作Serializable接口
public class User implements Serializable {
private String name;
private String phone;
private Integer age;
public User() {
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
// 必须重新定义equals()与hashCode()
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(!(obj instanceof User)) {
return false;
}
User user = (User) obj;
return new EqualsBuilder()
.append(this.name, user.getName())
.append(this.phone, user.getPhone())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(this.name)
.append(this.phone)
.toHashCode();
}
}
equals()与hashCode()方法被用作两笔不同数据的识别依据;接着您可以使用<composite-id>在映射文件中定义复合主键与对象的属性对应:
User.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=”onlyfun.caterpillar.User” table=”user”>
<composite-id>
<key-property name=”name”
column=”name”
type=”java.lang.String”/>
<key-property name=”phone”
column=”phone”
type=”java.lang.String”/>
</composite-id>
<property name=”age” column=”age” type=”java.lang.Integer”/>
</class>
</hibernate-mapping>
在储存数据方面,复合主键的储存没什么区别,现在的问题在于如何依据复合主键来查询数据,例如使用load()方法,您可以创建一个User实例,并设定复合主键对应的属性,接着再透过load()查询对应的数据,例如:
User user = new User();
user.setName(“bush“);
user.setPhone(“0970123456“);
Session session = sessionFactory.openSession();
// 以实例设定复合主键并加载对应的数据
user = (User) session.load(User.class, user);
System.out.println(user.getAge() + “\t“ +
user.getName() + “\t“ +
user.getPhone());
session.close();
可以将主键的信息独立为一个类别,例如:
UserPK.java
package onlyfun.caterpillar;
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
public class UserPK implements Serializable {
private String name;
private String phone;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(!(obj instanceof User)) {
return false;
}
UserPK pk = (UserPK) obj;
return new EqualsBuilder()
.append(this.name, pk.getName())
.append(this.phone, pk.getPhone())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(this.name)
.append(this.phone)
.toHashCode();
}
}
现在User类别的主键信息被分离出来了,例如:
User.java
package onlyfun.caterpillar;
import java.io.Serializable;
public class User implements Serializable {
private UserPK userPK; // 主键
private Integer age;
public User() {
}
public UserPK getUserPK() {
return userPK;
}
public void setUserPK(UserPK userPK) {
this.userPK = userPK;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
在映像文件方面,需要指定主键类的信息,例如:
User.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=”onlyfun.caterpillar.User” table=”user”>
<composite-id name=”userPK”
class=”onlyfun.caterpillar.UserPK”
unsaved-value=”any”>
<key-property name=”name”
column=”name”
type=”java.lang.String”/>
<key-property name=”phone”
column=”phone”
type=”java.lang.String”/>
</composite-id>
<property name=”age” column=”age” type=”java.lang.Integer”/>
</class>
</hibernate-mapping>
在查询数据时,必须指定主键信息,例如:
UserPK pk = new UserPK();
pk.setName(“bush“);
pk.setPhone(“0970123456“);
Session session = sessionFactory.openSession();
// 以主键类实例设定复合主键并加载对应的数据
User user = (User) session.load(User.class, pk);
System.out.println(user.getAge() + “\t“ +
user.getUserPK().getName() + “\t“ +
user.getUserPK().getPhone());
session.close();
=================================================
再参考robbin的一篇文章
一个简单的复合主键的做关联类的例子
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
November 9, 2006
key words:mappingResources,通配符,mappingDirectoryLocations
平时写mapping的文件需要一个一个的放到配置文件里,比如
1. <property name=”mappingResources”>
2. <list>
3. <value>net/foxlog/model/Classes.hbm.xml</value>
4. <value>net/foxlog/model/Parent.hbm.xml</value>
5. <value>net/foxlog/model/Child.hbm.xml</value>
6. <value>net/foxlog/model/User.hbm.xml</value>
7. </list>
8. </property>
可以用更一步到位的方法:
1. <property name=”mappingDirectoryLocations”>
2. <list>
3. <value>classpath*:/org/springside/bookstore/commons/model/hbm</value>
4. </list>
5. </property>
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
November 7, 2006
key words:hibernate,load,session.get,session.load()
public class Parent implements Serializable {
/** identifier field */
private Long id;
/** persistent field */
private List childs;
/** full constructor */
public Parent(Long id, List childs) {
this.id = id;
this.childs = childs;
}
/** default constructor */
public Parent() {
}
/**
* @hibernate.id
* generator-class=”assigned”
* type=”java.lang.Long”
* column=”id”
*
*/
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @hibernate.set
* lazy=”true”
* inverse=”true”
* cascade=”none”
* @hibernate.collection-key
* column=”parent_id”
* @hibernate.collection-one-to-many
* class=”net.foxlog.model.Child”
*
*/
public List getChilds() {
return this.childs;
}
public void setChilds(List childs) {
this.childs = childs;
}
public String toString() {
return new ToStringBuilder(this)
.append(“id“, getId())
.toString();
}
public boolean equals(Object other) {
if ( !(other instanceof Parent) ) return false;
Parent castOther = (Parent) other;
return new EqualsBuilder()
.append(this.getId(), castOther.getId())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(getId())
.toHashCode();
}
}
Parent parent = (Parent)session.load(Parent.class,new Integer(7));
运行提示出错,”can’t get Entity..”
知道是什么低级错误么? 其实很弱智的东西,但是不注意还就会出现,呵呵,答案如下:


Parent parent = (Parent)session.load(Parent.class,new Long(7))
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
October 23, 2006
碰到一个奇怪的问题,Spring在启动的时候得listener提示启动失败,打开log也没有任何信息,最后把log4j打开 :
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE log4j:configuration SYSTEM ”log4j.dtd”>
<log4j:configuration xmlns:log4j=”http://jakarta.apache.org/log4j/”>
<appender name=”CONSOLE” class=”org.apache.log4j.ConsoleAppender”>
<layout class=”org.apache.log4j.PatternLayout”>
<param name=”ConversionPattern”
value=”%p - %C{1}.%M(%L) | %m%n”/>
</layout>
</appender>
<logger name=”org.apache”>
<level value=”WARN”/>
</logger>
<logger name=”net.sf.hibernate”>
<level value=”WARN”/>
</logger>
<logger name=”org.springframework”>
<level value=”DEBUG”/>
</logger>
<!–
<logger name=”org.appfuse”>
<level value=”DEBUG”/>
</logger>
–>
<root>
<level value=”DEBUG”/>
<appender-ref ref=”CONSOLE”/>
</root>
</log4j:configuration>
提示说applicationContext.xml的编码有问题,最后改为UTF-8解决
VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]