Spring學習總結(五)——Spring整合MyBatis(Maven+MySQL)二

接着上一篇博客《Spring整合MyBatis(Maven+MySQL)一》繼續。html

Spring的開放性和擴張性在J2EE應用領域獲得了充分的證實,與其餘優秀框架無縫的集成是Spring最爲強大的功能。Spring相似電腦的主板,能夠將許多部件集成在一塊兒協調工做。java

1、在Web項目中啓動Spring容器

在Web項目中當Web容器啓動時咱們要同時啓動Spring容器,有三種辦法,第一種使用監聽器啓動,第二使用Servlet啓動,第三使用MVC框架的擴展點啓動,這裏主要選擇第一種,由於監聽器的啓動時機早於Servlet。強烈建議使用辦法一。web

1.一、使用監聽器啓動Spring容器

咱們須要使用到Spring定義好的一個監聽器:org.springframework.web.context.ContextLoaderListener,該監聽器在包Spring-web.x.x.x.jar下,修改pom.xml文件,添加依賴:spring

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

修改web.xml文件,新增監聽器聲明,代碼以下:sql

    <listener>
        <description>Spring容器啓動監聽器</description>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

當監聽器在啓動Spring容器時會自動查找Web-INF/lib目錄下名爲applicationContext.xml配置文件,固然也能夠設置參數指定配置文件的具體位置,特別是有多個配置文件的狀況,指定辦法以下:數據庫

    <listener>
        <description>Spring容器加載監聽器</description>
        <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>

若是有多個配置文件則能夠經過逗號分開。怎麼判斷是否啓動成功,則能夠參考本文第二點,得到ApplicationContext實例,也能夠查看tomcat啓動時的信息,若是沒有出現錯誤且能找到以下說明基本成功。express

 

啓動失敗也有幾種可能,如applicationContext.xml文件的路徑錯誤;找不到類ContextLoaderListener;若是提示找不到類,極可能是由於沒有將Maven依賴的包發佈出去,能夠在項目屬性中設置,以下所示:apache

1.二、使用Servlet方式啓動Spring容器

方法與1.1基本相同,只是配置有小的區別,修改web.xml的具體內容以下:tomcat

<servlet>
    <servlet-name>context</servlet-name>
    <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</ servlet >
<context-param> 
    <param-name>contextConfigLocation</param-name>
       <!-- 多個配置文件之間以「,」隔開 -->
    <param-value>
           classpath:beans1.xml,classpath:beans2.xml...
     </param-value>
  </context-param>

須要注意的是二者都是繼承類ContextLoader,但從Spring3.0開始已經移除了ContextLoaderServlet,用ContextLoaderListener的方式替代。第3種啓動方式只有在特定的框架中才有效,因此很少用。session

2、獲取ApplicationContext實例

 當web容器啓動時Spring容器若是也成功啓動了,則能夠在整個web應用程序中得到ApplicationContext完成IOC、AOP及Spring的其它功能,得到ApplicationContext的經常使用方法有兩種:

2.一、使用工具類WebApplicationContextUtils得到Spring容器

2.1.一、定義一個Service類,BookTypeService代碼以下:

package com.zhangguo.Spring61.service;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import com.zhangguo.Spring61.entities.BookType;
import com.zhangguo.Spring61.mapping.BookTypeDAO;

/*
 * 圖書類型服務
 */
@Service
public class BookTypeService {

    @Resource
    BookTypeDAO bookTypeDAO;

    public List<BookType> getAllBookTypes() {
        System.err.println("一些被省去的業務");
        return bookTypeDAO.getAllBookTypes();
    }
}

@Service表示Spring容器將自動管理BookTypeService實例,@Resource表示自動裝配,會自動從Spring容器中找到類型爲BookTypeDAO的Bean完成bookTypeDAO字段的初始化。

2.1.二、定義一個Servlet,BookTypeList Servlet代碼以下:

package com.zhangguo.Spring61.action;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.zhangguo.Spring61.service.BookTypeService;

