讀古今文學網 > Spring Boot實戰 > 6.1 使用GORM進行數據持久化 >

6.1 使用GORM進行數據持久化

Grails裡最讓人著迷的恐怕就是GORM了。GORM將數據庫相關工作簡化到和聲明要持久化的實體一樣容易。例如,代碼清單6-1演示了閱讀列表裡的Book該如何用Groovy寫成GORM實體。

代碼清單6-1 GORM Book實體

package readinglist

import grails.persistence.*

@Entity          ←---這是一個GORM實體
class Book {

  Reader reader
  String isbn
  String title
  String author
  String description

}

  

就和Book的Java版本一樣,這個類裡有很多描述圖書的屬性。但又與Java版本不一樣,這裡沒有分號、publicprivate修飾符、setter和getter方法或其他Java中常見的代碼噪聲。是Grails的@Entity註解讓這個類變成了GORM實例。這個簡單的實體可幹了不少事,包括將對像映射到數據庫,為Book添加持久化方法,通過這些方法可以存取圖書。

要在Spring Boot項目裡使用GORM,必須在項目裡添加GORM依賴。在Maven中,<dependency>看起來是這樣的:

<dependency>
  <groupId>org.grails</groupId>
  <artifactId>gorm-hibernate4-spring-boot</artifactId>
  <version>1.1.0.RELEASE</version>
</dependency>

  

一樣的依賴,在Gradle裡是這樣的:

compile(\"org.grails:gorm-hibernate4-spring-boot:1.1.0.RELEASE\")

  

這個庫自帶了一些Spring Boot自動配置,會自動配置所有支持GORM所需的Bean。你只管寫代碼就好了。

GORM在Spring Boot裡的另一個選擇

正如其名,gorm-hibernate4-spring-boot是通過Hibernate開啟GORM數據持久化的。對很多項目而言,這很好。但如果你想用MongoDB,那你會對Spring Boot裡的MongoDB GORM支持很感興趣。

它的Maven依賴是這樣的:

<dependency>
  <groupId>org.grails</groupId>
  <artifactId>gorm-mongodb-spring-boot</artifactId>
  <version>1.1.0.RELEASE</version>
</dependency>

  

下面是相同的Gradle依賴:

compile(\"org.grails:gorm-mongodb-spring-boot:1.1.0.RELEASE\")

  

GORM的工作原理要求實體類必須用Groovy來編寫。我們已經在代碼清單6-1里寫了一個Book實體,下面再寫一個Reader實體,如代碼清單6-2所示。

代碼清單6-2 GORM Reader實體

package readinglist

import grails.persistence.*

import org.springframework.security.core.GrantedAuthority
import
    org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

@Entity                      ←---這是一個實體
class Reader implements UserDetails {

  String username
  String fullname
  String password

  Collection<? extends GrantedAuthority> getAuthorities {

    Arrays.asList(new SimpleGrantedAuthority(\"READER\"))
  }

  boolean isAccountNonExpired {   ←---實現了UserDetails
    true
  }

  boolean isAccountNonLocked {
    true
  }

  boolean isCredentialsNonExpired {
    true
  }

  boolean isEnabled {
    true
  }

}

  

現在,我們的閱讀列表應用程序裡有了兩個GORM實體,我們需要重寫剩下的應用程序來使用這兩個實體。因為使用Groovy是如此令人愉悅(和Grails十分相似),所以其他類我們也會用Groovy來編寫。

首先是ReadingListController,如代碼清單6-3所示。

代碼清單6-3 Groovy的ReadingListController

package readinglist

import org.springframework.beans.factory.annotation.Autowired
import
    org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.ResponseStatus

@Controller
@RequestMapping(\"/\")
@ConfigurationProperties(\"amazon\")
class ReadingListController {

  @Autowired
  AmazonProperties amazonProperties

  @ExceptionHandler(value=RuntimeException.class)
  @ResponseStatus(value=HttpStatus.BANDWIDTH_LIMIT_EXCEEDED)
  def error {
    \"error\"
  }

  @RequestMapping(method=RequestMethod.GET)
  def readersBooks(Reader reader, Model model) {
    List<Book> readingList = Book.findAllByReader(reader)   ←---查找讀者的全部圖書
    model.addAttribute(\"reader\", reader)
    if (readingList) {
      model.addAttribute(\"books\", readingList)
      model.addAttribute(\"amazonID\", amazonProperties.getAssociateId)
    }
    \"readingList\"
  }

  @RequestMapping(method=RequestMethod.POST)
  def addToReadingList(Reader reader, Book book) {
    Book.withTransaction {
      book.setReader(reader)
      book.save       ←---保存一本書
    }
    \"redirect:/\"
  }

}

 

這個版本的ReadingListController和第3章裡的相比,最明顯的不同之處在於,它是用Groovy寫的,沒有Java的那些代碼噪聲。最重要的不同之處在於,無需再注入ReadingListRepository,它直接通過Book類型持久化。

readersBooks方法裡,它調用了BookfindAllByReader靜態方法,傳入了指定的讀者信息。雖然代碼清單6-1沒有提供findAllByReader方法,但這段代碼仍然可以執行,因為GORM會為我們實現這個方法。

與之類似,addToReadingList方法使用了靜態方法withTransaction和實例方法save。這兩個方法也是GORM提供的,用於將Book保存到數據庫裡。

我們所要做的就是聲明一些屬性,在Book上添加@Entity註解。如果你問我怎麼看——我覺得這筆買賣很划算。

SecurityConfig也要做類似的修改,通過GORM而非ReadingListRepository來獲取Reader。代碼清單6-4就是新的SecurityConfig

代碼清單6-4 Groovy版本的SecurityConfig

package readinglist

import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.
                                  builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.
                                                  builders.HttpSecurity
import org.springframework.security.config.annotation.web.
                             configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.userdetails.UserDetailsService

@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {

  void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests
        .antMatchers(\"/\").access(\"hasRole(\'READER\')\")
        .antMatchers(\"/**\").permitAll
      .and
      .formLogin
        .loginPage(\"/login\")
        .failureUrl(\"/login?error=true\")
  }

  void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
      .userDetailsService(
        { username -> Reader.findByUsername(username) }    ←---根據用戶名查找讀者
        as UserDetailsService)
  }

}

  

除了用Groovy重寫,SecurityConfig裡最明顯的變化無疑就是第二個configure方法。如你所見,它使用了一個閉包(UserDetailsService的實現類),其中調用靜態方法findByUsername來查找Reader,這個功能是GORM提供的。

你也許會好奇——在這個GORM版本的應用程序裡,ReadingListRepository變成什麼了?GORM替我們處理了所有的持久化工作,這裡已經不再需要ReadingListRepository了,它的實現也都不需要了。我想你會同意代碼越少越好這個觀點。

應用程序中剩餘的代碼也應該用Groovy重寫,這樣才能和我們的變更相匹配。但它們和GORM沒什麼關係,也不在本章的討論範圍內。如果想要完整的代碼,可以到示範代碼頁面裡去下載。

此刻,你可以通過各種運行Spring Boot應用程序的方法來啟動閱讀列表應用程序。啟動後,應用程序應該能像從前一樣工作。只有你我知道持久化機制已經被改變了。

除了GORM,Grails應用程序通常還會用Groovy Server Pages將模型數據以HTML的方式呈現給瀏覽器。6.2節應用程序的Grails化還會繼續。我們會把Thymeleaf替換為等價的GSP。