1、事务的新知识点,保存点SavePoint。
需求:AB(必须),CD(可选)
Connection conn = null;
Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
//1 获得连接
conn = ...;
//2 开启事务
conn.setAutoCommit(false);
A
B
savepoint = conn.setSavepoint();
C
D
//3 提交事务
conn.commit();
} catch(){
if(savepoint != null){ //CD异常
// 回滚到CD之前
conn.rollback(savepoint);
// 提交AB
conn.commit();
} else{ //AB异常
// 回滚AB
conn.rollback();
}
}
2、事务管理介绍。
——我们先说事务管理,在spring的核心包里,我们之前也用过,有一个叫做spring-tx-3.2.0.RELEASE.jar
的jar包。里面包含了主要的事务接口。比如我们下面说的3个重要接口。
- PlatformTransactionManager 平台事务管理器,spring要管理事务,必须使用事务管理器。进行事务配置时,必须配置事务管理器。
- TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,例如:隔离级别、是否只读、超时时间等。进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
- TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。spring底层根据状态进行相应操作。
——接下来我们详细的说说每一项。我们先说事务管理器(PlatformTransactionManager)。我们如果要使用事务管理器的话,就不能直接使用上面的接口,而是要使用它的实现类,而根据我们未来使用的技术不同,有2个实现类,一个是如果使用jdbc,一个是使用hibernate的orm,所以这两个jar包分别如下,都能在\spring-framework-3.2.0.RELEASE\libs
里找到:
对应着上面的,我们常用的事务管理器有:
- DataSourceTransactionManager ,jdbc开发时事务管理器,采用JdbcTemplate。
- HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate。
事务管理器里面的核心api,我们只做了解,因为我们不会直接使用api。
- TransactionStatus getTransaction(TransactionDefinition definition) ,事务管理器通过“事务详情”,获得“事务状态”,从而管理事务。
- void commit(TransactionStatus status)根据状态提交。
- void rollback(TransactionStatus status)根据状态回滚。
——另一个说的是事务状态(TransactionStatus)。仅作了解即可。
——最后来说说事务详情。我们关联了源码之后,可以看事务详情的源码,我们选中TransactionDefinition后,按Ctrl+o
可以查看属性和方法。
- getName()。配置事务详情的名称,一般是方法名,也有公司有特殊规定的前后缀。
- 其他几个分别是是否只读、隔离级别、获得超时时间、和传播行为。看最上面的都是取值,其中超时时间默认是-1,也就是按照数据库自己的设置来,隔离级别是01248几种取值,剩下的就是传播行为的几个取值。
我们来看看传播行为(PropagationBehavior),就是在两个业务之间如何共享事务。
PROPAGATION_REQUIRED
, required , 必须 【默认值】。支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将创建一个新的事务。PROPAGATION_SUPPORTS
,supports ,支持。支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将以非事务执行。PROPAGATION_MANDATORY
,mandatory ,强制。支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将抛异常。PROPAGATION_REQUIRES_NEW
, requires_new ,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务,如果A没有事务,B创建一个新的事务。PROPAGATION_NOT_SUPPORTED
,not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行。如果A没有事务,B将以非事务执行。PROPAGATION_NEVER
,never,从不。如果A有事务,B将抛异常。如果A没有事务,B将以非事务执行。PROPAGATION_NESTED
,nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
常见的有以下3个,需要了解,后续写的时候也是通过提示来写:是PROPAGATION_REQUIRED
、PROPAGATION_REQUIRES_NEW
、PROPAGATION_NESTED
。
3、转账案例。
——第一步搭建环境。导入jar包。
创建数据库和表t_account。
写实体类Account、Dao层、Service层、配置applicationContext.xml,最后测试,发现从成功,但是此时是没有事务的,也就是说我们在两笔操作之间如果加入异常,那么……。
源代码:JavaEE spring事务操作环境和基本功能搭建
——我们现在开始手动管理事务。主要步骤就是,我们需要配置一下事务管理器(配置事务管理器需要事务,事务是从Connection获得的,而Connection是从连接池里获得的,所以我们配置事务管理器的时候需要注入一个连接池dataSource)。然后配置事务模板(需要注入事务管理器)。把这个事务模板注入给AccountServiceImpl的bean,这个时候它拥有了2个属性注入。
改造了一下AccountServiceImpl ,增加了一个属性transactionTemplate
,以及把操作放到了事务中。
package com.hello.service.impl;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.hello.dao.AccountDao;
import com.hello.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void tranfer(final String outer, final String inner, final Integer money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
accountDao.out(outer, money);
int i=1/0;
accountDao.in(inner, money);
}
});
}
}
然后在applicationContext.xml里的配置,修改了3,增加了4和5,后面2步就是配置事务管理器(需要数据源)、配置事务模板(需要事务管理器),然后这个事务模板作为属性注入到service层里面供使用。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring01"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2.dao配置 -->
<bean id="accountDao" class="com.hello.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置Service -->
<bean id="accountService" class="com.hello.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 4.配置事务模板,需要注入事务管理器 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"></property>
</bean>
<!-- 5.配置事务管理器,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
——半自动管理事务。我们上面管理事务的目的就是在AccountServiceImpl里的操作之上加了一层事务管理,这个就是AOP切面编程的思想。所以我们可以让spring帮我们生成AccountServiceImpl的代理对象,然后我们获得AccountServiceImpl的时候其实就是获得了它的代理对象,当然,这个代理对象我们是添加了事务管理功能的(这个是用spring的事务管理框架自动添加的)。
我们的AccountServiceImpl类还是:
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void tranfer(final String outer, final String inner, final Integer money) {
accountDao.out(outer, money);
int i=1/0;
accountDao.in(inner, money);
}
然后,我们的配置文件,需要配置代理对象的bean,主要在第5步,第5步需要配置事务管理器和事务属性。
<?xml version="1.0" encoding="UTF-8"?>
<beans ……
<!-- 1.配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring01"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2.dao配置 -->
<bean id="accountDao" class="com.hello.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置Service -->
<bean id="accountService" class="com.hello.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 4.配置事务管理器,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.配置代理对象的bean,添加目标类、接口、事务管理器、事务属性 -->
<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces" value="com.hello.service.AccountService"></property>
<property name="target" ref="accountService"></property>
<property name="transactionManager" ref="txManager"></property>
<property name="transactionAttributes">
<props>
<!-- 这个key就是我们上面说过的事务的getName的值 -->
<!-- 格式:PROPAGATION,ISOLATION,readOnly,-Exception(异常回滚),+Exception(异常提交) -->
<prop key="tranfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
</props>
</property>
</bean>
</beans>
测试:
@Test
public void test01(){
String xmlPath="applicationContext.xml";
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService=(AccountService)classPathXmlApplicationContext.getBean("proxyAccountService");
accountService.tranfer("Eric", "Andy", 100);
}
源代码:JavaEE spring半自动bean管理事务案例
——自动管理事务。就是AOP了,先说基于XML的。基于上面的半自动,我们只需要继续修改applicationContext.xml文件即可。我们需要做的事情就是配置事务管理器、配置事务属性、配置aop切面。需要引入aop和tx命名空间。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
……
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">
<!-- 1.配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring01"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2.dao配置 -->
<bean id="accountDao" class="com.hello.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置Service -->
<bean id="accountService" class="com.hello.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 4.1配置事务管理器,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2配置事务通知,也就是增强类 -->
<!-- 配置事务 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 在aop筛选出的切入点(方法)基础上决定这些方法用什么传播方式,就是下面这样可以一个个设置 -->
<tx:method name="tranfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 4.3aop编程,也就是把目标类里的方法和增强通知连接起来 -->
<aop:config>
<!-- 这里就是把什么样的方法和增强通知连接起来 -->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hello.service.impl.AccountServiceImpl.*(..))"/>
</aop:config>
</beans>
源代码:JavaEE spring基于XML的自动管理事务案例
——基于注解的自动事务管理。只做2件事:
- 配置事务管理器,将并事务管理器交予spring
- 在目标类或目标方法添加注解即可 @Transactional
在applicationContext.xml里:
<?xml version="1.0" encoding="UTF-8"?>
<beans ……
<!-- 1.配置C3P0数据源 -->
……
<!-- 2.dao配置 -->
……
<!-- 3.配置Service -->
……
<!-- 4.1配置事务管理器,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2把事务管理器交给spring.proxy-target-class为true时强制使用cglib -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="false"/>
</beans>
然后在需要增强通知的目标类或者里面的方法上面添加注解:
@Transactional
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void tranfer(final String outer, final String inner, final Integer money) {
accountDao.out(outer, money);
int i=1/0;
accountDao.in(inner, money);
}
}
源代码:JavaEE spring基于注解的自动管理事务案例
4、整合Junit。就是改写测试类,而且是使用注解的方式。主要做两件事:让Junit通知spring加载配置文件(也就是我们不自己加载配置文件的代码了),让spring容器自动进行注入(也就是说我们不通过getBean,而是加一个属性accountService,然后自动注入)。
先导入spring-test-3.2.0.RELEASE.jar
。然后开始修改,添加的注解其实就是取代了下面注释掉的代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestTx {
@Autowired
private AccountService accountService;
@Test
public void test01(){
// String xmlPath="applicationContext.xml";
// ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
// AccountService accountService=(AccountService)classPathXmlApplicationContext.getBean("accountService");
accountService.tranfer("Eric", "Andy", 100);
}
}
5、整合WEB,就是把spring容器放到servletContext作用域里,然后需要的时候从这个里面获取spring容器,当然我们放到作用域里的是配置信息,我们到时候取得的就直接是一个类似ApplicationContext 的对象。
需要先导入spring-web-3.2.0.RELEASE.jar
,才能使用这个整合WEB的功能。
我们在index.jsp页面写一个连接,点击后是进入我们自己写的servlet,这个servlet里面演示的是如何获得spring容器,取出来后我们再用这个spring容器获得bean来做操作,这个取容器只做了解。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ApplicationContext applicationContext =(ApplicationContext)this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.tranfer("Eric", "Andy", 100);
}
那我们之前其实已经把spring容器放到servletContext里了,是通过监听器实现的,spring提供了一个监听器,就是ContextLoaderListener,它会在tomcat服务器一启动的时候就加载配置文件,也就是spring容器,当然它的加载路径是/WEB-INF/applicationContext.xml
,但是我们的文件不在这个路径下面,所以我们需要自己定义配置文件的位置,其中classpath:
表示在src文件下的意思。
<!-- 确定配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置spring 监听器,加载xml配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
至此,我们访问http://localhost:8080/Day01_Tx/index.jsp
的时候,点击按钮,然后就执行了一次转账。
6、SSH整合的第一步就是导入jar包,我们可以导入三个框架的jar包。
——先导入struts2的jar包,比较容易。在struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib
下面所有13个jar包。
——再导入spring的jar包。核心包spring-framework-3.2.0.RELEASE\libs
下有11个。依赖包spring-framework-3.0.2.RELEASE-dependencies
下面C3P0的1个、aop联盟1个、commons下面的logging已经在struts2里面导入过并且dbcp和pool一对jar包配置需要再导入(我们导入了C3P0)、aspectJ里面1个。一共14个。
——我们导入Hibernate之前,先导入mysql的驱动包,1个。
——我们导入Hibernate的jar包。首先是hibernate3.jar
核心包1个,hibernate-distribution-3.6.10.Final\lib\required
下面的6个必须的。jpa里1个jar包。我们还要导入二级缓存的jar包(我们用的ehcache),原先是1个核心包+2个依赖包,但是有一个依赖包commons-logging.jar
之前已经导入了,所以只有ehcache-1.5.0.jar
核心包和backport-util-concurrent.jar
依赖包2个。一共10个。
——针对日志方面。导入slf4j转log4j的jar包。在hibernate最后一节课用过。导入这个jar包。算是1个。
——然后,我们需要SSH整合,整合的顺序是struts2整合spring,spring整合hibernate,这是由jar包决定的:struts2整合spring,是因为struts2有一个jar包叫struts2-spring-plugin-2.3.15.3.jar
,我们也导入它,也算是1个。spring整合hibernate是因为spring有一个spring-orm-3.2.0.RELEASE.jar
,之前我们已经导入了。
——至此,以上一共40个。我们发现其中有两个是重复的,删除javassist-3.11.0.GA.jar
保留javassist-3.12.0.GA.jar
。所以最终是39个jar包。把这份jar包收藏一下,下次直接拷贝用。
7、spring整合hibernate(有hibernate.cfg.xml)。简单开立账户存款的案例。
——先建立table表。
——创建实体类Account,立即创建对应的映射文件Account.hbm.xml。
——创建dao接口和实体类。这里使用了hibernateTemplate来操作数据了,而不是用jdbc来操作数据,这个是新知识点。为此,在后面我们需要spring里配置以下让它为我们自动生成一个hibernateTemplate,供此处属性注入的时候用(也在spring配置文件里)。
package com.hello.dao.impl;
import org.springframework.orm.hibernate3.HibernateTemplate;
import com.hello.dao.AccountDao;
import com.hello.domain.Account;
public class AccountDaoImpl implements AccountDao {
private HibernateTemplate hibernateTemplate;
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public void save(Account account) {
hibernateTemplate.save(account);
}
}
——然后就是service接口和实现类。
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void register(Account account) {
accountDao.save(account);
}
}
——业务逻辑都写清之后,我们开始配置hibernate的配置文件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>
<session-factory>
<!-- 基本4项 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/spring01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- SQL语句 -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- 线程 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 自动生成表,我们已经建立表了,一般用不着 -->
<property name="hbm2ddl.auto">update</property>
<!-- 导入映射文件 -->
<mapping resource="com/hello/domain/Account.hbm.xml"/>
</session-factory>
</hibernate-configuration>
——然后,我们就开始设置spring的配置文件applicationContext.xml。主要核心都在这。我们需要配置service的实现类bean,那么就需要dao的实现类bean,那么就需要一个hibernateTemplate模板的bean,那么就需要一个sessionFactory的bean,环环相扣。最终还要利用aop配置事务,我们配置事务最终是通过aop:advisor
连接advice通知和pointcut切入点,我们的切入点可以直接用表达式来写,我们的advice通知在这里就是事务通知,也就是事务方面的增强代码,也就是事务详情,事务详情的配置需要事务管理器(并规定每个方法的详情事务信息比如隔离级别等),所以我们需要先生成一个hibernateTransactionManager事务管理器。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 0.添加aop/context/tx3个命名空间 -->
<!-- 1.加载hibernate配置文件,获得sessionFactory对象 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 2.创建模板,底层用的是session,session是由sessionFactory获得的,所以传入sessionFactory -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 3.dao -->
<bean id="accountDao" class="com.hello.dao.impl.AccountDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<!-- 4.service -->
<bean id="accountService" class="com.hello.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 5.事务管理,自动提交 -->
<!-- 5.1事务管理器,jdbc注入的是dataSource,用hibernate模板注入的是sessionFactory -->
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 5.1事务详情 -->
<tx:advice id="txAdvice" transaction-manager="hibernateTransactionManager">
<tx:attributes>
<tx:method name="register"/>
</tx:attributes>
</tx:advice>
<!-- 5.1aop标称 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hello.service.impl.AccountServiceImpl.*(..))"/>
</aop:config>
</beans>
——最后,测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestSSH {
@Autowired
private AccountService accountService;
@Test
public void test01(){
Account account=new Account();
account.setName("Tom");
account.setMoney(1000);
accountService.register(account);
}
}
——最后,我们在hibernate-distribution-3.6.10.Final\project\etc
里找一个log4j.properties
放在src目录下,不然项目一直有warning。
源代码:JavaEE spring和Hibernate整合(有hibernate.cfg.xml)
8、spring整合hibernate(没有hibernate.cfg.xml),主要有2个工作要做,基于上面的项目:
- 删除hibernate.cfg.xml,把里面的配置都整合到appilicationContext.xml里。
- 修改Dao层,直接继承自HibernateDaoSupport,这样就不需要写一个hibernateTemplate属性了,HibernateDaoSupport里帮我们生成了,所以对应的dao的spring配置注入方面也需要修改。
——我们先来删除hibernate.cfg.xml,修改appilicationContext.xml,下面我们已经把功能配置都移到appilicationContext.xml里了。
<!-- 0.1配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring01"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 1.加载hibernate配置文件,获得sessionFactory对象 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="show_sql">true</prop>
<prop key="format_sql">true</prop>
<prop key="hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>
<!-- 详见外面注解 -->
<property name="mappingLocations" value="classpath:com/hello/domain/*.hbm.xml"></property>
</bean>
上面导入映射文件的属性之间非分别:
- mappingLocations ,确定映射文件位置,需要“classpath:”,支持通配符,
<property name="mappingLocations" value="classpath:com/itheima/domain/User.hbm.xml"></property>
,<property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
- mappingResources ,加载执行映射文件,从src下开始 。不支持通配符
*
。<property name="mappingResources" value="com/itheima/domain/User.hbm.xml"></property>
- mappingDirectoryLocations ,加载指定目录下的,所有配置文件。
<property name="mappingDirectoryLocations" value="classpath:com/itheima/domain/"></property>
- mappingJarLocations, 从jar包中获得映射文件。
——修改dao层和spring的配置。
public class AccountDaoImpl extends HibernateDaoSupport implements AccountDao {
public void save(Account account) {
this.getHibernateTemplate().save(account);
}
}
<!-- 2.dao -->
<bean id="accountDao" class="com.hello.dao.impl.AccountDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
源代码:JavaEE spring和Hibernate整合(没有hibernate.cfg.xml)
9、struts整合spring:spring创建action。
- 编写action类,并将其配置给spring ,spring可以注入service
- 编写struts.xml
- 表单jsp页面
- web.xml 配置
- 确定配置文件contextConfigLocation
- 配置监听器 ContextLoaderListener
- 配置前端控制器 StrutsPrepareAndExecuteFitler
——我们编写一个数据表t_account,然后回到代码开发里来,写了一个实体类Account,然后写了一个实体类对应的映射文件Account.hbm.xml,然后写了dao接口和实体类AccountDaoImpl。然后写了service接口和实体类AccountServiceImpl。然后写了一个action类叫AccountAction:
package com.hello.web.action;
import com.hello.domain.Account;
import com.hello.service.AccountService;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class AccountAction extends ActionSupport implements ModelDriven<Account> {
//注意,此处一定不要忘记实例化了,不然就报错无法saveOrUpdate
private Account account=new Account();
public Account getModel() {
return account;
}
private AccountService accountService;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public String register(){
accountService.register(account);
return "success";
}
}
——然后在spring配置中配置以下action的bean。
<bean id="accountAction" class="com.hello.web.action.AccountAction" scope="prototype">
<property name="accountService" ref="accountService"></property>
</bean>
——然后去配置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" />
<package name="default" namespace="/" extends="struts-default">
<!-- 这里的accountAction是之前在spring里面配置过的id -->
<action name="accountAction_*" class="accountAction" method="{1}">
<result name="success">/success.jsp</result>
</action>
</package>
</struts>
——然后jsp页面:
<form action="${pageContext.request.contextPath }/accountAction_register" method="post">
用户名:<input type="text" name="name" /><br>
存款金额:<input type="text" name="money" /><br>
<input type="submit"/>
</form>
——最后是web.xml的配置,这个主要是加载配置(把spring的配置文件加载进来了)、配置监听、配置struts2:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
</web-app>
用struts2整合spring,我们之前使用struts2的时候就是在web.xml里面配置了struts2的过滤器之后,然后根据特定的action的名称,根据struts.xml里的配置去特定类里面找特定的方法去处理即可。整合之后,这里其实只是把action的生成交给了spring,然后我们在使用action类的地方可以使用哪个id,貌似二者整合并不深,而且我们可以修改上面的代码,即在spring里面删除掉生成action的那个bean配置,然后在struts.xml配置文件里用到action类的地方直接使用全限定类名来取代id,那么也是可以的,而且后面这种全限定类名的写法就是我们之前一直使用的方法。
源代码:JavaEE Struts2整合Spring的小案例
10、做上面第9节案例的时候,出现2个报错。
——第一个报错,是所有代码都没问题,但是就是无法访问index.jsp,提示404错误,描述是The requested resource is not available.
,这个时候,我们如果从代码层面无法解决的话,那么就应该是项目层面的问题,比如项目根目录的名称写的不对,那有人说了,项目名称就摆在那,怎么可能写错了呢?实际上就是如此。
我们在练习的时候经常是在上一个项目的基础上rename一下项目的名字就可以了,但是这个时候web context-root
的根路径是没有被修改过来的,如果你注意看看那个报错信息,有的时候会出现上一个项目的名称,那就是因为你没修改彻底。那么怎么修改呢?右击项目,选择属性后,如下:
最好再看一下如下的配置,也同步修改了:
——第二个报错,出现Messages: attempt to create saveOrUpdate event with null entity
。详细如下:
java.lang.IllegalArgumentException: attempt to create saveOrUpdate event with null entity
at org.hibernate.event.SaveOrUpdateEvent.<init>(SaveOrUpdateEvent.java:40)
at org.hibernate.event.SaveOrUpdateEvent.<init>(SaveOrUpdateEvent.java:23)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:518)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:514)
意思就是,无法保存实体类,实体类是null空。首先我们从根源上来找,我们第一步是form表单,然后是实体类,我们要保证form表单的name和实体类的属性一致。然后再去看我们的接下来就是经过了action类,在action类里我们使用了ModelDriver,这是个好东西,但是它需要先声明一个实体类的属性,并且初始化。就是在这个初始化的地方我忘记了,而对这个实体类的setter和getter是可以省略的,当然加上也没什么大事。
public class AccountAction extends ActionSupport implements ModelDriven<Account> {
private Account account=new Account();
public Account getModel() {
return account;
}
}