Spring入門(四)— 整合Struts和Hibernate

来源:https://www.cnblogs.com/gdwkong/archive/2018/02/19/8453166.html
-Advertisement-
Play Games

本文主要介紹Sprint整合Struts和Hibernate的基本使用。 ...


一、Spring整合Struts

1. 初步整合

只要在項目裡面體現spring和 strut即可,不做任何的優化。

  • struts 環境搭建

    1. 創建action

    public class UserAction extends ActionSupport {
        public String save(){
            System.out.println("調用了UserAction的save方法~~!");
        }
    }
    1. 在src下配置struts.xml , 以便struts能根據請求調用具體方法

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 1. 導入約束 -->
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
    <struts>
        <package name="user" namespace="/" extends="struts-default">
            <!-- localhost:8080/項目名/user_save -->
            <action name="user_*" class="com.pri.web.action.UserAction" method="{1}"></action>
        </package>
    </struts>
    1. 在web.xml中配置前端控制器,以便strust抓住請求

      <!-- struts的前端控制器  | 前端控制總棧 -->
      <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      
      <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>  
      </filter-mapping>
  • spring 環境搭建

    1. 創建service

    2. 在src下創建applicationContext.xml 並且托管service實現類

    3. 在action裡面獲取工廠進而調用方法

2. 初步整合的問題

該小節講述的是: 上面初步整合遺留下來的問題。 

問題:

  1. 每次請求都會創建工廠,解析xml

    ​ 解決方案: 工具類 | 靜態代碼塊

    1. 工廠創建時機有點晚, 請求到來的時候才創建工廠

      ​解決方案: 讓工廠提前 ---- 項目發佈| 伺服器啟動 ----- 使用監聽器(ServletContextListener) ----- 不用我們編寫監聽器 (spring已經寫好了。) ---- 配置監聽器即可。

3. 進階整合

該小節講述的是:把上面初步整合出現的問題給解決了。

該階段整合的目標就是解決上面出現的兩個問題。

 xml裡面配置listener
 <!-- 註冊spring的監聽器 -->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <!-- 監聽器裡面會執行工廠的創建,創建工廠需要依賴xml文件,所以我們還得告訴它xml文件在哪裡 -->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
​
代碼裡面獲取工廠
​
    ApplicationContext context =WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext());

4. 進階整合的問題

該小節講述的是: action和service對接的問題。 已經不是工廠創建這類問題

public class UserAction  extends ActionSupport{
​
    public String save(){
        System.out.println("調用了UserAction的save方法~~·");
        
        ApplicationContext context =  WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext());
        UserService us = (UserService)context.getBean("userService");
        us.save();
        
        return NONE;
    }
}

分析以上代碼:

​ 其實action就是讓service幹活。但是我們想要讓service幹活,必須經過兩個動作:

    1. 先從工具類中獲取工廠
  2. 從工廠裡面拿service對象。

一個方法還好,如果action裡面有多個方法呢? 都得這麼寫!!

​ 以前的解決方案:

​ 工具類 | 靜態代碼塊

​ 現在學了spring的解決方案:

​ action z只有一個目的就是為了得到service。 service已經在spring容器裡面了。我要讓spring把service主動送過來。 所以這裡必須是註入。 要想完成註入,前提是托管action。 

​ 總結:

​ 把action交給spring托管

​ 把service註入到action中來。

5. 最終整合

把action的創建工作交給spring來完成。 註意的是: spring創建action的實例必須是多例的。

  • spring配置

<bean id="userAction" class="com.pri.web.action.UserAction" scope="prototype">
        <property name="userService" ref="userService"></property>
</bean><bean id="userService" class="com.pri.service.impl.UserServiceImpl"></bean>
  • struts.xml的配置

<!-- 為了能夠讓struts使用到spring創建好的action實例,這裡的class不要寫全路徑了,而是寫spring那邊托管action的id標識符 -->
<action name="user_*" class="userAction" method="{1}">
</action>

6. 最終整合的背後細節

說一說剛纔導入的jar包struts-spring-plugin.jar 它裡面的細

  1. struts-spring-plugin.jar 裡面有一個struts-plugin.xml , 其中有兩行關鍵的配置

<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
    
<!--  Make the Spring object factory the automatic default -->
<constant name="struts.objectFactory" value="spring" />
  1. 在struts的default.properties中也有以下兩個配置

