Spring第三天
整体课程安排(3天+2天):
第一天:Spring框架入门、IoC控制反转的配置管理、Spring Web集成、Spring Junit集成。
第二天:Spring AOP面向切面编程、AspectJ的集成配置、JdbcTemplate工具类。
第三天:Spring声明式事务管理、Spring和Struts2、Hibernate的整合
第四天、第五天:综合练习,熟悉SSH整合开发、jQuery Ajax、分页
今天的主要内容:
-
Spring的事务管理机制(3个核心接口对象)
-
声明式事务管理案例-转账(xml-tx、aop、注解@Transactional)
-
SSH的整合(SSH jar包和配置文件的导入、Spring(大管家)整合Hibernate(hibernateTemplate操作数据库)、Struts2整合Spring、延迟加载问题)
课程目标:
-
声明式事务的配置编写(事务的传播行为等几个概念,xml,注解写法)
-
SSH整合(整合原理)
-
Spring的事务管理机制
Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:
-
PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
-
TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
-
TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。
-
PlatformTransactionManager事务管理器
参考:spring-framework-3.2.0.RELEASE/docs/javadoc-api/index.html
该接口提供三个方法:
-
commit:提交事务
-
rollback:回滚事务
-
getTransaction:获取事务状态
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现:
事务 |
说明 |
org.springframework.jdbc.datasource.DataSourceTransactionManager |
使用Spring JDBC或iBatis 进行持久化数据时使用 |
org.springframework.orm.hibernate3.HibernateTransactionManager |
使用Hibernate3.0版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager |
使用JPA进行持久化时使用 |
org.springframework.jdo.JdoTransactionManager |
当持久化机制是Jdo时使用 |
org.springframework.transaction.jta.JtaTransactionManager |
使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
-
DataSourceTransactionManager针对JdbcTemplate、MyBatis 事务控制 ,使用Connection(连接)进行事务控制 :
开启事务 connection.setAutoCommit(false);
提交事务 connection.commit();
回滚事务 connection.rollback();
-
HibernateTransactionManager针对Hibernate框架进行事务管理, 使用Session的Transaction相关操作进行事务控制 :
开启事务 session.beginTransaction();
提交事务 session.getTransaction().commit();
回滚事务 session.getTransaction().rollback();
事务管理器的选择? 用户根据选择和使用的持久层技术,来选择对应的事务管理器。 |
用来定义事务相关的属性的,给事务管理器用。
参考:spring-framework-3.2.0.RELEASE/docs/javadoc-api/index.html
该接口主要提供的方法:
-
getIsolationLevel:隔离级别获取
-
getPropagationBehavior:传播行为获取
-
getTimeout:获取超时时间(事务的有效期)
-
isReadOnly 是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写),事务管理器能够根据这个返回值进行优化。
这些事务的定义信息,都可以在配置文件中配置和定制。
-
事务的隔离级别IsolationLevel
隔离级别 |
含义 |
DEFAULT |
使用后端数据库默认的隔离级别(spring中的的选择项) |
READ_UNCOMMITED |
允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITTED |
允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 |
REPEATABLE_READ |
对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE |
完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 |
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,"可重复读"在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
事务四大特性 ACID ---隔离性引发问题 ---- 解决事务的隔离问题 隔离级别
Mysql 默认隔离级别 REPEATABLE_READ
Oracle 默认隔离级别 READ_COMMITTED
多学一招:什么是ACID原则:
参考:
百度百科http://baike.baidu.com/link?url=EPDxwsYxObXZAmy1WEEhuj1_Hr3b3UMQi4SGYOPVcWcvPMzFteL2MRU39khXZgHB9NLzpqpRQoiI6OFCS5WkeJ2nyK-ozmNcXb-aRocRMYa
-
事务的传播行为PropagationBehavior
什么是事务的传播行为? 有什么作用?
事务传播行为用于解决两个被事务管理的方法互相调用问题
业务层两个方法面临的事务问题:
* 有些时候需要处于同一个事务(删除用户删除完成之后,需要同时删除用户对应的订单,需要事务回滚,例如商场工作人员删除订单业务),
* 有些时候不能在同一个事务(取款是一个事务操作,打印凭条是一个事务操作,例如ATM取款业务) !
//开启事务一
//事务一
//提交事务一
//开启事务2
//事务2
//提交事务2
//开启事务
//事务一
//事务2
//提交事务
事务的传播行为的7种类型:
事务传播行为类型 |
说明 |
PROPAGATION_REQUIRED |
支持当前事务,如果不存在 就新建一个 |
PROPAGATION_SUPPORTS |
支持当前事务,如果不存在,就不使用事务 |
PROPAGATION_MANDATORY |
支持当前事务,如果不存在,抛出异常 |
PROPAGATION_REQUIRES_NEW |
如果有事务存在,挂起当前事务,创建一个新的事务 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式运行,如果有事务存在,挂起当前事务 |
PROPAGATION_NEVER |
以非事务方式运行,如果有事务存在,抛出异常 |
PROPAGATION_NESTED |
如果当前事务存在,则嵌套事务执行 只对DataSourceTransactionManager 起效 |
主要分为三大类:
事务默认传播行为 REQUIRED。最常用的。
不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。 常用的事务传播行为:PROPAGATION_REQUIRES_NEW
嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点 |
附录:嵌套事务的示例:
Connection conn = null;
try {
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
1.stmt.executeUpdate("update person set name='888' where id=1");
2.Savepoint savepoint = conn.setSavepoint();
try{
3.conn.createStatement().executeUpdate("update person set name='222' where sid=2");
}catch(Exception ex){
conn.rollback(savepoint);
}
stmt.executeUpdate("delete from person where id=9");
conn.commit();
stmt.close();
} catch (Exception e) {
conn.rollback();
}finally{
try {
if(null!=conn && !conn.isClosed()) conn.close();
} catch (SQLException e) { e.printStackTrace(); }
}
}
【面试题】REQUIRED、REQUIRES_NEW、NESTED 区分
REQUIRED:只有一个事务(默认,推荐)
REQUIRES_NEW:存在两个事务 ,如果事务存在,挂起事务,重新又开启了一个新的事务
NESTED 嵌套事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚
事务运行过程中,每个时间点 事务状态信息 !
flush(),给hibernate使用,底层发出sql的
hasSavepoint():判断是否有保留点
isCompleted():判断事务是否结束
isNewTransaction():判断当前事务是否是新开的一个事务。
isRollbackOnly():判断事务是否只能回滚
setRollbackOnly():设置事务是否回滚
事务的结束:必须通过commit 确认事务提交, rollback 作用标记为回滚。
数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出了commit。
try {
操作
} catch (){
rollback
} finally {
commit
}
【三个事务超级接口对象之间的关系】
1)首先用户管理事务,需要先配置TransactionDefinition(事务定义信息、事务的管理方案);
2)然后根据TransactionDefinition,通过TransactionManager(事务管理器)进行事务管理;
3)最后事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。
-
Spring事务管理两种方式
Spring 支持两种方式事务管理
-
一:编程式的事务管理
在实际应用中很少使用,原因是要修改原来的代码,加入事务管理代码 (侵入性 )
参考文档:http://www.yiibai.com/spring/programmatic_management.html
-
二:使用XML或注解配置声明式事务
* Spring的声明式事务是通过AOP实现的(环绕通知)
* 开发中经常使用(代码侵入性最小)--推荐使用!
-
声明式事务管理案例-转账(xml、注解)
-
编写转账案例,引出事务管理问题
-
需求:账号转账,Tom账号取出1000元,存放到Jack账号上
数据表和测试数据准备:
建表脚本(MySQL):
第一步:创建表t_account CREATE TABLE `t_account` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) NOT NULL, `money` DOUBLE DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 第二步:插入测试数据: INSERT INTO `t_account` VALUES (1, 'Tom', 1000); INSERT INTO `t_account` VALUES (2, 'Jack', 1100); INSERT INTO `t_account` VALUES (3, 'Rose', 1200); 第三步:查看测试数据: |
第一步:新建web工程,spring3_day03_transaction
第二步:导入jar(核心4+2,数据库驱动,c3p0连接池、测试包,jdbc和事务2个)和applicationContext.xml配置文件和log4j.properties文件和db.properties文件:
建包: cn.itcast.spring.dao
第三步:创建IAccountDao接口
public interface IAccountDao {
//(存入)转入
public void in(String name,Double money);
//(取出)转出
public void out(String name,Double money);
}
创建AccounDaoImpl实现类,实现了IAccountDao接口
//账户操作持久层
//技术方案:jdbctempate
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
//(存入)转入
public void in(String name,Double money){
String sql="update t_account set money = money+ ? where name = ?";
super.getJdbcTemplate().update(sql, money,name);
}
//(取出)转出
public void out(String name,Double money){
String sql="update t_account set money = money- ? where name = ?";
super.getJdbcTemplate().update(sql, money,name);
}
}
第四步:建立service层,创建IAccountService接口,编写转账的业务代码:
建包: cn.itcast.spring.service
public interface IAccountService {
void transfer(String outName,String inName,Double money);
}
创建AccountServiceImpl实现类,实现了IAccountService接口,编写转账的业务操作
//掌握操作的业务层
public class AccountServiceImpl implements IAccountService{
//注入dao
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
//转账操作的业务逻辑
public void transfer(String outName,String inName,Double money){
//调用dao层
//先取出
accountDao.out(outName, money);
//再转入
accountDao.in(inName, money);
}
}
第五步:使用SpringTest进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class SpringTest {
//注入测试的service
@Autowired
private IAccountService accountService;
//需求:账号转账,Tom账号取出1000元,存放到Jack账号上
@Test
public void testTransfer(){
accountService.transfer("Tom", "Jack", 1000d);
System.out.println("转账成功!");
}
}
但是发现问题:
事务管理问题:在Service层没有事务的情况下,如果出现异常,则会转账不成功,数据异常。
扩展:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。
-
XML配置方式添加事务管理(tx、aop元素)
【操作思路】:
1、 确定目标:需要对AccountService 的 transfer方法,配置切入点
2、 需要Advice (环绕通知),方法前开启事务,方法后提交关闭事务
3、 配置切面和切入点
第一步:导入aop相关的包(4个),引入约束名称空间(aop和tx 的名称空间)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
配置本地提示:
配置Advice通知:
Spring为简化事务的配置,提供了<tx:advice>来配置事务管理,也可以理解为该标签是spring为你实现好了的事务的通知增强方案。
第一步:导入jar包:
其中:
com.springsource.org.aopalliance-1.0.0.jar:aop切面编程
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:注解开发切面
spring-aop-3.2.0.RELEASE.jar:aop切面编程
spring-aspects-3.2.0.RELEASE.jar:注解开发切面
spring-tx-3.2.0.RELEASE.jar:事务处理
第二步:配置spring容器,applicationContext.xml文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入外部属性配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 -->
<!-- c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.className}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 第一步:定义具体的平台事务管理器(DataSource事务管理器) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 第二步:定义通知,通知中要处理的就是事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的属性定义 -->
<tx:attributes>
<!-- 配置具体的方法的事务属性
isolation//事务的隔离级别,默认是按数据库的隔离级别来
propagation//事务的传播行为,默认是同一个事务
timeout="-1":事务的超时时间,默认值使用数据库的超时时间。
read-only="false":事务是否只读,默认可读写。
rollback-for:遇到哪些异常就回滚,其他的都不回滚
no-rollback-for:遇到哪些异常不回滚,其他的都回滚。和上面互斥的
-->
<tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
<!-- 支持通配符
要求service中 方法名字必须符合下面的规则
-->
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 第三步:配置切入点,让通知关联切入点,即事务控制业务层的方法 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="bean(*Service)" id="txPointcut"/>
<!-- 切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- dao -->
<bean id="accountDao" class="cn.itcast.spring.dao.AccountDaoImpl">
<!-- 注入数据源,才拥有jdbctemplate -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 业务层 -->
<bean id="accountService" class="cn.itcast.spring.service.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
使用SpringTest.java测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class SpringTest {
//注入测试的service
@Autowired
private IAccountService accountService;
//需求:账号转账,Tom账号取出1000元,存放到Jack账号上
@Test
public void testTransfer(){
accountService.transfer("Tom", "Jack", 1000d);
System.out.println("转账成功!");
}
}
数据正常!
【声明式事务处理的原理图】
【注意】
如果不配置,则走默认的事务(默认事务是每个数据库操作都是一个事务,相当于没事务),所以我们开发时需要配置事务。
【补充了解】:
rollback-for属性:
参考:spring3_day1_课前资料/Spring2.5-中文参考手册.chm
注意事项:声明式事务处理对运行时异常有效,任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
测试:
applicationContext.xml:
遇到该异常事务不会回滚:
-
注解配置方式添加事务管理 @Transactional
步骤:
1.在需要管理事务的方法或者类上面 添加@Transactional 注解
2.配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)
第一步:确定目标(bean的方法):
创建包cn.itcast.spring.anntx
在cn.itcast.spring.anntx.dao中创建IAccountDao和AccountDaoImpl类
-
IAccountDao.java接口
public interface IAccountDao {
//(存入)转入
public void in(String name,Double money);
//(取出)转出
public void out(String name,Double money);
}
(2)AccountDaoImpl.java类
//账户操作持久层
//技术方案:jdbctempate
/**
* @Repository("accountDao")
* 相当于容易中定义<bean id="accountDao" class="cn.itcast.spring.anntx.dao.AccountDaoImpl"/>
*/
@Repository("accountDao")
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
//注入数据源
////@Autowired
//private DataSource dataSource;//没有注入数据源成功~
////原理:放到属性上的的注解相当于,自动生成setter方法上加注解
//@Autowired //自动到spring的容器中寻找类型是参数类型(DataSource)的bean
//public void setDataSource(DataSource dataSource){
// this.dataSource=dataSource;
//}
@Autowired//当初始化dao的时候,会调用该方法啊,通过set方法的形参注入数据源
//方法名无所谓
public void setSuperDataSource(DataSource dataSource){
//调用父类的方法
super.setDataSource(dataSource);
}
//(存入)转入
public void in(String name,Double money){
String sql="update t_account set money = money+ ? where name = ?";
super.getJdbcTemplate().update(sql, money,name);
}
//(取出)转出
public void out(String name,Double money){
String sql="update t_account set money = money- ? where name = ?";
super.getJdbcTemplate().update(sql, money,name);
}
}
在cn.itcast.spring.anntx.service中创建IAccountService和AccountServiceImpl类
-
IAccountService接口
public interface IAccountService {
void transfer(String outName,String inName,Double money);
}
-
AccountServiceImpl类
//掌握操作的业务层
/**
* @Service("accountService")
* 相当于spring容器中定义:<bean id="accountService" class="cn.itcast.spring.anntx.service.AccountServiceImpl">
*/
@Service("accountService")
@Transactional//会对该类中,所有的共有的方法,自动加上事务--全局的设置,默认是可写
public class AccountServiceImpl implements IAccountService{
//注入dao
@Autowired
private IAccountDao accountDao;
//转账操作的业务逻辑
// @Transactional//在方法上添加事务
public void transfer(String outName,String inName,Double money){
//调用dao层
//先取出
accountDao.out(outName, money);
int d = 1/0;
//再转入
accountDao.in(inName, money);
}
@Transactional(readOnly=true)//使用局部覆盖全局的
public void findAccount(){
System.out.println("查询帐号的信息了");
}
}
第二步:创建applicationContext-tx.xml在applicationContext-tx.xml中配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 配置数据源 -->
<!-- c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.className}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置bean注解扫描 -->
<context:component-scan base-package="cn.itcast.spring.anntx"/>
<!-- 定义具体的平台事务管理器(DataSource事务管理器) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务注解驱动 :识别事务的注解@tr。。。
transaction-manager:具体的平台事务管理器
-->
<!-- <tx:annotation-driven transaction-manager="transactionManager"/> -->
<!-- 默认的平台事务管理器的名字叫:transactionManager,此时transaction-manager="transactionManager"可以不写 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
【注意】:数据源的注解注入 需要自己添加set方法
(1)在需要管理事务的方法或者类上面 添加@Transactional 注解
(2)配置事务的定义属性信息,在注解中直接配置:
【扩展1】
如果 @Transactional 标注在 Class 上面, 那么将会对这个 Class 里面所有的 public 方法都包装事务方法。等同于该类的每个公有方法都放上了@Transactional。
如果某方法需要单独的事务定义,则需要在方法上加@Transactional来覆盖类上的标注声明。记住:方法级别的事务覆盖类级别的事务
//掌握操作的业务层
/**
* @Service("accountService")
* 相当于spring容器中定义:<bean id="accountService" class="cn.itcast.spring.anntx.service.AccountServiceImpl">
*/
@Service("accountService")
@Transactional()//会对该类中,所有的共有的方法,自动加上事务--全局的设置,默认是可写
public class AccountServiceImpl implements IAccountService{
//注入dao
@Autowired
private IAccountDao accountDao;
//转账操作的业务逻辑
@Transactional(readOnly=false)//在方法上添加事务
public void transfer(String outName,String inName,Double money){
//调用dao层
//先取出
accountDao.out(outName, money);
int d = 1/0;
//再转入
accountDao.in(inName, money);
}
@Transactional(readOnly=true)//使用局部覆盖全局的
public void findAccount(){
System.out.println("查询帐号的信息了");
}
}
-
小结-xml和注解的选择
XML配置方式和注解配置方式 进行事务管理 哪种用的多?
XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。
使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护
注意:以后的service的方法名字的命名,必须是上面规则,否则,不能被spring事务管理。!!!!
即以save开头的方法,update开头的方法,delete开头的方法,表示增删改的操作,故事务为可写
以find开头的方法,表示查询,故事务为只读
(1)xml方式小结
(2)注解方式小结
-
SSH的整合(Spring整合Hibernate、Struts2整合Spring、延迟加载问题)xml方式
目标:体现spring的特性:粘合剂的作用(整合Hibernate,整合Struts2)。能让spring管理的都让其管理。
新建 web工程 spring3_day03_ssh
-
SSH jar包和配置文件的导入
-
struts2框架(15个)
学习版本: 2.3.15.3
第一步:导入Jar包
-
-
最基本jar包 apps/struts2-blank.war (13个 )
-
lib下插件扩展包
struts2-json-plugin-2.3.15.3.jar 进行Ajax开发插件包
struts2-convention-plugin-2.3.15.3.jar 约定扫描插件 (注解开发,xml开发的时候一定不要拷贝 )
struts2-spring-plugin-2.3.15.3.jar 整合spring插件包 (1个)
第二步:web.xml配置文件
-
web.xml 配置核心Filter
<!-- struts2的前端控制器,这是struts2运行的核心 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步:在src下创建struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 开发者模式:-->
<constant name="struts.devMode" value="true" />
<!-- 主题样式 -->
<constant name="struts.ui.theme" value="simple"/>
<package name="default" namespace="/" extends="struts-default">
<!-- 图书管理 -->
<!-- action还是struts自己管理,自动装配bean的机制 -->
<action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">
</action>
</package>
</struts>
-
第四步:在src下,创建log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=info, stdout,file
-
spring3框架 (13)
学习版本:spring3.2.x
第一步:导入Jar包
Spring3.2 开发最基本jar包
-
spring-beans-3.2.0.RELEASE.jar
-
spring-context-3.2.0.RELEASE.jar
-
spring-core-3.2.0.RELEASE.jar
-
spring-expression-3.2.0.RELEASE.jar
-
com.springsource.org.apache.commons.logging-1.1.1.jar
-
com.springsource.org.apache.log4j-1.2.15.jar
AOP开发
-
spring-aop-3.2.0.RELEASE.jar
-
spring-aspects-3.2.0.RELEASE.jar
-
com.springsource.org.aopalliance-1.0.0.jar
-
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
Spring Jdbc开发
-
spring-jdbc-3.2.0.RELEASE.jar
Spring事务管理
-
spring-tx-3.2.0.RELEASE.jar
Spring整合其他ORM框架
-
spring-orm-3.2.0.RELEASE.jar
Spring在web中使用
-
spring-web-3.2.0.RELEASE.jar
Spring整合Junit测试
-
spring-test-3.2.0.RELEASE.jar
总结:
-
Spring的lib目录 需要导入jar包 (11个 )
基本4个
Aop 2个
Jdbc事务 2个
Test 1个
Web1 个
整合ORM框架 1个 (整合hibernate :spring-orm.jar )
-
日志 commons-logging + log4j (注意:已经在struts2 导入过)
-
AOP第三方 (2个)
第二步:定义web.xml配置文件
-
web.xml 核心监听器,初始化容器
<!-- spring的核心监听器:初始化spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring的核心监听器:定义spring容器的位置,在src下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
第三步:在src下applicationContext.xml ,引入bean、context、aop、tx的约束
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
-
hibernate3框架 (10)
学习版本 : hibernate3.6.10.final
第一步:Jar包
解压根目录 hibernate3.jar
解压根目录lib equired*.jar
解压根目录libjpahibernate-jpa-2.0-api-1.0.1.Final.jar
数据库连接池c3p0
解压根目录liboptionalc3p0c3p0-0.9.1.jar
整合log4j :slf4j-log4j12-1.7.2.jar 、log4j-1.2.16.jar
使用二级缓存 Ehcache
数据库驱动 mysql-connector-java-5.0.8-bin.jar
总结:
-
hibernate3.jar 核心包 (1个)
-
lib/required 必须的 (5个 javassist 已经导入 )
-
日志 slf4j 整合 log4j (1个)
-
lib/jpa (1个)
-
lib/optional/c3p0 连接池 (1个)
-
数据库驱动 mysql(1个)
注意:多个框架整合时的jar冲突问题。
可以使用:,如果发现出现冲突,默认是用高版本的。
第二步:配置文件 ,在src下 hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- JDBC基本连接参数 -->
<session-factory>
<!-- conntion配置-mysql -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///itcastspring</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 配置方言-mysql -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 常见其它配置 -->
<property name="hibernate.show_sql">true</property> <!-- 控制台上打印SQL -->
<property name="hibernate.format_sql">true</property> <!-- 控制台输出时,对SQL语句格式化 -->
<!-- 测试环境 create/ create-drop 正式环境 update validate -->
<property name="hibernate.hbm2ddl.auto">update</property> <!-- 自动建表 -->
<!-- 引入映射配置 -->
<mapping resource="cn/itcast/ssh/domain/Book.hbm.xml"/>
</session-factory>
</hibernate-configuration>
-
Spring和Hibernate整合
-
创建数据表,编写实体映射
第一步:数据库脚本
用户名和密码都是root
第二步:建立cn.itcast.ssh.domain包,用来存放实体类Book.java和实体类的映射文件Book.hbm.xml:
(1)实体类Book.java
//po实体类
public class Book {
private Integer id;//oid
private String name;
private Double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String toString() {
return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
(2)编写实体类的映射文件Book.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.ssh.domain.Book" table="book">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="price"/>
</class>
</hibernate-mapping>
第三步:配置hibernate.cfg.xml 引入映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- JDBC基本连接参数 -->
<session-factory>
<!-- 理解为连接池 -->
<!-- conntion配置-mysql -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///itcastspring</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 配置方言-mysql -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 常见其它配置 -->
<property name="hibernate.show_sql">true</property> <!-- 控制台上打印SQL -->
<property name="hibernate.format_sql">true</property> <!-- 控制台输出时,对SQL语句格式化 -->
<!-- 测试环境 create/ create-drop 正式环境 update validate -->
<property name="hibernate.hbm2ddl.auto">update</property> <!-- 自动建表 -->
<!-- 引入映射配置 -->
<mapping resource="cn/itcast/ssh/domain/Book.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第四步:使用Junit测试连接数据库:
创建包cn.itcast.ssh.test,创建类HibernateTest.java进行测试
public class HibernateTest {
@Test
public void test(){
Configuration configuration = new Configuration();
//加载src下的hibernate.cfg.xml
configuration.configure();
//创建SessionFactory工厂
SessionFactory sf = configuration.buildSessionFactory();
//打开Session
Session s = sf.openSession();
//开启事务
Transaction tr = s.beginTransaction();
Book book = new Book();
book.setName("降龙十八掌");
book.setPrice(122d);
s.save(book);
//事务提交
tr.commit();
//session关闭
s.close();
}
}
以上是使用hibernate.cfg.xml文件的定义连接数据库的,那么能否修改成使用spring连接数据库呢?
大家都知道,Hibernate的核心是SessionFactory,并由SessionFactory创建Session,再由Session操作数据库。
思想:将Hibernate中SessionFactory对象,由Spring管理
通过 spring-orm包 提供 LocalSessionFactoryBean 实现
方式一: 直接在spring中加载hibernate配置文件 (零障碍整合)
<!-- spring整合Hibernate:将session工厂交给spring管理,这是spring整合hibernate的核心 -->
<!-- 方式一: 直接在spring中加载hibernate配置文件 (零障碍整合) -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 注入Hibernate的核心配置文件
提示技巧:xxxLocation,一般都要加classpath -->
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
</bean>
测试:验证整合是否成功:删除book表,将项目部署tomcat,启动服务器,查看是否建表
原理:Web.xml 配置监听器 ---> 初始化spring容器 ---> 初始化hibernate配置--->初始化所有单例对象 ---> sessionFactory 建表
容器之间的关系原理:
方式二: (推荐)将hibernate参数配置到spring文件
没有hibernate.cfg.xml配置文件 !!!完全由spring进行管理
经过分析: 需要三个方面的配置 (数据源配置、 常用属性配置、 hbm映射加载 )
-
数据源配置(在applicationContext.xml中配置)
<!-- 引入外部属性配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
-
Hibernate常用属性配置
和
-
Hbm映射引入加载配置
<!-- 方式二: (推荐)将hibernate参数配置到spring文件 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 1.数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2.Hibernate的属性 -->
<property name="hibernateProperties">
<props>
<!-- 设置方言 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<!-- 设置打印sql语句 -->
<prop key="hibernate.show_sql">true</prop>
<!-- 格式化sql语句 -->
<prop key="hibernate.format_sql">true</prop>
<!-- 自动建表 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 3.mapping映射 -->
<property name="mappingResources">
<list>
<value>cn/itcast/ssh/domain/Book.hbm.xml</value>
</list>
</property>
</bean>
测试:验证整合是否成功:删除book表,将项目部署tomcat,启动服务器,查看是否建表
-
编写Dao类 (数据层)
传统写法: 通过Hibernate的Session ,实现数据表操作
Spring 整合 Hibernate,提供 HibernateTemplate 简化原始Hibernate代码 (不用关心session,你只需要用就行了)
附录:
HibernateTemplate常用的API
-
void save(Object entity)
-
void update(Object entity)
-
void delete(Object entity)
-
<T> T get(Class<T> entityClass, Serializable id)
-
<T> T load(Class<T> entityClass, Serializable id)
-
List find(String queryString, Object... values)
-
List findByCriteria(DetachedCriteria criteria)
-
List findByNamedQuery(String queryName, Object... values)
新建包:cn.itcast.ssh.dao,用来编写dao:
第一步:创建IBookDao接口
public interface IBookDao {
//保存图书
public void save(Book book);
//更新图书(根据id)
public void update(Book book);
//删除图书(根据id)
public void delete(Book book);
//根据id查询
public Book findById(Integer id);
//查询所有
public List<Book> findAll();
//复杂条件条件
//1.命名查询:QBN
public List<Book> findByNamedQuery(String queryName, Object... values);
//2.离线条件查询:QBC
public List<Book> findByCriteria(DetachedCriteria criteria);
}
第二步:创建BookDaoImpl的实现类
使用HibernateTemplate 实现增删改查
//图书的持久层
//HibernateDaoSupport用来简化代码,能提供HibernateTemplate,
public class BookDaoImpl extends HibernateDaoSupport implements IBookDao{
//保存图书
public void save(Book book){
//注入sesson工厂,获取session--不会写了
//因为:spring提供了模版类,来整合Hibernate
super.getHibernateTemplate().save(book);
}
//更新图书(根据id)
public void update(Book book){
super.getHibernateTemplate().update(book);
}
//删除图书(根据id)
public void delete(Book book){
super.getHibernateTemplate().delete(book);
}
//根据id查询
public Book findById(Integer id){
return super.getHibernateTemplate().get(Book.class, id);
}
//查询所有
public List<Book> findAll(){
return super.getHibernateTemplate().loadAll(Book.class);
// return super.getHibernateTemplate().find("from Book");//hql方式
}
//复杂条件条件
//1.命名查询:QBN
public List<Book> findByNamedQuery(String queryName, Object... values){
return super.getHibernateTemplate().findByNamedQuery(queryName, values);
}
//2.离线条件查询:QBC
public List<Book> findByCriteria(DetachedCriteria criteria){
return super.getHibernateTemplate().findByCriteria(criteria);
}
}
查看HibernateDaoSupport类的底层,我们发现,要想创建、使用HibernateTemplate,必须要注入SessionFactory
那么如何注入SessionFactory呢?
第三步:配置applicationContext.xml
<!-- dao -->
<bean id="bookDao" class="cn.itcast.ssh.dao.BookDaoImpl">
<!-- 注入sessionFactory,这是在Dao层使用hibernateTemplate的条件,用来操作数据库的crud -->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
-
编写Service (业务层)
新建包cn.itcast.ssh.service:用来存放service
业务层代码,将dao注入到service,可以使用setXxx方法
第一步:创建接口类IBookService.java
public interface IBookService {
//保存图书
//方法名不能随便写,配置事务save*,事务才能可写
public void saveBook(Book book);
//查询:复杂条件查询,根据书名模糊查询,配置事务find*,表示查询,事务只读
public List<Book> findBookListByNameLike(String name);
}
第二步:创建接口类的实现类BookServiceImpl实现接口类
//图书业务层
public class BookServiceImpl implements IBookService {
//注入dao层
private IBookDao bookDao;
public void setBookDao(IBookDao bookDao) {
this.bookDao = bookDao;
}
//保存图书
//方法名不能随便写,配置事务save*
public void saveBook(Book book){
//调用dao层
bookDao.save(book);
}
//查询:复杂条件查询,根据书名模糊查询
public List<Book> findBookListByNameLike(String name){
//1.qbn
// return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");
//2.qbc
DetachedCriteria criteria = DetachedCriteria.forClass(Book.class);//root对象类型
criteria.add(Restrictions.like("name", "%"+name+"%"));
return bookDao.findByCriteria(criteria);
}
}
第三步:Book.hbm.xml文件,定义命名查询:使用qbn的方式查询的时候使用
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.ssh.domain.Book" table="book">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="price"/>
</class>
<!--定义命名查询-->
<query name="Book.findBookListByNameLike">
from Book where name like ?
</query>
</hibernate-mapping>
第四步:配置applicationContext.xml
<!--service -->
<bean id="bookService" class="cn.itcast.ssh.service.BookServiceImpl">
<!-- 注入dao -->
<property name="bookDao" ref="bookDao"/>
</bean>
第五步:声明式事务管理 ,在Service层完成
<!-- 配置声明式事务处理 -->
<!-- 平台事务管理器的实现 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessin工厂 -->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 具体事务的属性策略 -->
<tx:attributes>
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 事务的切入点和切面配置 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="bean(*Service)" id="txPointcut"/>
<!-- 切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
第六步:使用SrpingTest.java进行测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {
//注入service
@Autowired
private IBookService bookService;
@Test
public void testSave(){
//保存图书
Book book = new Book();
//book.setId(id)
book.setName("约会专家周星星");
book.setPrice(998d);
bookService.saveBook(book);
}
@Test
public void testFind(){
//查询
List<Book> list = bookService.findBookListByNameLike("会");
System.out.println(list);
}
}
-
编写Action (表现层)
-
编写添加图书页面addbook.jsp访问到Action
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>添加图书</title>
</head>
<body>
<h1>欢迎使用Xxx的图书管理系统</h1>
<s:form action="book_add" namespace="/" method="post">
图书的名称:<s:textfield name="name"/><br/>
图书的价格:<s:textfield name="price"/><br/>
<s:submit value="保存"/><s:reset value="重置"/>
</s:form>
</body>
</html>
第二步:新建cn.itcast.web.action包,存放action,编写BookAction,配置add方法完成添加
//图书管理的表现层
public class BookAction extends ActionSupport implements ModelDriven<Book>{
//数据模型对象
private Book book=new Book();
public Book getModel() {
return book;
}
//成员变量
private IBookService bookService;
//激活了自动注入装配spring的bean的机制功能
//在实例化action之后,自动到spring容器中,寻找xxx的名字的bean,调用setXxx
public void setBookService(BookServiceImpl bookService){
this.bookService = bookService;
}
//业务方法:保存图书
public String add(){
System.out.println(book);
//调用业务层保存...
bookService.saveBook(book);
return NONE;
}
}
第三步:配置struts.xml
<package name="default" namespace="/" extends="struts-default">
<!-- 图书管理 -->
<!-- action还是struts自己管理,自动装配bean的机制 -->
<action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">
</action>
</package>
第四步:测试,访问http://localhost:8080/spring3_day03_ssh1/addbook.jsp
发现空指针异常。因为Action类并没有注入Service,Action类的bookService为空。
-
Struts2整合Spring
思想: 在Action类注入Service
方式一: Action自动装配Service --(机制:struts整合了spring)
1) 引入struts2-spring-plugin.jar 插件包
阅读 插件包根目录 struts-plugin.xml
打开struts-plugin.xml文件
修改struts的对象工厂为 spring (StrutsSpringObjectFactory),开启struts2整合spring
2) struts2-core-2.3.15.3.jar 中default.properties,默认struts2整合spring是注释的,要想使用struts2整合spring需要开启。
如果对象工厂变为spring之后, autoWire(自动绑定)机制被激活,默认按名称自动注入 bean,即Acton由struts2的一个工厂来生成,并可以自动根据名称来注入spring的bean.(默认,也可以更改为按照类型等。)
简单的再说:你只要更改了创建action对象的工厂是spring(StrutsSpringObjectFactory),那么你的action就可以自动根据名字(setter方法的名字)拿到spring中的bean对象。
3) Action提供 setXxxService 的方法 ,只要名称和spring中Service匹配,Service就可以自动装配到Action中
例如:在cn.itcast.ssh.web.action中编辑BookAction.java
//图书管理的表现层
public class BookAction extends ActionSupport implements ModelDriven<Book>{
//数据模型对象
private Book book=new Book();
public Book getModel() {
return book;
}
//成员变量
private IBookService bookService;
//激活了自动注入装配spring的bean的机制功能
//在实例化action之后,自动到spring容器中,寻找xxx的名字的bean,调用setXxx
public void setBookService(IBookService bookService){
this.bookService = bookService;
}
//业务方法:保存图书
public String add(){
System.out.println(book);
//调用业务层保存...
bookService.saveBook(book);
return NONE;
}
}
最后,使用addbook.jsp进行测试
方式二: Action交给Spring容器管理 (推荐 )
方式一的缺点:Action对象由struts2框架创建, Service根据名称自动装配Action (缺点: Action不能被Spring管理 , 无法对Action使用AOP、bean注入 )
1) 导入struts2-spring-plugin.jar 插件
对象工厂被修改成spring
原理:当struts2容器去创建Action对象时, 会根据Action中配置的class(伪类名)名字,优先获取spring容器寻找同名的bean ,找不到再自己构建。
-
spring配置中配置伪类对应Bean,配置applicationContext.xml
<!-- action
scope="prototype":必须是多例,否则会出现线程问题,因为struts2是多线程多实例
-->
<bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">
<!-- 注入dao -->
<property name="bookService" ref="bookService"/>
</bean>
-
struts2 配置Action的class 是一个伪类
<package name="default" namespace="/" extends="struts-default">
<!-- 图书管理 -->
<!-- action还是struts自己管理,自动装配bean的机制 -->
<!-- struts2全权交给spring装配管理
当StrutsSpringObjectFactory的来创建action,struts2会拿class的名字在spring容器中找,同名的bean,如果有,则直接拿过来用,此时不需要在创建。
如果没有,则自己想办法new
即:
class="bookAction",使用bookAction到spring的容器applicationContext.xml中查找对象
<bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">
<property name="bookService" ref="bookService"/>
</bean>
-->
<action name="book_*" class="bookAction" method="{1}">
</action>
</package>
【注意】
面试问题:Struts2管理Action 和 Spring 管理Action 区别?
(1)Struts2 管理Action,由struts来构建action, 默认多实例
<package name="default" namespace="/" extends="struts-default">
<!-- 图书管理 -->
<!-- action还是struts自己管理,自动装配bean的机制 -->
<!-- struts2全权交给spring装配管理
当StrutsSpringObjectFactory的来创建action,struts2会拿class的名字在spring容器中找,同名的bean,如果有,则直接拿过来用,此时不需要在创建。
如果没有,则自己想办法new
即:
class="bookAction",使用bookAction到spring的容器applicationContext.xml中查找对象
<bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">
<property name="bookService" ref="bookService"/>
</bean>
-->
<action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">
</action>
</package>
(2)Spring 管理Action,action由spring来创建,Action 默认singleton单例 (引发问题 ),所以需要配置 scope="prototype"
<!-- action
scope="prototype":必须是多例,否则会出现线程问题,因为struts2是多线程多实例
-->
<bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">
<!-- 注入dao -->
<property name="bookService" ref="bookService"/>
</bean>
(3)都需要导入struts2-spring-plugin.jar
ssh整合小结:
-
Spring和hibernate 通过管理SessionFactory (LocalSessionFactoryBean)
* 方式一:加载hibernate自己的配置文件,使用hibernate.cfg.xml
* 方式二:将所有参数配置到spring,不需要hibernate配置文件,省略掉hibernate.cfg.xml ,hibernateTemplate的使用(dao继承hibernateDaoSupport)。
-
Spring和Struts2 的整合:通过修改对象工厂,放入struts2-spring-plugin.jar 插件包
* 方式一: struts.xml配置Action全名,struts2管理Action对象,自动装配Service对象,
* 方式二: struts.xml配置伪类名, 对应spring中Action Bean, spring管理Action, 配置注入Service
-
延迟加载问题 OpenSessionInView方案
什么是延迟加载问题 ?
业务层查询数据,返回后,session关闭了, 表现层获取数据如果关联延迟数据,无法初始化 ! (No Session 延迟加载问题 )
问题: 如何解决延迟加载问题 ?
方案一: 在Xxx.hbm.xml中配置为立即加载 lazy=false (不推荐 )
方案二: Service方法返回前, 对延迟数据进行初始化 (缺点多写代码 )
List<Employee> list = dao.findAll ();
for(Employee e : list ){
Hibernate.initialize(e.getDepartment() );
}
方案三: spring提供了OpenSessionInView 机制 (将Session开启到表现层 最前面 Filter )
Spring 提供 OpenSessionInViewFilter [注意:需要配置在struts2 Filter前面,否则不起作用 ]
web.xml
<!-- OpenSessionInView机制:会将会话到表现层,让会话在请求结束之后关闭,延迟了session关闭,需要放置到struts2的过滤器的前面 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
OpenSessionInViewFilter:在request过程中维持session。延迟session的关闭,直到request结束,再自动关闭session,也就是说,直到表现层的数据全部加载完毕,再关闭Session。
注意(副作用): 如果没有被事务管理的方法, OpenSessionInViewFilter 会将这些方法的事务变为 readOnly 的 !
例如:addBook方法没有添加事务,按道理它是可写的方法,但是这里只会readOnly只读的。
懒加载异常:
解决方案:必须将事务拦截到的方法,都配置事务的属性,如果不配置会默认成readOnly只读操作。
-
SSH整合(注解方式)
-
导入jar包和配置
-
第一步:新建 web工程 spring3_day03_sshannotation
-
Jar包 导入 SSH整合 37个jar包 + struts2-convention-plugin.jar (约定扫描)共38个
-
配置文件
src下log4j.properties 日志配置
src下创建db.properties文件,配置数据库的连接
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url jdbc:mysql:///itcastspring
jdbc.username = root
jdbc.password = root
src下 applicationContext.xml 整合Hibernate (配置SessionFactory )
<!-- 引入外部属性配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
-
Hibernate 实体类 映射采用JPA的注解完成
@Entity 实体
@Table 表映射
@Id 主键标识
@GeneratedValue JPA生成策略
@GeneratedGenerator 指定hibernate生成策略
@Column 普通列映射
@OneToMany @ManyToOne @ManyToMany @OneToOne 、@JoinColumn @JoinTable 关系
@Cascade 配置hibernate级联
@Fetch @LazyToOne @LazyCollection 配置抓取策略
@Cache 配置二级缓存
@NamedQueries @NamedQuery 配置命名查询 HQL
@NamedNaviveQueries @NamedNaviteQuery 配置命名查询 SQL
注解配置文档,可以参考:hibernate注解说明文档.pdf
第一步:在cn.itcast.ssh.domain包下创建Book.java对象,添加注解的方式,取代Book.hbm.xml文件
//po实体类
@Entity
@Table(name="book")
@NamedQuery(name="Book.findBookListByNameLike",query="from Book where name like ?")
public class Book {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)//自动
private Integer id;//oid
private String name;
private Double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String toString() {
return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
-
spring整合hibernate配置sessionFactory
第一步:使用AnnotationSessionFactoryBean 配置SessionFactory !
<!-- hibernate的会话工厂 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<!-- 1.数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2.Hibernate的属性 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 3.注解实体扫描 -->
<property name="packagesToScan">
<!-- 扫描某包中的所有@entity注解实体 -->
<list>
<value>cn.itcast.ssh.domain</value>
</list>
</property>
</bean>
第二步:在web.xml 注册struts2 (Filter器) 和 spring (Listener器 )
<!-- struts2的前端控制器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- spring的核心监听器:初始化spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
第三步:测试,将项目部署到tomcat,删除数据表,启动项目如果能自动建表 ---- 整合成功 !
-
编写Action、Service、Dao注解注册<bean>
-
编写BookDao
-
第一步:配置applicationContext.xml,配置组件扫描Bean,开启注解功能
<!-- bean组件扫描配置,开启注解功能,可以在类上使用@Repository,@Service,@Action,@Component -->
<context:component-scan base-package="cn.itcast.ssh"/>
新建包:cn.itcast.ssh.dao,用来编写dao:
第二步:创建IBookDao接口
//保存图书
public void save(Book book);
//更新图书(根据id)
public void update(Book book);
//删除图书(根据id)
public void delete(Book book);
//根据id查询
public Book findById(Integer id);
//查询所有
public List<Book> findAll();
//复杂条件条件
//1.命名查询:QBN
public List<Book> findByNamedQuery(String queryName, Object... values);
//2.离线条件查询:QBC
public List<Book> findByCriteria(DetachedCriteria criteria);
}
第三步:创建BookDaoImpl的实现类
使用HibernateTemplate 实现增删改查,使用注解@Repository和@Autowired
//图书的持久层
//HibernateDaoSupport用来简化代码,能提供HibernateTemplate,
@Repository("bookDao")
public class BookDaoImpl extends HibernateDaoSupport implements IBookDao {
//注入sessionFactory:注意写法,这样才可以使用hibernateTemplate
@Autowired
public void setSuperSessionFactory(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
//保存图书
public void save(Book book){
//注入sesson工厂,获取session--不会写了
//因为:spring提供了模版类,来整合Hibernate
super.getHibernateTemplate().save(book);
}
//更新图书(根据id)
public void update(Book book){
super.getHibernateTemplate().update(book);
}
//删除图书(根据id)
public void delete(Book book){
super.getHibernateTemplate().delete(book);
}
//根据id查询
public Book findById(Integer id){
return super.getHibernateTemplate().get(Book.class, id);
}
//查询所有
public List<Book> findAll(){
return super.getHibernateTemplate().loadAll(Book.class);
// return super.getHibernateTemplate().find("from Book");//hql方式
}
//复杂条件条件
//1.命名查询:QBN
public List<Book> findByNamedQuery(String queryName, Object... values){
return super.getHibernateTemplate().findByNamedQuery(queryName, values);
}
//2.离线条件查询:QBC
public List<Book> findByCriteria(DetachedCriteria criteria){
return super.getHibernateTemplate().findByCriteria(criteria);
}
}
查看HibernateDaoSupport类的底层,我们发现,要想创建、使用HibernateTemplate,必须要注入SessionFactory
那么如何注入SessionFactory呢?使用@Autowired
//注入sessionFactory:注意写法,这样才可以使用hibernateTemplate
@Autowired
public void setSuperSessionFactory(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
-
编写BookService(业务层)
新建包cn.itcast.ssh.service:用来存放service
业务层代码,将dao注入到service,可以使用@Autowired存放到属性上
第一步:创建接口类IBookService.java
public interface IBookService {
//保存图书
//方法名不能随便写,配置事务save*,事务才能可写
public void saveBook(Book book);
//查询:复杂条件查询,根据书名模糊查询,配置事务find*,表示查询,事务只读
public List<Book> findBookListByNameLike(String name);
}
第二步:创建接口类的实现类BookServiceImpl实现接口类
//图书业务层
@Service("bookService")
@Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)
public class BookServiceImpl implements IBookService{
//注入dao层
@Autowired
private IBookDao bookDao;
@Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)
public void saveBook(Book book){
//调用dao层
bookDao.save(book);
}
//查询:复杂条件查询,根据书名模糊查询
public List<Book> findBookListByNameLike(String name){
//1.qbn
// return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");
//2.qbc
DetachedCriteria criteria =DetachedCriteria.forClass(Book.class);//root对象类型
criteria.add(Restrictions.like("name", "%"+name+"%"));
return bookDao.findByCriteria(criteria);
}
}
第三步:声明式事务管理 ,在Service层完成,添加注解@Transactional
-
在applicationContext.xml文件中配置事务控制的注解写法
<!-- 平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory -->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 声明式事务(注解驱动) -->
<tx:annotation-driven transaction-manager="transactionManager"/>
(2)类上定义只读,因为一个类中查询的方法比较多
@Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)
public class BookServiceImpl implements IBookService{
}
(3)增删改方法上定义可写,可写的操作需要将事务定义可写
//保存图书
@Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)
public void saveBook(Book book){
}
第四步:使用SrpingTest.java进行测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {
//注入service
@Autowired
private IBookService bookService;
@Test
public void testSave(){
//保存图书
Book book = new Book();
//book.setId(id)
book.setName("约会专家周星星");
book.setPrice(998d);
bookService.saveBook(book);
}
@Test
public void testFind(){
//查询
List<Book> list = bookService.findBookListByNameLike("会");
System.out.println(list);
}
}
-
编写BookAction
在cn.itcast.ssh.web.action中创建BookAction类,注解完成Service注入Action
//图书管理的表现层
@Controller("bookAction")
@Scope("prototype")//多例!!!!!
public class BookAction extends ActionSupport implements ModelDriven<Book>{
//数据模型对象
private Book book=new Book();
public Book getModel() {
return book;
}
//成员变量
@Autowired
private IBookService bookService;
//业务方法:保存图书
public String add(){
System.out.println(book);
//调用业务层保存...
bookService.saveBook(book);
return NONE;
}
}
-
使用struts2注解,页面访问Action
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>添加图书</title>
</head>
<body>
<h1>欢迎使用Xxx的图书管理系统</h1>
<s:form action="book_add" namespace="/" method="post">
图书的名称:<s:textfield name="name"/><br/>
图书的价格:<s:textfield name="price"/><br/>
<s:submit value="保存"/><s:reset value="重置"/>
</s:form>
</body>
</html>
如果添加namespace:
<s:form action="book_add" namespace="/ann" method="post"><!--或者是action="/ann/book_add" namespace="/"-->
图书的名称:<s:textfield name="name"/><br/>
图书的价格:<s:textfield name="price"/><br/>
<s:submit value="保存"/><s:reset value="重置"/>
</s:form>
第二步:导入Struts2 注解开发,基于约定扫描 (导入 struts2-convention-plugin.jar ),从struts2的struts-2.3.15.3-allstruts-2.3.15.3lib查找
找到struts-plugin.xml文件
打开文件分析:
1、<constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/>
表示:用来配置,哪些包会被扫描 ------ 使用注解的Action必须位于四个包和子包中 !
2、<constant name="struts.convention.action.suffix" value="Action"/>
表示:扫描Action 结尾的类
3、应用struts2 注解
@Action 访问
@Result 结果集
@NameSpace 包名称空间 默认 /
@ParentPackage 配置父包 默认 struts-default
第三步:配置struts.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 开发者模式:关闭:副作用:如果是ajax请求的情况下,一旦出错,不会在页面上打印错误 -->
<!-- <constant name="struts.devMode" value="true" /> -->
<!-- 如果使用ajax调试,可以使用:自动加载核心配置文件 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 如果使用ajax调试,可以使用:自动加载国际化资源文件 -->
<constant name="struts.i18n.reload" value="true"/>
<!-- 主题样式 -->
<constant name="struts.ui.theme" value="simple"/>
</struts>
在cn.itcast.ssh.web.action中创建BookAction的完整代码:
package cn.itcast.ssh.web.action;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import cn.itcast.ssh.domain.Book;
import cn.itcast.ssh.service.IBookService;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
//图书管理的表现层
@Controller("bookAction")
@Scope("prototype")//多例!!!!!
@ParentPackage("struts-default")
@Namespace("/ann")
@Results({@Result(name="success",location="/index.jsp")})//全局的结果集
public class BookAction extends ActionSupport implements ModelDriven<Book>{
//数据模型对象
private Book book=new Book();
public Book getModel() {
return book;
}
//成员变量
@Autowired
private IBookService bookService;
//业务方法:保存图书
//业务方法:保存图书
@Action(value="book_add"
,results={@Result(name="success",location="/index.jsp")})
public String add(){
System.out.println(book);
//调用业务层保存...
bookService.saveBook(book);
return "success";
}
}
-
Spring和Ehcache缓存集成
缓存作用:提升查询的效率,降低数据库的压力。
Hibernate的二级缓存和Spring缓存的对比:
Hibernate的二级缓存缺点:使用麻烦,只能缓存hibernate相关的对象。
spring的缓存:非常灵活,缓存任何的对象。使用简单。
Spring的缓存:可以整合第三方缓存框架,比如ehcache。
spring的缓存使用原理:基于aop面向切面的编程。开发步骤:
-
-
确定目标(要缓存哪些bean的哪些方法)
-
编写通知,配置通知
-
配置aop:切入点和切面
缓存从xml方式+注解方式分别讲解。
spring缓存的配置原理:(xml)
开发步骤:
1.确定目标:BookServiceImpl类中的查询方法
2.编写通知(spring帮你写好了),只需要配置,使用标签<cache:advice>
(1)spring-context-support-3.2.0.RELEASE.jar包
提供了平台缓存管理器相关class。
引入:
spring-framework-3.0.2.RELEASE-dependencies et.sourceforge.ehcachecom.springsource.net.sf.ehcache1.6.2
com.springsource.net.sf.ehcache-1.6.2.jar
第二步:引入约束
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
配置本地提示:导入spring-cache-3.2.xsd文件。
第三步:配置ehcache的缓存区域:从com.springsource.net.sf.ehcache-1.6.2.jar包中拷贝ehcache-failsafe.xml文件的内容
在src下创建ehcache.xml文件,用来覆盖ehcache-failsafe.xml文件,ehcache.xml文件的内容如下:
属性说明参考: http://blog.163.com/zsq303288862@126/blog/static/937459612011116112640694/
第四步:配置applicationContext.xml文件
<!-- 使用Xml的方式配置缓存 -->
<!--
第一步:配置缓存管理器工厂
-->
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!--
注入ehcache的核心配置文件
classpath中寻找ehcache.xml
-->
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<!-- 第二步:具体平台缓存管理器:整合ehcache的实现,需要导入jar -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<!-- 注入ehcache的缓存工厂 -->
<property name="cacheManager" ref="ehCacheManager"/>
</bean>
<!--
第三步:配置spring的缓存通知
* id:通知的名字
* cache-manager:缓存管理器,spring要整合缓存,这里要提供具体的缓存的方案的平台缓存管理器
-->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
<!-- 缓存的具体策略
cache:ehcache.xml文件中的缓存区域的名称
-->
<cache:caching cache="bookCache">
<!-- 拦截到的方法中,哪些方法,的结果要放入缓存 -->
<cache:cacheable method="findBookByNameLike "/>
<!-- 拦截到的方法中,哪些方法,清除缓存 -->
<cache:cache-evict method="save*" all-entries="true"/>
<cache:cache-evict method="update*" all-entries="true"/>
<cache:cache-evict method="delete*" all-entries="true"/>
</cache:caching>
</cache:advice>
<!-- 第四步:缓存的切面和切入点 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="bean(*Service)" id="cachePointcut"/>
<!-- 切面,通知的方法关联切入点 -->
<aop:advisor advice-ref="cacheAdvice" pointcut-ref="cachePointcut"/>
</aop:config>
第五步:测试代码:在cn.itcast.ssh.test包中创建BookServiceTest进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class BookServiceTest {
//注入service
@Autowired
private IBookService bookService;
@Test
public void testSaveBook() {
Book book = new Book();
book.setName("葵花宝典233");
book.setPrice(998d);
bookService.saveBook(book);
}
@Test
public void testFindBookListByNameLike() {
List<Book> list = bookService.findBookListByNameLike("星");
System.out.println(list);
List<Book> list2 = bookService.findBookListByNameLike("星");
System.out.println(list2);
Book book = new Book();
book.setName("葵花宝典2");
book.setPrice(998d);
bookService.saveBook(book);
List<Book> list3 = bookService.findBookListByNameLike("星");
System.out.println(list3);
List<Book> list4 = bookService.findBookListByNameLike("星");
System.out.println(list4);
}
}
第一次查询数据库,第二次从缓存中查询!
第二种方式:注解方式:
注解的方式使用缓存:
第一步:导入jar包
(1)spring-context-support-3.2.0.RELEASE.jar包
提供了平台缓存管理器相关class。
引入:
第二步:引入ehcache.xml文件:
第三步:引入头信息(和上面一样)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
第四步:编写applicationContext.xml,使用注解方式:
<!-- 使用注解的方式配置缓存 -->
<!-- 第一步:定义ehcache的对象EhCacheManager:spring提供了工厂,专业来new对象 -->
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- 注入ehcache的核心配置文件
通过源代码发现,这里即使不配置,默认就到classpath中寻找ehcache.xml
-->
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<!-- 第二步:具体平台缓存管理器:整合ehcache的实现,需要导入jar -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<!-- 注入ehcache的缓存对象 -->
<property name="cacheManager" ref="ehCacheManager"/>
</bean>
<!-- 第三步:配置缓存注解驱动 -->
<cache:annotation-driven cache-manager="cacheManager"/>
第五步:使用缓存的bean的类型的方法上,加两个注解:
在cn.itcast.ssh.service包中的类BookServiceImpl.java中添加缓存的注解
//图书业务层
@Service("bookService")
@Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)
public class BookServiceImpl implements IBookService{
//注入dao层
@Autowired
private IBookDao bookDao;
//保存图书
@Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)
@CacheEvict(value="bookCache",allEntries=true)
public void saveBook(Book book){
//调用dao层
bookDao.save(book);
}
//查询:复杂条件查询,根据书名模糊查询
@Cacheable(value="bookCache")//value:echache缓存区域的名字
public List<Book> findBookListByNameLike(String name){
//1.qbn
return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");
//2.qbc
// DetachedCriteria criteria =DetachedCriteria.forClass(Book.class);//root对象类型
// criteria.add(Restrictions.like("name", "%"+name+"%"));
// return bookDao.findByCriteria(criteria);
}
}
第六步:测试代码:在cn.itcast.ssh.test包中创建BookServiceTest进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class BookServiceTest {
//注入service
@Autowired
private IBookService bookService;
@Test
public void testSaveBook() {
Book book = new Book();
book.setName("葵花宝典233");
book.setPrice(998d);
bookService.saveBook(book);
}
@Test
public void testFindBookListByNameLike() {
List<Book> list = bookService.findBookListByNameLike("星");
System.out.println(list);
List<Book> list2 = bookService.findBookListByNameLike("星");
System.out.println(list2);
Book book = new Book();
book.setName("葵花宝典2");
book.setPrice(998d);
bookService.saveBook(book);
List<Book> list3 = bookService.findBookListByNameLike("星");
System.out.println(list3);
List<Book> list4 = bookService.findBookListByNameLike("星");
System.out.println(list4);
}
}
查看测试结果,控制台中输出:
第一次查询数据库,第二次从缓存中查询!
小结:推荐是用注解,简单,xml配置麻烦。
作业:
-
xml整合 (两遍 , 背着写一遍 )
-
声明式事务管理原理 (转账)(xml和注解)
-
注解整合(练习一遍)
-
spring的缓存整合配置一下,跑跑示例。--会配置,会用。