Spring+Hibernate+Junit+Oracle的最简单的人门

一年前,就计划学习Spring,但初步了解一下,发现还是有些庞大,就知难而退了。最近反思一年来开发进展迟缓的原因,发现技术学习上比较保守,是重要原因。学习主流的开发技术,虽然有学习成本,但会有开发效率的提升。
第一个目标:用Spring+Hibernate完成对持久层的改造,这是Spring最迷人的应用之一吧。
Spring比较复杂,有七大部分,比如ORM/MVC/WEB等等,我想,应该分步学习,有些部分可能我以后也不打算学习,比如其MVC部分。
首先,作一点理论准备。SpringFramework的官方站点查阅资料,初步了解一下IOC,AOP这些SpringFramework力推的核心概念。阅读了官方手册,以及《Spring In Action》的部分内容,以及Martin Fowler谈IOC(Dependency Injection)的文章。
其次,寻找可用的入门教程,最后锁定了IBM developworks上的一篇文章,以及SpringFramework自带的petclinic作为实战材料。
Object-relation mapping without the container-Develop a transactional persistence layer using Hibernate and Spring这篇文章非常符合我的目标。透明思考称该文的作法是:“Spring结合Hibernate的标准做法”
我的开发环境是:Jbuilder X+Oracle 8i +Jboss 3.27,操作系统是TurboLinux Server 8.0。
下载Richard HighTower 的源代码。
让这个例子运行起来,居然费了我一整天的时间。HighTower的例子,其数据库用的是Mysql,而我的是Oracle 8i,单单这个差别,就浪费了我一天的时间,因为Dbunit操作Mysql 与Oracle有些细节地方不同,就会导致运行失败。
为了让和我一样的初学者少走点弯路,下面给出我改造过的例子,尽可能的简化再简化,因此本例特地只使用一个数据库表:hofman_user。
1. 建立数据表
create table hofman_user (pk_user_id number(8),
vc_email varchar2(50),
vc_password varchar2(80),
primary key(pk_user_id));
2. 建立hofman_user表主键pk_user_id使用的序列
create sequence hofmanid_seq;
3. 一个数据表对应一个POJO:


package hofman;

import java.util.ArrayList;
import java.util.List;


public class User {

private Long id = new Long(-1);
private String email;
private String password;

public Long getId() {
return id;
}


public void setId(Long long1) {
id = long1;
}


public String getEmail() {
return email;
}

public String getPassword() {
return password;
}

public void setEmail(String string) {
email = string;
}


public void setPassword(String string) {
password = string;
}
}

4. 对应的hibernate 的映射文件:User_hbm.xml

<?xml version="1.0"?>

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

<hibernate-mapping>
<class
name=
"hofman.User"
table=
"HOFMAN_USER"
dynamic-update=
"false"
dynamic-insert=
"false"
discriminator-value=
"2"
>

<id
name=
"id"
column=
"PK_USER_ID"
type=
"java.lang.Long"
unsaved-value=
"-1"
>
<generator class=
"sequence">
<param name=
"sequence">hofmanid_seq</param>
</generator>
</id>
<property
name=
"email"
type=
"string"
update=
"false"
insert=
"true"
column=
"VC_EMAIL"
length=
"82"
not-null=
"true"
unique=
"true"
/>
<property
name=
"password"
type=
"string"
update=
"false"
insert=
"true"
column=
"VC_PASSWORD"
length=
"20"
not-null=
"true"
unique=
"true"
/>

</class>

<query name=
"AllUsers"><![CDATA[
from User user order by user.email asc
]]></query>


</hibernate-mapping>


5. UserDAO的interface文件:UserDAO.java

package hofman;

import java.util.List;

public interface UserDAO {

public List getUsers();

public void addUser(User user);

public User findUserByEmail(String email);

public void removeUser(String email);
}


6. UserDAO的实现文件:UserDAOImpl.java

package hofman;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Query;
import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;


