开发环境的塔建我就不多说了,具体可以参考我以前的几篇日志。
在Eclipse中新建一个动态Web工程,将Struts 、Spring、Hibernate需要的jar都添加到WEB-INF/lib目录下,
将Struts标签所需的tld文件、struts-config.xml文件(可以由StrutsIDE生成)添加到WEB-INF下,另外在
此目录下添加一个Spring的配置文件applicationContext.xml。
我参考的例子是夏昕的《深入浅出Hibernate》上的第6章,很好的一个论坛示例。
首先按照书上的ER模型,在数据库(MySQL)中建立相应的数据库。
create database forum;
use forum;
create table user(
id int not null auto_increment primary key,
name varchar(50),
pwd varchar(50),
email varchar(50)
)type=innodb;
create table board(
id int not null auto_increment primary key,
parent_id int,
create_by int not null,
name varchar(50) not null,
remark varchar(255),
create_time datetime,
index(parent_id),
index(create_by),
foreign key(parent_id) references board(id) on delete cascade,
foreign key(create_by) references user(id) on delete no action
)type=innodb;
create table article(
id int not null auto_increment primary key,
parent_id int ,
board_id int not null,
article_type int not null,
title varchar(255),
body text,
create_by int not null,
create_time datetime,
hits int unsigned,
bytes int unsigned,
last_update_by int not null,
last_update_time datetime,
index(parent_id),
index(board_id),
index(create_by),
index(last_update_by),
foreign key(parent_id) references article(id) on delete no action,
foreign key(board_id) references board(id) on delete cascade,
foreign key(create_by) references user(id) on delete no action,
foreign key(last_update_by) references user(id) on delete no action
)type=innodb;
之后,我选择了用Hibernate Sychonizer来生成映射文件和实体类,然后对生成的类进行修改(删掉了生成的Base类),
实际上Hibernate Sychonizer生成的映射文件也有错误,需要修改,我也是后来在使用中发现的。
//User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
" >
<hibernate-mapping package="com.lzy.forum.domain">
<class
name="User"
table="user"
>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="Name"
column="name"
type="string"
not-null="false"
length="50"
/>
<property
name="Pwd"
column="pwd"
type="string"
not-null="false"
length="50"
/>
<property
name="Email"
column="email"
type="string"
not-null="false"
length="50"
/>
<set name="ArticlesByLastUpdateBy" inverse="true" lazy="true">
<key column="last_update_by"/>
<one-to-many class="Article"/>
</set>
<set name="ArticlesByCreateBy" inverse="true" lazy="true">
<key column="create_by"/>【这里的create_by是后来修改的,Hibernate Sychonizer生成的有错,后面有几处同样的错误】
<one-to-many class="Article"/>
</set>
<set name="Boards" inverse="true" lazy="true">
<key column="create_by"/>【这里的create_by是后来修改的】
<one-to-many class="Board"/>
</set>
</class>
</hibernate-mapping>
//User.java
package com.lzy.forum.domain;
public class User{
private static final long serialVersionUID = 1L;
// primary key
private java.lang.Integer id;
// fields
private java.lang.String name;
private java.lang.String pwd;
private java.lang.String email;
// collections
private java.util.Set<Article> articlesByLastUpdateBy;
private java.util.Set<Article> articlesByCreateBy;
private java.util.Set<Board> boards;
public java.util.Set<Article> getArticlesByCreateBy() {
return articlesByCreateBy;
}
public void setArticlesByCreateBy(java.util.Set<Article> articlesByCreateBy) {
this.articlesByCreateBy = articlesByCreateBy;
}
public java.util.Set<Article> getArticlesByLastUpdateBy() {
return articlesByLastUpdateBy;
}
public void setArticlesByLastUpdateBy(
java.util.Set<Article> articlesByLastUpdateBy) {
this.articlesByLastUpdateBy = articlesByLastUpdateBy;
}
public java.util.Set<Board> getBoards() {
return boards;
}
public void setBoards(java.util.Set<Board> boards) {
this.boards = boards;
}
public java.lang.String getEmail() {
return email;
}
public void setEmail(java.lang.String email) {
this.email = email;
}
public java.lang.Integer getId() {
return id;
}
public void setId(java.lang.Integer id) {
this.id = id;
}
public java.lang.String getName() {
return name;
}
public void setName(java.lang.String name) {
this.name = name;
}
public java.lang.String getPwd() {
return pwd;
}
public void setPwd(java.lang.String pwd) {
this.pwd = pwd;
}
}
//Board.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
" >
<hibernate-mapping package="com.lzy.forum.domain">
<class
name="Board"
table="board"
>
<meta attribute="sync-DAO">false</meta>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="Name"
column="name"
type="string"
not-null="true"
length="50"
/>
<property
name="Remark"
column="remark"
type="string"
not-null="false"
length="255"
/>
<property
name="CreateTime"
column="create_time"
type="timestamp"
not-null="false"
length="19"
/>
<many-to-one
name="CreateBy"
column="create_by"
class="User"
not-null="true"
>
</many-to-one>
<many-to-one
name="Parent"
column="parent_id"
class="Board"
not-null="false"
lazy="false">
</many-to-one>
<set name="Articles" inverse="true" lazy="true" order-by="id">
<key column="board_id"/>【这里的board_id是后来修改的】
<one-to-many class="Article"/>
</set>
<set name="ChildBoards" inverse="true" lazy="true" order-by="id">
<key column="parent_id"/>【这里的parent_id是后来修改的】
<one-to-many class="Board"/>
</set>
</class>
</hibernate-mapping>
//Board.java
package com.lzy.forum.domain;
public class Board {
private static final long serialVersionUID = 1L;
// primary key
private java.lang.Integer id;
// fields
private java.lang.String name;
private java.lang.String remark;
private java.util.Date createTime;
// many to one
private com.lzy.forum.domain.User createBy;
private com.lzy.forum.domain.Board parent;
// collections
private java.util.Set<com.lzy.forum.domain.Article> articles;
private java.util.Set<com.lzy.forum.domain.Board> childBoards;
public java.util.Set<com.lzy.forum.domain.Article> getArticles() {
return articles;
}
public void setArticles(java.util.Set<com.lzy.forum.domain.Article> articles) {
this.articles = articles;
}
public java.util.Set<com.lzy.forum.domain.Board> getChildBoards() {
return childBoards;
}
public void setChildBoards(java.util.Set<com.lzy.forum.domain.Board> childBoards) {
this.childBoards = childBoards;
}
public com.lzy.forum.domain.User getCreateBy() {
return createBy;
}
public void setCreateBy(com.lzy.forum.domain.User createBy) {
this.createBy = createBy;
}
public java.util.Date getCreateTime() {
return createTime;
}
public void setCreateTime(java.util.Date createTime) {
this.createTime = createTime;
}
public java.lang.Integer getId() {
return id;
}
public void setId(java.lang.Integer id) {
this.id = id;
}
public java.lang.String getName() {
return name;
}
public void setName(java.lang.String name) {
this.name = name;
}
public com.lzy.forum.domain.Board getParent() {
return parent;
}
public void setParent(com.lzy.forum.domain.Board parent) {
this.parent = parent;
}
public java.lang.String getRemark() {
return remark;
}
public void setRemark(java.lang.String remark) {
this.remark = remark;
}
}
//Article.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
" >
<hibernate-mapping package="com.lzy.forum.domain">
<class
name="Article"
table="article"
>
<meta attribute="sync-DAO">false</meta>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="ArticleType"
column="article_type"
type="integer"
not-null="true"
length="11"
/>
<property
name="Title"
column="title"
type="string"
not-null="false"
length="255"
/>
<property
name="Body"
column="body"
type="string"
not-null="false"
/>
<property
name="Hits"
column="hits"
type="integer"
not-null="false"
length="10"
/>
<property
name="Bytes"
column="bytes"
type="integer"
not-null="false"
length="10"
/>
<property
name="CreateTime"
column="create_time"
type="timestamp"
not-null="false"
length="19"
/>
<property
name="LastUpdateTime"
column="last_update_time"
type="timestamp"
not-null="false"
length="19"
/>
<many-to-one
name="LastUpdateBy"
column="last_update_by"
class="User"
not-null="true"
>
</many-to-one>
<many-to-one
name="CreateBy"
column="create_by"
class="User"
not-null="true"
>
</many-to-one>
<many-to-one
name="Parent"
column="parent_id"
class="Article"
not-null="false"
>
</many-to-one>
<many-to-one
name="Board"
column="board_id"
class="Board"
not-null="true"
>
</many-to-one>
<set name="Articles" inverse="true" lazy="false" order-by="id">
<key column="parent_id"/>【这里的parent_id是后来修改的】
<one-to-many class="Article"/>
</set>
</class>
</hibernate-mapping>
//Article.java
package com.lzy.forum.domain;
import java.util.Date;
public class Article {
private static final long serialVersionUID = 1L;
// primary key
private java.lang.Integer id;
// fields
private java.lang.Integer articleType;
private java.lang.String title;
private java.lang.String body;
private java.lang.Integer hits;
private java.lang.Integer bytes;
private java.util.Date lastUpdateTime;
private java.util.Date createTime;
// many to one
private com.lzy.forum.domain.User lastUpdateBy;
private com.lzy.forum.domain.User createBy;
private com.lzy.forum.domain.Article parent;
private com.lzy.forum.domain.Board board;
// collections
private java.util.Set<com.lzy.forum.domain.Article> articles;
public java.util.Set<com.lzy.forum.domain.Article> getArticles() {
return articles;
}
public void setArticles(java.util.Set<com.lzy.forum.domain.Article> articles) {
this.articles = articles;
}
public java.lang.Integer getArticleType() {
return articleType;
}
public void setArticleType(java.lang.Integer articleType) {
this.articleType = articleType;
}
public com.lzy.forum.domain.Board getBoard() {
return board;
}
public void setBoard(com.lzy.forum.domain.Board board) {
this.board = board;
}
public java.lang.String getBody() {
return body;
}
public void setBody(java.lang.String body) {
this.body = body;
}
public java.lang.Integer getBytes() {
return bytes;
}
public void setBytes(java.lang.Integer bytes) {
this.bytes = bytes;
}
public com.lzy.forum.domain.User getCreateBy() {
return createBy;
}
public void setCreateBy(com.lzy.forum.domain.User createBy) {
this.createBy = createBy;
}
public java.lang.Integer getHits() {
return hits;
}
public void setHits(java.lang.Integer hits) {
this.hits = hits;
}
public java.lang.Integer getId() {
return id;
}
public void setId(java.lang.Integer id) {
this.id = id;
}
public com.lzy.forum.domain.User getLastUpdateBy() {
return lastUpdateBy;
}
public void setLastUpdateBy(com.lzy.forum.domain.User lastUpdateBy) {
this.lastUpdateBy = lastUpdateBy;
}
public java.util.Date getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(java.util.Date lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
public com.lzy.forum.domain.Article getParent() {
return parent;
}
public void setParent(com.lzy.forum.domain.Article parent) {
this.parent = parent;
}
public java.lang.String getTitle() {
return title;
}
public void setTitle(java.lang.String title) {
this.title = title;
}
public java.util.Date getCreateTime(){
return createTime;
}
public void setCreateTime(java.util.Date createTime) {
// TODO Auto-generated method stub
this.createTime = createTime;
}
接下来将会在web中加入Spring支持和为实体类提供DAO支持。
Spring和Struts结合有几种方法,我选了最常用的PlugIn方式,在struts-config.xml中加入
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/action-servlet.xml" />
</plug-in>
WEB-INF目录下的action-servlet.xml就是Spring的配置文件,下面给出的是整个的action-servlet.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
//Data Source
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/forum</value>
</property>
<property name="username">
<value>test</value>
</property>
<property name="password">
<null />
</property>
</bean>
// For Hibernate
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>com/lzy/forum/domain/User.hbm.xml</value>
<value>com/lzy/forum/domain/Article.hbm.xml</value>
<value>com/lzy/forum/domain/Board.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
// For DAO
<bean id="userDAO"
class="com.lzy.forum.dao.impl.UserDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="boardDAO"
class="com.lzy.forum.dao.impl.BoardDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="articleDAO"
class="com.lzy.forum.dao.impl.ArticleDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
//For Struts Action
<bean name="/regist" class="com.lzy.forum.action.RegistAction"
singleton="false">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
<bean name="/login" class="com.lzy.forum.action.LoginAction"
singleton="false">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
<bean name="/boardManage"
class="com.lzy.forum.action.BoardManageAction" singleton="false">
<property name="userDAO">
<ref local="userDAO" />
</property>
<property name="boardDAO">
<ref local="boardDAO" />
</property>
</bean>
<bean name="/boardNavigate"
class="com.lzy.forum.action.BoardNavigateAction" singleton="false">
<property name="boardDAO">
<ref local="boardDAO" />
</property>
</bean>
<bean name="/boardDisplay"
class="com.lzy.forum.action.BoardDisplayAction" singleton="false">
<property name="boardDAO">
<ref local="boardDAO" />
</property>
</bean>
<bean name="/articleManage"
class="com.lzy.forum.action.ArticleManageAction" singleton="false">
<property name="userDAO">
<ref local="userDAO" />
</property>
<property name="boardDAO">
<ref local="boardDAO" />
</property>
<property name="articleDAO">
<ref local="articleDAO" />
</property>
</bean>
</beans>
经过这样的配置之后,Hibernate事务交由Spring管理,sessionFactory从Spring容器获得。下面通过其中的一个DAO实例来看看这样做带来的方便。
//BoardDAO.java
package com.lzy.forum.dao;
import java.util.ArrayList;
import com.lzy.forum.domain.Board;
public interface BoardDAO {
public boolean isBoardExist(String name);
public void addBoard(Board board);
public void deleteBoard(Board board);
public Board loadBoard(int id);
public Board loadBoard(String name);
public Board loadBoardWithArticles(int id);
public ArrayList getRootBoardsList();
}
//BoardDAOHibernate.java
package com.lzy.forum.dao.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Set;
import org.hibernate.Hibernate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.lzy.forum.dao.BoardDAO;
import com.lzy.forum.domain.Board;
public class BoardDAOHibernate extends HibernateDaoSupport implements BoardDAO {
public void deleteBoard(Board board) {
// TODO Auto-generated method stub
this.getHibernateTemplate().delete(board);
}
public void addBoard(Board board) {
// TODO Auto-generated method stub
this.getHibernateTemplate().saveOrUpdate(board);
}
public boolean isBoardExist(String name) {
// TODO Auto-generated method stub
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board where name = ? ", new Object[] { name });
if (list.size() > 0) {
// System.out.println("find it");
return true;
}
return false;
}
public Board loadBoard(int id) {
// TODO Auto-generated method stub
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board where id = ? ", new Object[] { id });
Board b = null;
if (list.size() > 0) {
b = (Board) list.get(0);
}
return b;
}
public Board loadBoard(String name) {
// TODO Auto-generated method stub
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board where name = ? ", new Object[] { name });
Board b = null;
if (list.size() > 0) {
b = (Board) list.get(0);
}
return b;
}
public ArrayList getRootBoardsList() {
// TODO Auto-generated method stub
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board b left join fetch b.ChildBoards where b.Parent = null order by b.id");
System.out.println(list.size() + " root boards found ");
ListIterator index = list.listIterator();
while (index.hasNext()) {
Board s = (Board) index.next();
//this.getSession(true);
//Hibernate.initialize(s.getChildBoards());
///*
Set children = (Set) s.getChildBoards();
Iterator it = children.iterator();
while(it.hasNext()){
Board b = (Board)it.next();
//Hibernate.initialize(b);
System.out.println(b.getName());
}
//*/
//System.out.println(children.size() + "child(ren) found ");
//s.setChildBoards(s.getChildBoards());
}
return list;
}
public Board loadBoardWithArticles(int id) {
// TODO Auto-generated method stub
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board b left join fetch b.Articles where b.Id = ? ", new Object[] { id });
Board b = null;
if (list.size() > 0) {
b = (Board) list.get(0);
}
return b;
}
}
BoardDAOHibernate继承 HibernateDaoSupport后,通过getHibernateTemplate()得到一个HibernateTemplate实例,然后执行CRUD操作,非常简单。需要注意的是,由于执行一次CRUD操作后,Hibernate session关闭,如果有使用延迟加载策略的对象没有加载,在后面的Web层很容易出现如下错误:
failed to lazily initialize a collection of role
从网上搜到的解决方法是Open Session In View,
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<!-- singleSession默认为true,若设为false则等于没用OpenSessionInView -->
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
我试了试,还是有一些问题,所以后来还是放弃了这种方法,所有在Web层中需要的对象都必须在Business层中加载完成。如下面的代码所示:
ArrayList list = (ArrayList) this.getHibernateTemplate().find(
"from Board b left join fetch b.Articles where b.Id = ? ", new Object[] { id });
这样Hibernate和Spring的结合也算完成了。其他的一些DAO类和实现代码我没有给出,但是大同小异,和Board的实现类似。
最后将是Struts 和Spring的结合,将在下一篇给出。
前面通过PlugIn我们实现了Spring Context的加载,不过仅仅加载Context并没有什么实际
意义,我们还需要修改配置,将Struts Action交给Spring容器进行管理。下面将通过一个Regsit
实例加以说明。
首先准备好regist.jsp和login.jsp,在regist.jsp中 的form有name,password,password2,email域。
在struts-config.xml中添加配置:
<form-bean name="registForm" type="com.lzy.forum.form.RegistForm" />
<action path="/regist" name="registForm"
type="org.springframework.web.struts.DelegatingActionProxy"
validate="true" input="/regist.jsp" scope="request">
<forward name="failure" path="/regist.jsp" />
<forward name="success" path="/login.jsp" />
</action>
RegistForm,RegistAction按照原来Struts的方法去写,我在RegistAction中加入了一个UserDAO对象
userDAO,这个对象由Spring注入。
<bean name="/regist" class="com.lzy.forum.action.RegistAction"
singleton="false">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
基本上SSH的架构已经完成,经确认后可以自己测试一下了。