@WebServlet("/BookTypeList.do")
public class BookTypeList extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
     BookTypeService bookTypeService;
     
    @Override
    public void init() throws ServletException {
      //在當前上下文中得到Spring容器
      WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(getServletContext());
      //從容器中得到bean
      bookTypeService=ctx.getBean(BookTypeService.class);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer=response.getWriter();
        writer.print(bookTypeService.getAllBookTypes().size());
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

在Servlet中咱們重寫了父類的init方法,注意若是重寫帶參數的那個init方法init(ServletConfig config),則必定要記得調用父類的init方法完成參數的初始化,即不要刪除super.init(config),若是不這樣將獲不到servlet下上文;在init方法中咱們經過WebApplicationContextUtils類得到得了Spring容器。

此時的applicationContext.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:aop="http://www.springframework.org/schema/aop" 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/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <!--屬性佔位文件引入,能夠經過${屬性名}得到屬性文件中的內容 -->
    <context:property-placeholder location="classpath:db.properties" />

    <!--定義一個jdbc數據源,建立一個驅動管理數據源的bean -->
    <bean id="jdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.uid}" />
        <property name="password" value="${jdbc.pwd}" />
        <property name="acquireIncrement" value="5"></property>
        <property name="initialPoolSize" value="10"></property>
        <property name="minPoolSize" value="5"></property>
        <property name="maxPoolSize" value="20"></property>
    </bean>

    <!--定義一個jdbc數據源,建立一個驅動管理數據源的bean -->
    <bean id="jdbcDataSourceBak"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.uid}" />
        <property name="password" value="${jdbc.pwd}" />
    </bean>

    <!--建立一個sql會話工廠bean,指定數據源 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 指定數據源 -->
        <property name="dataSource" ref="jdbcDataSource" />
        <!--類型別名包,默認引入com.zhangguo.Spring61.entities下的全部類 -->
        <property name="typeAliasesPackage" value="com.zhangguo.Spring61.entities"></property>
        <!--指定sql映射xml文件的路徑 -->
        <property name="mapperLocations"
            value="classpath:com/zhangguo/Spring61/mapping/*Mapper.xml"></property>
    </bean>

    <!--自動掃描映射接口 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定sql會話工廠,在上面配置過的 -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <!-- 指定基礎包,即自動掃描com.zhangguo.Spring61.mapping這個包以及它的子包下的全部映射接口類 -->
        <property name="basePackage" value="com.zhangguo.Spring61.mapping"></property>
    </bean>

    <!-- 建立一個sqlSession對象 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

    <!--自動掃描組件 -->
    <context:component-scan base-package="com.zhangguo.Spring61">
        <context:exclude-filter type="aspectj" expression="com.zhangguo.Spring61.dao.*"/>
    </context:component-scan>
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
View Code

運行結果:

2.二、實現接口ApplicationContextAware

 當一個類實現了org.springframework.context.ApplicationContextAware接口時且實現該接口的類被Spring容器管理,則Spring容器會自動意識到須要調用接口中的方法setApplicationContext設置當前的Spring上下文。通俗說實現這個接口能夠方便得到Spring上下文。該接口以下:

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;

/**
 * Interface to be implemented by any object that wishes to be notified
 * of the {@link ApplicationContext} that it runs in.
 *
 * <p>Implementing this interface makes sense for example when an object
 * requires access to a set of collaborating beans. Note that configuration
 * via bean references is preferable to implementing this interface just
 * for bean lookup purposes.
 *
 * <p>This interface can also be implemented if an object needs access to file
 * resources, i.e. wants to call {@code getResource}, wants to publish
 * an application event, or requires access to the MessageSource. However,
 * it is preferable to implement the more specific {@link ResourceLoaderAware},
 * {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
 * in such a specific scenario.
 *
 * <p>Note that file resource dependencies can also be exposed as bean properties
 * of type {@link org.springframework.core.io.Resource}, populated via Strings
 * with automatic type conversion by the bean factory. This removes the need
 * for implementing any callback interface just for the purpose of accessing
 * a specific file resource.
 *
 * <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
 * convenience base class for application objects, implementing this interface.
 *
 * <p>For a list of all bean lifecycle methods, see the
 * {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @see ResourceLoaderAware
 * @see ApplicationEventPublisherAware
 * @see MessageSourceAware
 * @see org.springframework.context.support.ApplicationObjectSupport
 * @see org.springframework.beans.factory.BeanFactoryAware
 */
public interface ApplicationContextAware extends Aware {

    /**
     * Set the ApplicationContext that this object runs in.
     * Normally this call will be used to initialize the object.
     * <p>Invoked after population of normal bean properties but before an init callback such
     * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
     * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
     * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
     * {@link MessageSourceAware}, if applicable.
     * @param applicationContext the ApplicationContext object to be used by this object
     * @throws ApplicationContextException in case of context initialization errors
     * @throws BeansException if thrown by application context methods
     * @see org.springframework.beans.factory.BeanInitializationException
     */
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}
View Code