public class UserDAOImpl extends HibernateDaoSupport implements UserDAO{

public List getUsers(){
return getHibernateTemplate().findByNamedQuery(
"AllUsers");
}

public User findUserByEmail(String email) {
return (User) getHibernateTemplate().find(
"from User u where u.email=?",email).get(0);
}

public void addUser(User user){
getHibernateTemplate().save(user);
}

public void removeUser (String email) {
User user = findUserByEmail(email);
this.getHibernateTemplate().delete(user);
}

}


7.SpringFramework的配置文件:. ApplicationContext.xml


<?xml version=
"1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
"-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<!--
- Application context definition for Express on Hibernate.
-->
<beans>
<!--
<bean id=
"dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name=
"jndiName">
<value>java:/comp/env/jdbc/OracleDs</value>
</property>
</bean>
-->
<!-- Datasource that works in any application server
You could easily use J2EE data source instead if this were
running inside of a J2EE container.
-->
<bean id=
"dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name=
"driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
<property name=
"url"><value>jdbc:oracle:thin:@192.168.1.2:1521:yulog</value></property>
<property name=
"username"><value>hofman</value></property>
<property name=
"password"><value>topSecret</value></property>
</bean>

<!-- Hibernate SessionFactory -->
<bean id=
"sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name=
"dataSource"><ref local="dataSource"/></property>

<!-- Must references all OR mapping files. -->
<property name=
"mappingResources">
<list>
<value>/hofman/User.hbm.xml</value>

</list>
</property>

<!-- Set the type of database; changing this one property will port this to Oracle,
MS SQL etc. -->
<property name=
"hibernateProperties">
<props>
<prop key=
"hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>
</props>
</property>
</bean>

<!-- Pass the session factory to our UserDAO -->
<bean id=
"userDAOTarget" class="hofman.UserDAOImpl">
<property name=
"sessionFactory"><ref local="sessionFactory"/></property>
</bean>

<bean id=
"transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name=
"sessionFactory"><ref bean="sessionFactory"/></property>
</bean>

<bean id=
"userDAO"
class=
"org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name=
"transactionManager"><ref local="transactionManager"/></property>
<property name=
"target"><ref local="userDAOTarget"/></property>
<property name=
"transactionAttributes">
<props>
<prop key=
"addUser">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>


7. 调用SrpingFramework的文件:ApplicationContextFactory.xml


package hofman;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @author Richard Hightower
* ArcMind Inc. http://www.arc-mind.com
*/

public class ApplicationContextFactory {


private static Log log = LogFactory.getLog(ApplicationContextFactory.class);

private static Object initObj = null;
private static int count = 0;


public static void init(Object o){
if (count > 0){
log.error(
"Can't initialize the application context twice: THIS SHOULD ONLY HAPPEN DURING TESTING");
}
initObj=o;
count++;
}

public static ApplicationContext getApplicationContext(){
if (initObj == null){
throw new IllegalStateException(
"Application context not initialized");
}
// else if (initObj instanceof ServletContext){
// ServletContext servletContext = (ServletContext) initObj;
// return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
//
// }
else if (initObj instanceof String){
String contextResourceLocation = (String) initObj;
return new ClassPathXmlApplicationContext(contextResourceLocation);
} else {
//throw new IllegalStateException("You must initialize the context with a String or ServletContext");
throw new IllegalStateException(
"You must initialize the context with a String");
}
}

}


8. Junit的测试用例:UserDAOTest.java


package hofman;

import java.util.List;
import junit.framework.TestCase;

