返回介绍

6.1 使用 GORM 进行数据持久化

发布于 2025-04-21 21:10:09 字数 7168 浏览 0 评论 0 收藏

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。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。