爲了達到目的,咱們如今建立一個名爲CtxUtil.java的類,實現該接口,代以下:

package com.zhangguo.Spring61.action;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class CtxUtil implements ApplicationContextAware {

    public static ApplicationContext springCtx;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        springCtx=applicationContext;
    }
    
    //根據名稱得到容器中的bean
    public static Object getBean(String name){
        return springCtx.getBean(name);
    }
    
    //根據類型得到容器中的bean
    public static <T> T getBean(Class<T> clazz){
        return springCtx.getBean(clazz);
    }

}

 爲了讓Spring容器在加載時能掃描到該類,咱們在類上註解了@Component;其實也能夠直接在ApplicationContext.xml中新增一個bean,以下所示:

    <bean id="ctxUtil" class="com.zhangguo.Spring61.action.CtxUtil"></bean>

    <!--自動掃描組件 -->
    <context:component-scan base-package="com.zhangguo.Spring61">
        <context:exclude-filter type="aspectj" expression="com.zhangguo.Spring61.dao.*"/>
    </context:component-scan>

 測試代碼以下:

package com.zhangguo.Spring61.action;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.zhangguo.Spring61.service.BookTypeService;

@WebServlet("/BookTypeSize.do")
public class BookTypeSize extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
     BookTypeService bookTypeService;
     
    @Override
    public void init() throws ServletException {
      //從容器中得到bean
      bookTypeService=CtxUtil.getBean(BookTypeService.class);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer=response.getWriter();
        writer.print(bookTypeService.getAllBookTypes().size());
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
View Code

運行結果同2.1。

3、基於註解的聲明式事務管理配置

MyBatis-Spring利用了存在於Spring中的DataSourceTransactionManager管理事務。

一旦Spring的PlatformTransactionManager配置好了,你能夠在Spring中以你一般的作法來配置事務。@Transactional註解和AOP方式的配置都是支持的。在事務處理期間,一個單獨的SqlSession對象將會被建立和使用。當事務完成時,這個session會以合適的方式提交或回滾。一旦事務建立以後,MyBatis-Spring將會透明的管理事務。在你的DAO類中就不須要額外的代碼了。

咱們先使用一個簡單的示例,證實沒有事務時的狀態:

        PrintWriter writer=response.getWriter();
        BookType entity1=new BookType();
        entity1.setTypeName("中國文學");
        
        BookType entity2=new BookType();
        entity1.setTypeName("外國文學");  //請注意這是是entity1
        writer.print(bookTypeService.addDouble(entity1, entity2));

當運行時會發現有異常,由於entity2的typeName屬性是null,報異常了;按照事務的特性兩條記錄要麼都添加成功要麼都添加不成功。但查看數據庫會發現,有一個對象的數據被添加成功了,另外一個則失敗了,這不符合原子特性。

爲了解決上面的問題,咱們引入Spring中的事務與MyBatis-Spring的事務管理。必定要記得添加Spring-tx.jar的依賴。修改ApplicationContext.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:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 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-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!--屬性佔位文件引入,能夠經過${屬性名}得到屬性文件中的內容 -->
    <context:property-placeholder location="classpath:db.properties" />

    <!--定義一個jdbc數據源,建立一個驅動管理數據源的bean -->
    <bean id="jdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.uid}" />
        <property name="password" value="${jdbc.pwd}" />
        <property name="acquireIncrement" value="5"></property>
        <property name="initialPoolSize" value="10"></property>
        <property name="minPoolSize" value="5"></property>
        <property name="maxPoolSize" value="20"></property>
    </bean>

    <!--定義一個jdbc數據源,建立一個驅動管理數據源的bean -->
    <bean id="jdbcDataSourceBak"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.uid}" />
        <property name="password" value="${jdbc.pwd}" />
    </bean>

    <!--建立一個sql會話工廠bean,指定數據源 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 指定數據源 -->
        <property name="dataSource" ref="jdbcDataSource" />
        <!--類型別名包,默認引入com.zhangguo.Spring61.entities下的全部類 -->
        <property name="typeAliasesPackage" value="com.zhangguo.Spring61.entities"></property>
        <!--指定sql映射xml文件的路徑 -->
        <property name="mapperLocations"
            value="classpath:com/zhangguo/Spring61/mapping/*Mapper.xml"></property>
    </bean>

    <!--自動掃描映射接口 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定sql會話工廠,在上面配置過的 -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <!-- 指定基礎包,即自動掃描com.zhangguo.Spring61.mapping這個包以及它的子包下的全部映射接口類 -->
        <property name="basePackage" value="com.zhangguo.Spring61.mapping"></property>
    </bean>

    <!-- 建立一個sqlSession對象 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
    
    <bean id="ctxUtil" class="com.zhangguo.Spring61.action.CtxUtil"></bean>
    
    <!--聲明式事務管理 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="jdbcDataSource"></property>
    </bean>
    <!--聲明支持使用註解管理事務 -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <!--自動掃描組件 -->
    <context:component-scan base-package="com.zhangguo.Spring61">
        <context:exclude-filter type="aspectj" expression="com.zhangguo.Spring61.dao.*"/>
    </context:component-scan>
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

