04.spring framework的集成hibernate+四種事務管理方式+精講基於註解的管理方式

一、依賴

maven依賴

1、依賴包版本

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>

    <spring.version>4.3.23.RELEASE</spring.version>
    <hibernate.version>5.1.17.Final</hibernate.version>
    <mysql.version>5.1.47</mysql.version>
    <c3p0.version>0.9.2.1</c3p0.version>
    <junit.version>4.12</junit.version>
    <lombok.version>1.18.6</lombok.version>
  </properties>

2、spring依賴包

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

  </dependencies>

3、hibernate依賴包

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>

4、數據庫驅動包

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>

5、數據庫連接池

由於hibernate已經不再對dbcp提供支持,所以這裏使用的是c3p0

    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>${c3p0.version}</version>
    </dependency>

6、其他包(測試、工具)

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>

二、在Spring中配置數據源

jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/spring-data-test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
#jdbc.url=jdbc:mysql://localhost:3306/spring-data-test 這個保存到數據庫中會導致亂碼
jdbc.driver.class=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=123456

applicationContext.xml

    <!-- 配置讀取properties文件的工具類 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- 配置c3p0數據庫連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="driverClass" value="${jdbc.driver.class}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

三、在Spring中配置hibernate的sessionFactory

<!-- 配置hibernate的sessionFactory -->
    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- hibernateProperties屬性:配置與hibernate相關內容,如顯示sql語句,開啓正向工程 -->
        <property name="hibernateProperties">
            <value>
                <!-- hibernate.dialect=org.hibernate.dialect.HSQLDialect -->
                <!-- 顯示當前執行的sql -->
                hibernate.show_sql=true
                <!-- 開啓正向工程 -->
                hibernate.hbm2ddl.auto=update
                <!--SQL格式化-->
                hibernate.format_sql=true
            </value>
        </property>
        <!-- 掃描實體所在的包 -->
        <property name="packagesToScan">
            <list>
                <value>com.mzj.spring.pojo</value>
            </list>
        </property>
    </bean>

其中hibernateProperties常用的屬性有:

hibernate.dialect

dialect就是“方言”,因爲hibernate是要把Java對象轉換成關係數據庫來描述的,而關係數據庫雖然有一些統一的標準,如SQL-92等。

但是實際上各數據庫如Oracle,MySQL,MS SQL Server等等爲了提高性能或增加功能都提供了一些額外的標準或語法,因此,hibernate爲了更好適配各種關係數據庫,針對每種數據庫都指定了一個方言dialect。

hibernate.show_sql 是否顯示執行的sql語句,取值:true/false
hibernate.hbm2ddl.auto

開啓正向工程,取值說明:

 validate:加載hibernate時,驗證創建數據庫表結構

  create:每次加載hibernate,重新創建數據庫表結構,這就是導致數據庫表結構丟失的原因

  create-drop:加載hibernate時創建,退出是刪除表結構

  update:加載hibernate自動更新數據庫結構

hibernate.format_sql 是否對sql進行格式化顯示輸出,,取值:true/false

四、在Spring中配置springIOC的註解掃描

<!-- 配置springIOC的註解掃描 -->
    <context:component-scan base-package="com.mzj.spring"></context:component-scan>

五、在Spring中配置HibernateTemplate對象

目的是可以在業務DAO實現類中注入HibernateTemplate對象,並通過操作此對象完成數據庫訪問。

<!-- 配置HibernateTemplate對象 -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

六、編碼

1、實體bean

package com.mzj.spring.pojo;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name="ts_users")
@Data
public class Users implements Serializable{
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    private Long id;

    @Column(name="user_name")
    private String userName;

    @Column(name="age")
    private Integer age;
}

2、Dao接口

package com.mzj.spring.dao;

import com.mzj.spring.pojo.Users;

public interface UsersDao {
    void insertUsers(Users users);

    void updateUsers(Users users);