### if specified, the default object factory can be overridden here
### Note: short-hand notation is supported in some cases, such as "spring"
###       Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
# struts.objectFactory = spring
​
### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name
  1. 其實struts.xml裡面的action ,class屬性也可以寫全路徑,也可以寫bean的標識符(id值).

struts 是這麼來的, 拿著這個id值去問spring的工廠要對象,如果找不到, 嘗試自己創建對象, 也就是把class的屬性看成一個類的全路徑地址。

<action name="user_*" class="com.pri.web.action.UserAction" method="{1}"></action>

spring的applicationContext.xml裡面配置如下,但是struts不用它,而且他是多例的,只用有的之後才會創建

<bean id="userAction" class="com.pri.web.action.UserAction" scope="prototype">
    <property name="userService" ref="userService"></property>
</bean>

既然都不用人家的實例,選擇自己創建,為什麼這個action裡面也會有service實例,也會調用setXXX方法呢?

原因就是在strust的defautl.properties裡面有一行關鍵的配置 , 它的特點是struts會按照名字去找spring的工廠要對象註入進來。前提條件是struts能找到spring的工廠,也就是說必須要依賴前面提到的struts-spring-plugin-xx.jar

### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name

二、Spring整合Hibernate

1. 初步整合

hibernate環境搭建

  1. 持久化類&映射文件

<hibernate-mapping>
      <class name="com.pri.bean.User" table="t_user">
          <id name="uid">
              <generator class="native"></generator>
          </id>
                
          <property name="username"/>
          <property name="password"/>
       </class>  
 </hibernate-mapping>
  1. hibernate核心配置文件

<hibernate-configuration>
    <session-factory>
            
        <!-- 可以寫三部分內容 -->
        <!-- 1. 核心必須 : 告訴hibernate連接什麼資料庫, 用什麼賬號 , 什麼密碼去連接-->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///user</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
                
        <!-- 2. 可選配置 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
                
        <!-- 3. 映射文件 -->
        <mapping resource="com/pri/bean/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

2. 初步整合的問題

  1. 來一次請求就會中一次sessionFactory的創建

    解決方法: 工具類 | 靜態代碼塊

  2. sessionFactory創建時機的問題 .當請求來的之後,才會創建sessionFactory.

解決方法: 提前創建sessionFactory  ----- 項目發佈| 伺服器啟動  ---  需要用到listener , 但是spring不再做出來新的監聽器了。
•~~~java
其實spring壓根不用再給hibernate做任何的監聽器。 struts已經有監聽器,它的監聽器用於捕獲項目發佈, 只要捕獲到了一定會解析spring的applicationContext.xml文件。所以spring建議hibernate的核心配置文件,就放到applicationContext.xml中來。
•~~~

3. 進階整合(保留hibernate核心配置文件)

該階段整合的目標是:在項目啟動的時候,創建hibernate的sessionFactory工廠。 需要在applicationContext.xml中配置LocalSessionFactoryBean.

  • applicationContext.xml中配置如下

<!-- 配置該類,spring會創建它的實例,並且裡面會包含瞭解析hibernate核心文件代碼以及創建SessionFactory -->
<bean id="sessionFactory"class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <!-- 要求指定hibernate核心文件所在位置 -->
     <property name="configLocations">
       <array>
            <value>classpath:hibernate.cfg.xml</value>
        </array>
     </property>
</bean>
...
<bean id="userDao" class="com.pri.dao.impl.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>
  • dao層代碼如下:

public class UserDaoImpl implements UserDao {
     //聲明sessionFactory
     private SessionFactory sessionFactory;
     //讓spring註入sessionFactory進來
     public void setSessionFactory(SessionFactory sessionFactory) {
           this.sessionFactory = sessionFactory;
     }
            
     @Override
     public void save() {
           System.out.println("調用了UserDaoImpl的save方法~~~");
           Session session = sessionFactory.openSession();
               
           Transaction transaction = session.beginTransaction();
                
           User user = new User();
           user.setUsername("bb");
           user.setPassword("123");
           session.save(user);
                
           transaction.commit();
           session.close();
           //sessionFactory.close();
      }
}

4. 進階整合(去掉hibernate核心配置文件)