請注意,加粗字體的部分是新增的。 修改後的BookTypeDAO接口以下:

package com.zhangguo.Spring61.mapping;

import java.util.List;

import com.zhangguo.Spring61.entities.BookType;

/**
 * 圖書類型數據訪問接口
 *
 */
public interface BookTypeDAO {
    /*
     * 得到全部圖書類型
     */
    public List<BookType> getAllBookTypes();
    
    /**
     * 添加新的圖書類型
     */
    public int add(BookType entity);
}

修改後的BookTypeMapper.xml映射文件以下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空間應該是對應接口的包名+類名 -->
<mapper namespace="com.zhangguo.Spring61.mapping.BookTypeDAO">
    <!--id應該是接口中的方法,結果類型如沒有配置別名則應該使用全名稱 -->
    <select id="getAllBookTypes" resultType="BookType">
        select id,typeName from booktypes
    </select>
    
    <insert id="add" parameterType="BookType">
        insert into booktypes(typeName) values(#{typeName})
    </insert>
</mapper> 

修改後的BookTypeService類以下:

package com.zhangguo.Spring61.service;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.zhangguo.Spring61.entities.BookType;
import com.zhangguo.Spring61.mapping.BookTypeDAO;

/*
 * 圖書類型服務
 */
@Service
public class BookTypeService {

    @Resource
    BookTypeDAO bookTypeDAO;

    public List<BookType> getAllBookTypes() {
        System.err.println("一些被省去的業務");
        return bookTypeDAO.getAllBookTypes();
    }
    
    @Transactional
    public int addDouble(BookType entity1,BookType entity2){
        int rows=0;
        rows+=bookTypeDAO.add(entity1);
        rows+=bookTypeDAO.add(entity2);
        return rows;
    }
}

在方法addDouble上增長了一個註解@Transactional,用於顯式聲明該方法須要事務處理,若是把該註解移除則不會擁有事務特性。

測試的Servlet代碼以下:

package com.zhangguo.Spring61.action;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.zhangguo.Spring61.entities.BookType;
import com.zhangguo.Spring61.service.BookTypeService;

@WebServlet("/BookTypeAdd.do")
public class BookTypeAdd extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
     BookTypeService bookTypeService;
     
    @Override
    public void init() throws ServletException {
      //從容器中得到bean
      bookTypeService=CtxUtil.getBean(BookTypeService.class);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer=response.getWriter();
        BookType entity1=new BookType();
        entity1.setTypeName("量子力學");
        
        BookType entity2=new BookType();
        entity1.setTypeName("天體物理");  //請注意這是是entity1,entity2的typeName屬性爲空
        writer.print(bookTypeService.addDouble(entity1, entity2));
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

entity2由於沒有typeName屬性,添加時會異常,有事務存在,則數據庫並無變化,entity1與entity2都未添加到數據庫中。

除了使用聲明式事務管理,也可使用AOP進行事務攔截,參考代碼以下:

    <!-- 攔截器方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="append*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="modify*" propagation="REQUIRED" />
            <tx:method name="edit*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="remove*" propagation="REQUIRED" />
            <tx:method name="repair" propagation="REQUIRED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" />
            <tx:method name="get*" propagation="SUPPORTS" />
            <tx:method name="find*" propagation="SUPPORTS" />
            <tx:method name="load*" propagation="SUPPORTS" />
            <tx:method name="search*" propagation="SUPPORTS" />
            <tx:method name="datagrid*" propagation="SUPPORTS" />
            <tx:method name="*" propagation="SUPPORTS" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.service..*Impl.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
    </aop:config>

小結:聲明式事務管理須要根據實際狀況編碼註解方法要不要事務處理,攔截方式配置事務則是根據方法名統一處理,通常查詢是不須要事務參與的。在上面的配置中REQUIRED則表示須要事務支持,而SUPPORTS則不須要事務支持。

4、示例下載

示例下載

相關文章
相關標籤/搜索