    void deleteUsers(Users users);

    Users selectUserById(Long id);
}

3、Dao接口實現

通過注入的HibernateTemplate實現對數據庫的訪問

package com.mzj.spring.dao.impl;

import com.mzj.spring.dao.UsersDao;
import com.mzj.spring.pojo.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UsersDaoImpl implements UsersDao {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    public void insertUsers(Users users) {
        this.hibernateTemplate.save(users);
    }

    public void updateUsers(Users users) {
        this.hibernateTemplate.update(users);
    }

    public void deleteUsers(Users users) {
        this.hibernateTemplate.delete(users);
    }

    public Users selectUserById(Long id) {
        return this.hibernateTemplate.get(Users.class, id);
    }

}

七、四種事務管理方式

通過上面的設置可以使得Spring和Hibernate完成整合,下面開始說Spring管理Hibernate事務的四種管理方式:

第一種方式,利用tx標籤配置事務。

	<!-- a. 配置事務容器 -->
	<bean id="txManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>
	<!-- b. 定義事務規則 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="update" propagation="REQUIRED"/>
			<tx:method name="*" read-only="false" />
		</tx:attributes>
	</tx:advice>
 
	<!-- c. 定義事務入口(那些類的哪些方法參與事務) -->
	<aop:config>
		<aop:pointcut expression="execution(* com.cn.tch.spring_hibernate2.*.*(..))"
			id="pt" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
	</aop:config>

下面也給出幾種傳播特性的解釋:

1. PROPAGATION_REQUIRED: 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇;

2. PROPAGATION_SUPPORTS: 支持當前事務,如果當前沒有事務,就以非事務方式執行;

3. PROPAGATION_MANDATORY: 支持當前事務,如果當前沒有事務,就拋出異常;

4. PROPAGATION_REQUIRES_NEW: 總是開啓一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起;

5. PROPAGATION_NOT_SUPPORTED: 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起;

6. PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則拋出異常;

7. PROPAGATION_NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。
第二種方式,使用代理進行配置

<!-- 配置事務容器 -->
	<bean id="transactionManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	 <tx:annotation-driven transaction-manager="transactionManager" /> 
	<!-- 定義事務規則 -->
	<bean id="transactionProxy"
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
		abstract="true">
		<property name="transactionManager" ref="transactionManager" />
		<property name="transactionAttributes">
			<props>
				<!-- ,回滾爲-,不回滾爲+ -->
				<prop key="update">PROPAGATION_SUPPORTS,-Exception</prop>
			</props>
		</property>
	</bean>
	<!-- 定義事務入口 -->
	<bean id="deptProxy" parent="transactionProxy">
		<property name="target" ref="deptDao"></property>
	</bean>

在使用代理管理事務時,如果需要使用到getCurrentSession()這個方法,需要設置(這裏所說的有點像是第四種管理方式的配置,有可能代理模式不支持使用getCurrentSession()):

   <tx:annotation-driven transaction-manager="transactionManager" />  

同時需要在service上添加@Transactional,不然會報錯:org.hibernate.HibernateException: No Session found for current thread

第三種方式,使用攔截器

	<!-- 配置事務容器 -->
	<bean id="transactionManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	<!-- 定義事務規則 -->
	<bean id="transactionInterceptor"
		class="org.springframework.transaction.interceptor.TransactionInterceptor">
		<property name="transactionManager" ref="transactionManager" />
		<property name="transactionAttributes">
			<props>
				<!-- 回滾爲-,不回滾爲+ -->
				<prop key="update">PROPAGATION_REQUIRED,-Exception</prop>
				<prop key="modify*">PROPAGATION_REQUIRED,+MyException</prop>
               
			</props>
		</property>
	</bean>
	<!-- 定義事務入口 -->
	<bean id="proxyFactory"
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="interceptorNames">
			<list>
				<value>transactionInterceptor</value>
			</list>
		</property>
		<property name="beanNames">
			<list>
				<value>deptDao</value>
			</list>
		</property>
	</bean>