該階段整合的目標是: 刪除掉hibernate.cfg.xml,但是它裡面的內容還是要有。 把這三部分內容放到spring裡面來寫。

<!-- 配置該類,spring會創建它的實例,並且裡面會包含瞭解析hibernate核心文件代碼以及創建SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 要求指定hibernate核心文件所在位置 -->
        <!-- <property name="configLocations">
                <array>
                    <value>classpath:hibernate.cfg.xml</value>
                </array>
            </property> -->
            
      <!-- 1. 核心必須配置  連接什麼資料庫,怎麼連資料庫-->
      <property name="dataSource" ref="dataSource"></property>
            
      <!-- 2. 可選配置 -->
      <property name="hibernateProperties">
            <props>
               <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
               <prop key="hibernate.show_sql">true</prop>
               <prop key="hibernate.format_sql">true</prop>
               <prop key="hibernate.hbm2ddl.auto">update</prop>
             </props>
      </property>
            
      <!-- 3. 映射文件導入 -->
      <property name="mappingResources">
          <array>
               <value>com/pri/bean/User.hbm.xml</value>
           </array>
       </property>
</bean>

5. 進階整合(細節)

  • 使用連接池

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClass}"></property
    <property name="jdbcUrl" value="${jdbcUrl}"></property
    <property name="user" value="${user}"></property
    <property name="password" value="${password}"></roperty>
</bean>
  • 使用jdbc.properties

  1. jdbc.properties:

driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///users
user=root
password=root
  1. xml:

<context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClass}"></property>
    <property name="jdbcUrl" value="${jdbcUrl}"></property>
    <property name="user" value="${user}"></property>
    <property name="password" value="${password}"></property>
</bean>
  • 多個映射文件的處理

以前的寫法:
    <property name="mappingResources">
        <array>
            <value>com/pri/bean/User.hbm.xml</value>
        </array>
    </property>
​
現在的寫法:
    <property name="mappingDirectoryLocations" value="classpath:com/pri/bean"/>

6. 最終整合

講述的是: 使用Hibernate模板 , 簡化我們dao層的CRUD操作

  1. 開啟事務

一定要開啟事務,否則會拋出異常

在applicationContext.xml中開啟事務

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
  1. service層代碼

@Transactional
public class UserServiceImpl implements UserService {}
  1. dao層代碼

public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
     @Override
     public void save() {
         User user = new User();
         user.setUsername("admin");
         user.setPassword("123456");
        
         getHibernateTemplate().save(user);
     }
}
  1. dao層一定要註入sessionFactory

<bean id="userDao" class="com.pri.dao.impl.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

三、 Hibernate模板

1. hibernate模板的API

  • save方法

@Override
public void save(User user) {
    getHibernateTemplate().save(user);
}
  • upate方法

public void update(User user) {
    getHibernateTemplate().update(user);
}
  • delete方法

public void delete(User user) {
    getHibernateTemplate().delete(user);
}
  • get方法

public User get(Integer id) {
    return getHibernateTemplate().get(User.class, id);
}
  • load方法

public User load(Integer id) {
    return getHibernateTemplate().load(User.class, id);
}
  • 查詢總條目數

@Override
public int findCount() {
        String hql = "select count(*) from User";
    //查詢的時候,返回總是list, 區別就是list裡面放的是什麼數據,因為查詢總數,回來肯定就是一個數字 
        //list.add(500)
        List<Long> list = (List<Long>) getHibernateTemplate().find(hql);
        if(list.size()>0){
            return list.get(0).intValue();
        }
        return 0;
}
  • 使用 HQL 方式查詢

    @Override
    public List<User> findByHQL() {
        
        //hql其實就是sql語句這種寫法的hibernate版本。  表名 --- 類名
        String hql = "from User";
        return (List<User>) getHibernateTemplate().find(hql);
    }
  • 使用QBC方式查詢

 @Override
 public List<User> findByQBC() {
        //QBC的方式是面向對象編程。 也就是我們再也不能寫sql語句, hql語句也寫不了。  對象。方法
        
        //這行離線對象對應的sql語句應該是  select * from user;
        //如果想知道離線對象更詳細的用法,以及它的作用。 請看 8號上午上課的11點左右。
        DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
//      criteria.add(Restrictions.like(propertyName, value))
        return (List<User>) getHibernateTemplate().findByCriteria(criteria);
 }

