【JavaWeb-25】事务管理相关知识、手动/半自动/自动管理事务案例、整合Junit、整合Web、spring和hibernate整合、struts2和spring整合

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_REQUIREDPROPAGATION_REQUIRES_NEWPROPAGATION_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>

源代码:JavaEE spring手动管理事务案例

——半自动管理事务。我们上面管理事务的目的就是在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;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值