第四種方式,使用註解方式

第一步:在 xml 配置文件中添加事務配置信息。除了用配置文件的方式,@EnableTransactionManagement 註解也可以啓用事務管理功能

<!-- 配置hibernate的事務管理器 -->
    <!-- 配置事務容器 -->
    <bean id="transactionManager"
          class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 配置開啓【註解事務】處理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

第二步:在service類或者方法上面添加@Transactional註解。

  • @Transactional 註解添加到類級別上時,表示所有該類的公共方法都配置相同的事務屬性信息;
  • 方法級別如果也配置了@Transactional,應用程序會以方法級別的事務屬性信息來管理事務,換言之,方法級別的事務屬性信息會覆蓋類級別的相關配置信息。
  • @Transactional 註解的常用屬性信息:
屬性名 說明
name 當在配置文件中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器
propagation 事務的傳播行爲,默認值爲 REQUIRED。
isolation 事務的隔離度,默認值採用 DEFAULT。
timeout 事務的超時時間,默認值爲-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
readOnly 指定事務是否爲只讀事務,默認值爲 false;爲了忽略那些不需要事務的方法,比如讀取數據,可以設置 read-only 爲 true。
rollbackFor 用於指定能夠觸發事務回滾的異常類型,如果有多個異常類型需要指定,各類型之間可以通過逗號分隔。
noRollbackFor 拋出 no-rollback-for 指定的異常類型,不回滾事務。
  • 屬性使用舉例:@Transactional(noRollbackFor=ProcessException.class)那他是什麼意思呢?一句話,在你聲明的這個事物裏如果發生了ProcessException這個異常就noRollBack,就是數據庫事務不發生回滾。就這麼簡單

縱觀以上四種在Spring中配置Hibernate事務的方法,其核心都是一樣的,不同的只是實現的方式而已。所以看到這,這篇博文中你只需要記住4句話,就可以輕鬆理解在Spring中配置Hibernate事務的核心:

         1.配置SessionFactory

         2.配置事務容器

         3.配置事務規則

         4.配置事務入口

Spring的註解方式實現事務管理機制擴展閱讀:

https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

(如果對 Spring 中的 @transaction 註解的事務管理理解的不夠透徹,就很容易出現錯誤,比如事務應該回滾(rollback)而沒有回滾事務的問題。)

示例完整spring配置文件內容:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mzj.spring</groupId>
  <artifactId>spring-hibernate</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>spring-hibernate</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>

    <spring.version>4.3.23.RELEASE</spring.version>
    <hibernate.version>5.1.17.Final</hibernate.version>
    <mysql.version>5.1.47</mysql.version>
    <c3p0.version>0.9.2.1</c3p0.version>
    <junit.version>4.12</junit.version>
    <lombok.version>1.18.6</lombok.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>

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

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

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

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>

    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>${c3p0.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

測試類(注意@Test中事務默認是回滾的,因此採用main函數進行測試):

package com.mzj.spring;

import com.mzj.spring.dao.UsersDao;
import com.mzj.spring.pojo.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class TestTransaction {

    @Autowired
    private UsersDao userDao;

    @Transactional(noRollbackFor = RuntimeException.class)
    public void insert(){
        Users users = new Users();
        users.setAge(18);
        users.setUserName("路飛55");
        this.userDao.insertUsers(users);
        if (true){
            throw new RuntimeException();
        }

        Users users2 = new Users();
        users2.setAge(33);
        users2.setUserName("路飛66");
        this.userDao.insertUsers(users2);
    }

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

        TestTransaction testTransaction = context.getBean(TestTransaction.class);

        testTransaction.insert();
    }
}

完整工程地址:E:\01.study\04.spring\04.integration\hibernate\springhibernate

由於比較簡單,沒有上傳github

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章