四、HibernateTemplate 懶載入的問題

  • 拋出異常

    could not initialize proxy - no Session

  • 原因

  spring管理這個session,也是放置到Theradlocal裡面去的。 只要在業務邏輯層提交了事務,那麼session就關閉掉。所以使用懶載入就會有一個問題,無法查詢,因為沒有了session。 為瞭解決這個問題,我們需要在web.xml中配置一個過濾器,以便讓session的關閉稍微推後、延遲點。  

  事實上spring自己整合了dao層的事務管理,也提供了dao層的模板支持,它為了確保這兩者使用的連接對象是同一個,就在底層使用ThreadLocal來存儲session連接 ,詳情請參看下麵的時序圖. 雖然該圖描述的是jdbc模板的技術,但是hibernate模板的技術也同樣適用。

  • 解決辦法:

    在web.xml中配置過濾器。這個過濾器會對來訪的請求進行過濾,以便知道哪些請求延遲關閉session。

註意: 要在struts的過濾器之前添加 否則無效 ,原因是struts的過濾器抓住請求後,後面不會給配置的這個過濾器了。

<filter>
    <filter-name>OpenSession</filter-name>
    <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
          
<filter-mapping>
    <filter-name>OpenSession</filter-name>
    <url-pattern>*.action</url-pattern> <!-- 匹配後面帶有.action的請求 -->
</filter-mapping>
  1. 有的同學可能會想,為什麼這裡過濾的是.action呢 ? 直接寫/可以不?

    其實也可以,但是/*能夠包含的範圍更廣,也就是不管是什麼請求,都會讓這個session延遲關閉,這就造成資源的浪費了。

  2. 有的同學可能還會想,我們的action訪問,不是會自動帶有.action的尾碼麽?

    其實這是一種錯誤的想法,我們在頁面上的寫的時候路徑是這樣的

    ​ localhost:8080/項目/user_save

    人家的那個action的尾碼規定,只是說,你在訪問action的時候可以帶上這個.action,也可以不帶,而不是說它會自己帶這個.action過來。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先,原標題是對那些只知道玩的成人說的。 Die With Me ========== Die With Me是一個超級無聊的比列時程式員開發的IOS的APP,有關這個APP大家可以自行 "百度" 。 不少人(包括我)都是通過"躺倒鴨"知道的DWM,我是一初二學生,買不起IPhone,用國產Andr ...
  • js
    1、js:JavaScript一種直譯式腳本語言(解釋型腳本語言,執行前不需要編譯;這一點和Java類似,Java也是解釋型語言,源碼變為位元組碼(jvm可執行的代碼)的過程不是編譯過程),是一種動態類型、弱類型、基於原型的語言,內置支持類型。它的解釋器被稱為JavaScript引擎,為瀏覽器的一部分 ...
  • timeChunk函數讓創建節點的工作分批進行,比如一秒鐘創建1000個節點,改為每個200ms創建10個節點。具體timeChunk函數封裝如下 應用實例見https://92node.com/article/js-fen-shi.html ...
  • 上一篇聊了聊構建分散式系統所面臨的困難,這篇將著重討論構建容錯分散式系統的演算法與協議。構建容錯系統的最佳方法是使用通用抽象,允許應用程式忽略分散式系統中的一些問題。本篇我們先聊一聊線性一致性,以及與線性一致性有關的技術,後續需要瞭解的分散式協調服務,如:ZooKeeper等,都是基於分散式系統的線性 ...
  • 本文簡要地示範瞭如何使用java CardLayout對程式進行佈局。 ...
  • Problem Link: http://codeforces.com/problemset/problem/888/F Problem Statement: There are n points marked on the plane. The points are situated in suc ...
  • 針對本文,博主最近在寫《成神之路系列文章》 ,分章分節介紹所有知識點。歡迎關註。 一、基礎篇 1.1 JVM 1.1.1. Java記憶體模型,Java記憶體管理,Java堆和棧,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 http://ifeve.com/ ...
  • 前言 本文配套代碼:https://github.com/TTGuoying/ThreadPool 先看看幾個概念: 我們為什麼要使用線程池呢? 簡單來說就是線程本身存在開銷,我們利用多線程來進行任務處理,單線程也不能濫用,無止禁的開新線程會給系統產生大量消耗,而線程本來就是可重用的資源,不需要每次 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...