// added by hofman on 2005-07-22
public class UserDAOTest extends TestCase {
private UserDAO userDAO;

static {
ApplicationContextFactory.init(
"/hofman/applicationContext.xml");
}

public void testAddUser(){
User user = new User();
String email =
"hofman@zhuoda.org";
user.setEmail(email);
user.setPassword(
"fakePass");
userDAO.addUser(user);

email =
"s@s.com";
user.setEmail(email);
user.setPassword(
"topSecret");
userDAO.addUser(user);

User user2 = userDAO.findUserByEmail(email);
assertEquals(email, user2.getEmail());
}


public void testFindUserByEmail(){
User user = userDAO.findUserByEmail(
"hofman@zhuoda.org");
assertNotNull(user);
}


public void testKillUser()throws Exception{
String email =
"s@s.com";
userDAO.removeUser(email);
}

protected void setUp() throws Exception {
super.setUp();
userDAO = (UserDAO) ApplicationContextFactory.getApplicationContext().getBean(
"userDAO");

}

protected void tearDown() throws Exception {
super.tearDown();
}


public static void main(String[] args) {
junit.swingui.TestRunner.run(UserDAOTest.class);
}

}


9. 调用UserDAO的jsp文件:getPass.jsp


<%@ page contentType=
"text/html; charset=GBK" %>
<%@ page import=
"hofman.*" %>
<%
UserDAO userDAO;
String password;

ApplicationContextFactory.init(
"/hofman/applicationContext.xml");
userDAO = (UserDAO) ApplicationContextFactory.getApplicationContext().getBean(
"userDAO");

User user = userDAO.findUserByEmail(
"hofman@zhuoda.org");
password = user.getPassword();

%>
<html>
<head><title>Get Password</title></head>
<body bgcolor=
"#ffffff">
<h1>hofman's password:<%=password %></h1>
</body>
</html>


10.完整的代码下载
[url=ftp://ftp.zhuoda.org/incoming/SpringInAction/helloSpring.tgz]helloSring.tgz[/url]
说明:
源代码很伟大,但适合新手阅读的源代码的文档很难找。我就是从HighTower的代码基础上开始起步,但编译时,首先就遇到一出错误,getHibernate.creatQuery()不存在,可能原作所使用的SpringFramework的版本比较老,我用的是写作本文时最新的1.2.2,注释掉相关部分,算是通过了,但UserDAOTest.java无法运行,Dbunit出问题了,费了老大劲,也未完全搞定,只不过,可用继续运行。
今天才发现,就本例,其实用junit就可用了,没有必要使用dbunit。Dbunit很伟大,如果数据库用的是Mysql的话,如果象我一样,数据库用oracle的话,也许有不少麻烦。

让Jbuider X支撑最新的SpringFramework很简单,Tools/Configure Libraries建立一个spring库,把spring发行包里面dist目录里面的jar加进去,就可用了。
支持Dbunit的方法类似。

前后化了一个星期的时间,终于可用使用被大吹特吹的Spring+Hibernate的持久层方案了,总算学会了用Junit调试连接数据库的DAO程序。

当然,这只是第一步。不过,目前,我并不想完全放弃我比较熟悉的J2EE开发模式,不管怎么说,引入新的持久层开发模式、特别是高效地调试模式,对提升开发效率无疑有好处。
hofman   2005-07-22 20:44:35 评论:5   阅读:5959   引用:0
下载路径 @2007-01-05 19:35:35  hofman
新的下载路径,helloSpring
@2007-01-05 17:52:09  hello
怎么找不到下载的路径
这片教程对新手非常好 @2006-11-19 17:19:50  lunzi
这教程对于学习spring和hibernate的结合的新手来说,是再合适不过了.现在对spring熟悉了,看这代码非常舒服!
包名称 @2006-09-14 16:36:10  hofman
src/hofman, hofman是包的名称,因为文件少,没有再分子包,比如hofman.dao,hofman.model,hofman.service之类.
请教一下 @2006-09-14 11:10:17  地主
我不明白你的这句  
ApplicationContextFactory.init("/hofman/applicationContext.xml");

难道你的applicationContext.xml文件放到hofman下的?
hofman是个什么目录?普通目录?

发表评论>>

署名发表(评论可管理,不必输入下面的姓名)

姓名:

主题:

内容: 最少15个,最长1000个字符

验证码: (如不清楚,请刷新)

2003-2007@copyright