一.学习目标
系统的研究Struts框架,Hibernate和Spring的运行原理以及分别DEMO演示Struts+Hibernate,Struts+Spring,Struts+Hibernate+Spring的互相结合时的运行流程及相关具体配置。
二.详细资料
1. STRUTS
2. Hibernate
(1)什么是Hibernate?
Hibernate就是帮助我们从面向对象的程序到关系型数据的映射,他可以帮助我们脱离具体的数据库细节,并可以帮助我们生成Sql语句,简化JDBC编程,这样就可以实现程序的面向对象化和数据库的移植。
(2)Hibernate与JDBC相比的优缺点
A.JDBC在对对象操作时要写大量烦琐的代码
例:PreparedStatement pt=con.prepareStatement(insert into “表名” values(?,?,?,?,?,?))
Pt.setString(1,EntityBean.getXX());
Pt.setString(2,EntityBean.getXX());
Pt.setString(3,EntityBean.getXX());……
如果实体BEAN中有上百个属性,使用JDBC那将是会非常大工作量;
而Hibernate可以直接对对象进行操作(save(Object)),省却了很多工作。
B.JDBC在处理多表连接时非常麻烦,如果要删除某条信息,需要知道关于此表的外联信息,即哪几张表和此表有外联关系,并需要同时删除所有外联表中的此信息,需要花费很多精力。
而Hibernate则可以在配置(cscade)自动建立这种多表关系,在对某表中信息操作会自动对其级联表的相关信息进行操作。
C.JDBC在开发中要求程序员对底层的数据库结构(哪个表中有什么字段等)非常了解,才能够很好的使用SQL语句对数据库进行操作。
而Hibernate则使数据层完全被隐藏,程序员只需对映射的BEAN对象进行一些GET和SET方法就可以对数据库进行操作,无须了解数据库的情况。
D.JDBC在进行大批量操作时,如批量更新,删除n条记录时,正常情况下需要向数据库发送n次语句,性能很差。
Hibernate可以使用batch功能可以将语句缓冲,最后一次发送给数据库,但性能提高不多。
而调用JDBC的API进行操作能使时间缩短十几倍,所以在进行大批量操
作时最好的方法是调用JDBC的API实现。
(3)Hibernate配置相关的类
A.Configuration类(负责管理Hibernate的配置信息)
Configuration类是Hibernate的入口,新建Configuration实例时,configure()方法会自动寻找Hibernate.cfg.xml配置文件,如找不到会报错;如找到会读取SessionFactory的name属性,以后在建立SessionFactory时就会使用指定的映射文档。Configuration是仅在配置期使用的对象,从第一个SessionFactory开始建立的时候,它就失效了。
B. SessionFactory类
负责Session实例的创建,SessionFactory是线程安全的,可以被多线程调用取得Session,但构造SessionFactory很费资源,所以一个应用中只初始化一个SessionFactory。
C. Session类
Session是Hibernate的中心,Session不是线程安全的,所以如果多个线程共享一个Session就会产生混乱。所以会使用ThreadLocal类来建立一个Session管理的辅助类,可以有效隔离执行所需要的数据。
每一个请求应该使用一个单独的Session。
每一个Session实例都可以看作为一个容器作为Hibernate的一及缓存。其作用一是减少程序访问数据库的次数,很多对象数据不是经常改变的,第一次访问时Hibernate将它放入缓存中,以后只要这个对象没有改动过,访问这个对象时,会先在Session的缓存内查找,如果有此对象,则直接返回应用程序;如果没有则发送 SQL语句到数据库,将字段组装成对象存于Session中。二是如果一个事务对数据进行了改变,这种变化不会立即传到数据库,Hibernate能对一些数据改变进行结合,从而对数据库进行最小数量的请求。
(4)对象在Hibernate中的状态
有3种状态:
A.瞬时状态
以new命令开辟内存空间的对象(A a = new A(“TOM”)),不和Session实例相关联,在数据库中没有和瞬时对象关联的数据,可以通过save()方法将瞬时对象插入到数据库中变成持久对象。
B. 持久化状态
持久的实例在数据库中有对应的记录,并拥有一个持久化标志。
如果使用delete()方法,它就会变为瞬时对象。
持久对象总是与Session和Transaction想关联,在一个Session中,对持久对象的改变不会马上对数据库变更,必须在Transaction终止即执行commit()后才进行变更。
C.脱管状态
当一个Session执行close()后,持久对象会变为脱管对象。它与瞬时对象本质上是相同的,只是比瞬时对象多了数据标志的id。
当脱管对象重新关联到新的Session上时候(通过update()等方法),会再次转变为持久的(脱管状态其间的改动将被持久化到数据库)。
(5)Session的常用方法
A.Session的save()方法(保存)
例子:Team t=new Team();
t.setId(“ 1000” );
t.setName(“TON”);
Transaction tx = session.beginTransaction(); // 开启事务
session.save(t);
tx.commit();
Session保存对象时先根据配置文件为主键id设置的生成算法.为t指定一个id,然后见t对象放入Session缓存,事务提交时将新对象通过insert语句持久化到数据库。
注意:t.setId(“ 1000” );
这句话设置的id无效,会被配置文件为主键id设置的生成算法替代。如要强行指定id,则可以调用
session.save(t,“1000“);
不过代理主键时候最好不要使用,以免主键的内容混乱;
Team t=new Team();
这句话说明save()方法只能对临时对象使用,而不能对脱管方法使用。
B.Session的update()方法(更新)
update()方法的特点是执行此方法时不发送SQL语句, Hibernate总是执行update()方法的。
例:Team t=(Team)session.get(Team.class,“--id号--“);
t.setName(“TOM“);
Transaction tx = session.beginTransaction(); // 开启事务
session.update(t);
tx.commit();
上面代码中session.update(t);是没有必要的,即使没有调用在事务提交时数据变更仍然会提交到数据库中,其实大部分情况下update()只是为了关联一个脱管对象到持久状态。
另外如果将session.update(t);更换成session.save(t);那么在清理缓存时Hibernate仍然会发送一条update语句以保证脱管对象和数据库记录一致。
注意:Team t=(Team)session.get(Team.class,“--id号--“);
说明update()方法只能对脱管对象进行操作而不能对临时对象进行操作。
C.Session的saveOrUpdate()方法
实际应用中往往不会注意对象的状态是临时还是脱管,所以为了防止save()和update()方法的错误应用,就产生了此方法,它会自动判断对象的状态来选择合适的方法来操作。
D.Session的delete()方法
此方法负责删除一个对象(包括持久对象和脱管对象)
例:Team t=(Team)session.get(Team.class,“--id号--“);
Transaction tx = session.beginTransaction(); // 开启事务
session.delete(t);
tx.commit();
在调用此方法进行删除时,会有一些性能问题,特别是在大批量删除时,如果有10000条记录则Hibernate会向数据库发送10000条delete语句。
所以在大批量删除时需要使用bulk delete操作:
Query q=session.createQuery(“delete from 表”)
q.executeUpdate();
但仍然会有问题,删除后的数据会在缓存中存在,查询时可能得到数据库不存在的数据。
E. Get()方法
例:
…….// 打开session,开启事务
Stu=(Student)session.get(Student.class,”ID”);
……//提交事务,关闭Session
Get()方法的执行顺序如下:
首先通过ID在Session缓存中查找对象,如果存在此ID主键值的对象, 直接将其返回。
在二级缓存中查找,找到后将其返回。
如果在Session缓存和二级缓存中没找到,则数据库中加载拥有此ID的 对象。
也就是说,get()方法并不总是导致发送SQL语句,只有缓存中无此数据 时,才向数据库取得数据。
F. load()方法
Load()方法和get()方法都能通过主键ID值从数据库中加载一个实体对 象。
(1)get()方法
例:….//打开Session,开启事务
Student stu=(Student)session.get(Student.class,”noExitld”);
…..//提交事务,关闭Session
If(stu=null)
System.out.println(“stu对象为null”);
(2)load()方法
例:….//打开Session,开启事务
Student stu=(Student)session.load(Student.class,”noExitld”);
…..//提交事务,关闭Session
If(stu=null)
System.out.println(“stu对象为null”);
可以看到当对象不存在并且是立即加载时,get()方法返回 null,但load()方法弹出了例外。因此使用load()方法时,要 确认查询的主键ID一定是存在的。
G. Query接口
(1)Query query=session.createQuery(“from 表 s where s.age>? and s.name = ?”);
(2)Query query=session.createQuery(“from 表s where s.age>:age and s.name = :name”);
HQL传递参数时有多种方式,最常用方式如上,然后需在后面对上方所传直进行设定
(1)时
query.setInteger(0,25);
query.setString(1,”tom”);
(2)时
query.setInteger(“age”,25);
query.setString(“name”,”tom”);
也可将Bean传入参数,query.setEntity(“age”,Student)
H. List()方法
例 Query query=session.createQuery(“from 表 s where s.age>?”);
query.setInteger(0,25);
List list=query.list();
For(int i=0;i<list.size();i++)
Team t=(Team)list.get(i);
如上就获得了一组数据
I. uniqueResault()方法
uniqueResault()方法必须是查询返回的集合中只有一个对象时才可引用
例:
Query query=session.createQuery(“from 表 s where s.age= 25 ” );
Team t=(Team)qyery. uniqueResault();
这时候不必须确定表中age=25的记录只有一条,否则将抛出错误.
(6)Hibernate的事务管理
Hibernate的事务默认情况下采用JDBC事务管理,JDBC事务这里不 做具体阐述。
当多事务并发时,为避免产生并发时产生的数据访问错误需要对数据 进行锁定,有2种方法。
A。悲观锁定
悲观锁定一如其名称所示,悲观的认定每次存取资料时,其他的客户 端也会存取同一笔数据,因此对该笔数据进行锁定,直到自己操作完 成后解除锁定,它是一种阻止并发事务访问同一数据的一种机制。
一些数据库,比如ORACLE,提供了一个SQL语句:select….for update,以此来明确地指定数据库使用悲观锁定。可以在Hibernate 的配置文件中检查当前所用的数据库是否支持for update特性,如果不支 持,则Hibernate执行普通的、没有for update在句子最后的select语句。
如果要在事务中使用悲观锁,则可以像下面这样写:
Transaction tx=session.beginTransaction();
//取得持久化USER对象,并使用LockMode.UPGRADE模式锁定对象
User user=(User)session.get(user.class.userld.LockMode.UPGRADE);
User.setName(“newName”);
tx.commit();
B。乐观锁定
乐观锁定乐观的认为资料的存取很少发生同时存取的问题,因而不作 数据库层次上的锁定。为了维护正确的数据,乐观锁定使用应用程序上的 逻辑实现版本控制的解决。通过version实现的乐观锁定机制是Hibernate 官方推荐的乐观锁定实现,同时也是Hibernate中目前惟一在数据对象脱 离Session发生修改的情况下依然有效的锁机制。因此一般情况下都选择 version方式作为Hibernate乐观锁定实现机制。
1. 使用版本检查
<class name="bean.Login" table="login" schema="dbo" catalog="test" optimistic-lock=”version”>
<id name="id" type="java.lang.String">
<column name="id" length="50" />
<generator class="uuid.hex" />
</id>
<version name="version" column=" version " type=”int”/>
<version>标签必须紧跟在<id>标签之后。
2.<version>控制的原理
例:
Team t1=(Team) session1.createQuery(“from 表 s where s.age= 25 ” ). uniqueResault();
Team t2=(Team) session2.createQuery(“from 表 s where s.age= 25 ” ). uniqueResault();
// 同时查询同一个数据,此时t1.getVersion()和t2.getVersion()是一样的,都是0
tx1=session.beginTransaction();
t1.setNname(“TOM”);
tx1.commit();
//此时由于更新,数据库中的version变成了1
tx2=session.beginTransaction();
t2.setNname(“JACK”);
tx2.commit();
//因为其版本号仍然是0,而数据库中版本号已变为1,所以更新失败,抛出异常
注意:由于乐观锁定是使用系统程序来控制而不是数据库中的锁定机制,所以如果在上面第2个事务提交时加入t2.setVersion(1);就不会有错误发生,设计时需要注意。
3. Spring
4. Struts+Hibernate的实现
使用MyEclipse加载
新建一个WEB项目,并加载STRUTS框架,在此不做描述
(1) 在项目上点击右键选择MyEclipse加载Hibernate,如图
(2)选择后出现下图
这里选择Hibernate的版本号和加载Hibernate的JAR包
(3)创建Hibernate配置文件
指定了配置文件的名称和位置
(4)配置Hibernate的DataSource
DB Profile选项可以选择早先在MyEclipse DataBase视图中已建立好的DataSource
也可自己填写。
(5)建立SessionFactory类
点击Packages,选择SessionFactory类建立的位置完成加载
(6)加载后生成hibernate.cfg.xml配置文件和SessionFactory类
其中SessionFactory类中会自动生成currentSession()和closeSession()方法,前者是获得一个Session,后者是关闭Session。在业务类中只需调用其方法即可:
Session session = SessionFactory类名.currentSession();// 开启连接
最后事务提交后需要SessionFactory类名.closeSession();//关闭连接
(7)下面对数据库中的表进行映射配置
首先切换到MyEclipse DataBase视图,对目标数据库建立连接
选中要操作的表,右键选择“Creeate Hibernate Mapping”
(8)选择数据库对应表生成的配置文件和实体Bean的位置,自动更新Hibernate的配置文件和生成Bean
(9)指定表的主键生成方式,完成后对表的映射配置完成,生成表的配置文件和Bean类
ID Generator的生成方式:一般情况,我们使用“native”或uuid.hex
“assigned”
主键由外部程序负责生成,在 save() 之前指定一个。
“hilo”
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。
“seqhilo”
与hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle。
“increment”
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。
“identity”
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL 中的主键生成机制。
“sequence”
采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。
“native”
由 Hibernate 根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式
“uuid.hex”
由 Hibernate 基于8 位 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
“uuid.string”
与uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中。
“foreign”
使用另外一个相关联的对象的标识符作为主键。
(10)对表的映射配置文件的一些说明
<class name="bean.Login" table="login" schema="dbo" catalog="test">
此行指定了Bean对应的数据库表
<id name="id" type="java.lang.String">
<column name="id" length="50" />
<generator class="uuid.hex" />
</id>
id指明了表中的主键及主键的生成方式,注意如果表中没有定义主键,则Hibernate会将所有列自动设置为复合主键。
在id下添加<version name="version" column=" version "/>可以实现Habernate对数据库的乐观锁,但其一定要跟在<id></id>后,不能将其放到其他列之后声明
<property name="pwd" type="java.lang.String">
<column name="pwd" length="50" />
</property>
普通列的声明
<set name="classTables" inverse="true" cascade="all">
<key>
<column name="StudentID" not-null="true" />
</key>
<one-to-many class="model.ClassTable" />
</set>
如果有及联关系,配置如上
<set name="classTables" inverse="true" cascade="all">
生命了外键的来源,cascade="all"意思是如果更新或删除classTables表中的某条信息,则在本表中与其关联的数据也会自动更新或删除。
<key>
<column name="StudentID" not-null="true" />
</key>
声明了本表中的外键。
(11)具体业务处理见DEMO
5. Struts+Spring的实现
新建一个WEB项目,并加载STRUTS框架,在此不做描述
(1)在项目上点击右键选择MyEclipse加载Spring,如图
(2)加载Spring所需的JAR包
加载包时将其加载到lib文件夹下,否则发布或移动时会找不到所需的JAR包
普通情况下只需要加载Core包,Spring联合Hibernate时需要加载DAO包。
推荐自己加载整合包,无须选择。
(3)指定Spring配置文件的位置,加载完成
(4)对一些配置文件的说明
Struts和Spring结合后,就由Spring来管理所有的Action类
A.Struts配置文件说明
<action path="/Login"
type="org.springframework.web.struts.DelegatingActionProxy"
name="LoginForm"
scope="request"
>
红字部分声明了由Spring来管理Action类
<plug-in className= "org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property= "contextConfigLocation" value="/WEB-INF/classes/bean.xml"/>
</plug-in>
以上加载了Spring的配置文件,并由此配置文件来管理Action类
B.Spring配置文件说明
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=test</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
声明了dataSource,相当于连接池
<bean id="test" class="model.DB">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
数据库业务处理类,将上面定义的dataSource注入了model.DB类,在DB类中只需
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
用setDataSource方法就能获得注入的dataSource。
<bean name="/Login" class="com.yourcompany.struts.action.LoginAction">
<property name=" test ">
<ref bean=" test " />
</property>
</bean>
在页面中出现"/Login" 请求时会经过Struts-config里的配置转到此配置文件,当有相同的请求配置时调用对应的Action。
把业务类注入Action类,在Action类中只需获得注入就可以调用其中的方法。
然后经由Action读取Struts-config配置里的Forward或Mapping等方法返回页面。
(5)具体实现流程见DEMO演示
6. Struts+Hibernate+Spring的实现
新建一个WEB项目,并加载STRUTS框架,在此不做描述
按方法5加载Spring
按方法4加载Hibernate,在第(3)步会出现
选择第2项则会把Hibernate配置自动写入Spring配置文件中,无须生成单独的Hibernate配置文件
第(4)步选择将Hibernate写入已有的Spring配置文件或是新建Spring配置文件,这里选择已有的写入
SessionFactory ID是定义<bean id=“…“的名称,内容是生成SessionFactory(取代Hibernate自动生成的SessionFactory类)
第(5)步定义DataSource的id和内容
然后流程回到方法4的第5步,但这次不生成SessionFactory类
完成后Hibernate和Spring加载完毕
添加表的映射见方法4
(1)对一些配置文件的说明
A.datasource和sessionFactory是自动生成,mappingResources下的表配置文件是自动添加的
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=test</value>
</property>
<property name="username">
<value>sa</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="datasource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>Login.hbm.xml</value>
<value>bean/Login.hbm.xml</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
此项为手动添加,声明了事务管理以便于使用Hibernate的各种操作方法
<bean id="login" class="bean.LoginDAO">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
声明了数据库业务处理类,此类必须继承HibernateDaoSupport类,注入了sessionFactory自动获得Session,然后只需调用HibernateDaoSupport类中的静态方法对数据库进行操作即可。
<bean id="userDAO"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="transactionManager"/></property>
<property name="target"><ref local="login"/></property>
<property name="transactionAttributes">
<props>
<prop key="findAll">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
此处声明了事务管理的具体类的具体方法
<property name="target"><ref local="login"/></property>
<prop key="findAll">PROPAGATION_REQUIRED</prop>
这2句声明了对bean.LoginDAO中的findAll方法进行了事务管理,如方法中对数据库的操作出现了错误,则会对整个方法进行回滚操作。
(2)具体实现流程见DEMO演示。