18、Grails开发:数据验证、表关系管理与Web服务实现

Grails开发:数据验证、表关系管理与Web服务实现

1. 自定义默认模板

在Grails中,可以完全自定义脚手架视图的外观和控制器的默认行为。操作步骤如下:
1. 在命令行输入 grails install-templates
2. 查看 src/templates 目录,这里包含了可用于按需调整的起始材料。

2. 数据验证

2.1 静态约束块的使用

以下是一个 Book 类的示例,展示了静态约束块的使用:

class Book {
    static constraints = {
        title(blank:false, maxSize:50)
        author(blank:false)
        cover(inList:["Hardback", "Paperback", "PDF"])
        pages(min:0, max:1500)
        category(inList:["", "Technical", "Fiction", "Non-fiction"])
        excerpt(maxSize:5000)
    }
    String title
    String author
    Integer pages
    String cover = "Paperback"
    String category
    String excerpt
    String toString(){
        "${title} by ${author}"
    }
}

静态约束块不仅可以控制动态脚手架视图的字段顺序,还能对数据输入进行验证。

2.2 常见验证设置

设置 作用
blank true | false 防止字段为空
email true | false 检查是否为格式正确的电子邮件地址
inList [“a”, “b”, “c”] 显示一个下拉框
min, max number 数字字段的最小值和最大值
minSize, maxSize number 文本字段的最小和最大长度
unique true | false 防止数据库中出现重复值

2.3 验证方法

在保存对象之前,像 book.save() 这样的方法会对对象进行验证。也可以使用 book.validate() 方法在不保存到数据库的情况下手动检查验证结果,示例代码如下:

if(book.validate()) {
    // do something
}
else {
    book.errors.allErrors.each {
        println it
    }
}

2.4 错误显示

grails-app/views/book/create.gsp 中,有一段代码用于查找错误并在网页上显示:

<g:hasErrors bean="${book}">
    <div class="errors">
        <g:renderErrors bean="${book}" as="list" />
    </div>
</g:hasErrors>

2.5 自定义验证消息

可以在 grails-app/i18n/messages.properties 中添加自定义错误消息,示例如下:

default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
book.pages.min.notmet=Who are you trying to kid? No book could have [{2}] pages.

所有标准错误消息都已国际化并存储在 grails-app/i18n 中的Java属性文件中。可以通过安装 i18n-templates 插件进一步实现应用程序的国际化,命令为 grails install-plugin i18n-templates

3. 表关系管理

3.1 一对多关系

以下是 Publisher Book 类的示例,展示了一对多关系的实现:

// grails-app/domain/Publisher.groovy
class Publisher{
    static constraints = {
        name()
    }
    static hasMany = [books:Book]
    String name
    String toString(){
        return name
    }
}

// grails-app/domain/Book.groovy
class Book {
    static constraints = {
        title()
        author()
        pages()
        publisher()
    }
    static belongsTo = [publisher:Publisher]
    String title
    String author
    Integer pages
    String toString(){
        "${title} by ${author}"
    }
}

Publisher 类中使用 static hasMany 声明,在 Book 类中使用 static belongsTo 声明,即可创建一对多关系。如果要强制执行级联删除和更新,可以在 Book 类中指定 static belongsTo = Publisher

3.2 一对一关系

以下是 Author Address 类的示例,展示了一对一关系的实现:

// grails-app/domain/Author.groovy
class Author{
    static constraints = {
        name()
        address()
    }
    String name
    Address address
    String toString(){
        return name
    }
}

// grails-app/domain/Address.groovy
class Address{
    static constraints = {
        street()
        city()
        state()
        zipcode()
    }
    String street
    String city
    String state
    String zipcode
    String toString(){
        return "${street}, ${city}, ${state}, ${zipcode}"
    }
}

Author 类添加一个 Address 字段即可创建一对一关系。为了确保真正的一对一关系,可以添加唯一约束。如果想将 Author Address 保存到同一表中,可以在 Author 类中添加 static embedded = ["address"]

3.3 多对多关系

以下是 Book Author 类的示例,展示了多对多关系的实现:

// grails-app/domain/Book.groovy
class Book {
    static belongsTo = Author
    static hasMany = [authors:Author]
    String title
    Author author
}

// grails-app/domain/Author.groovy
class Author {
    static hasMany = [books:Book]
    String name
}

GORM会创建一个关联表,并添加外键关联到 Book Author 表。但动态脚手架不会在HTML中显示这种关系,需要手动编写GSP页面。

3.4 映射类到遗留数据库

在处理遗留应用程序时,可以使用静态映射块将类名和属性名映射到表名和列名。示例如下:

class Magazine{
    String title
    String editorInChief
    static mapping = {
        table 'periodical'
        columns {
            id column:'periodical_id'
            title column:'publication_name'
            editorInChief column:'person_in_charge'
        }
    }
}

4. Grails与Web服务

4.1 返回XML

4.1.1 单个书籍的XML返回
import grails.converters.*
class BookController {
    // return a single book as xml
    def showXml = {
        render Book.get( params.id ) as XML
    }
}
4.1.2 书籍列表的XML返回
// return a list of books as xml
def listXml = {
    def list = Book.list(params)
    render(contentType:"text/xml"){
        books{
            for(b in list){
                book(id:b.id){
                    title(b.title)
                    author(b.author)
                    pages(b.pages)
                    unescaped << "<!-- coverPrice is coming in the next version -->"
                }
            }
        }
    }
}

如果需要自定义XML输出,可以使用 StreamingMarkupBuilder

4.2 返回JSON

4.2.1 单个书籍的JSON返回
import grails.converters.*
class BookController {
    // return a single book as json
    def showJson = {
        render Book.get( params.id ) as JSON
    }
}
4.2.2 书籍列表的JSON返回
// return a list of books as json
def listJson = {
    def list = Book.list(params) as Book[]
    render list as JSON
}

使用 as JSON 转换器可以将POGO对象或数组转换为JSON格式。

4.3 返回Excel电子表格

class BookController {
    def listExcel = {
        def list = Book.list(params)
        render(contentType:"application/vnd.ms-excel") {
            html{
                body{
                    h1("Books")
                    table{
                        // table header
                        tr{
                            th("ID")
                            th("Title")
                            th("Author")
                            th("Pages")
                        }
                        //table body
                        for(b in list) {
                            tr{
                                td(b.id)
                                td(b.title)
                                td(b.author)
                                td(b.pages)
                            }
                        }
                    }
                }
            }
        }
    }
}

通过设置 contentType application/vnd.ms-excel ,可以让浏览器将响应作为Excel文档处理。如果想让浏览器显示“另存为”对话框,可以设置 Content-Disposition 头:

def listExcel = {
    def list = Book.list(params)
    response.setHeader("Content-Disposition", "attachment; filename=foo.xls")
    render(contentType:"application/vnd.ms-excel") {
        // ...
    }
}

4.4 设置Atom Feed

以下是一个设置Atom Feed的示例:

class ItemController {
    def atom = {
        def itemList = Item.list( params )
        def df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'-07:00'")
        // feed header
        def updated = df.format(itemList[0].datePosted)
        def feedHeader = """<feed xmlns="http://www.w3.org/2005/Atom">
            <title type="text">aboutGroovy.com</title>
            <link rel="alternate" type="text/html" href="http://aboutGroovy.com"/>
            <link rel="self" type="application/atom+xml" href="http://aboutGroovy.com/item/atom" />
            <updated>${updated}</updated>
            <author><name>Scott Davis</name></author>
            <id>tag:aboutgroovy.com,2006-12-18:thisIsUnique</id>
            <generator uri="http://aboutGroovy.com" version="0.0.2">
                Hand-rolled Grails code
            </generator>
        """
        // feed body
        StringBuffer feed = new StringBuffer()
        itemList.each{item ->
            def sw = new java.io.StringWriter()
            def x = new groovy.xml.MarkupBuilder(sw)
            x.entry(xmlns:"http://www.w3.org/2005/Atom"){
                author{name("Scott Davis")}
                published(df.format(item.datePosted))
                updated(df.format(item.datePosted))
                link(href:"http://aboutGroovy.com/item/show/${item.id}", rel:"alternate", title:item.title, type:"text/html")
                id("tag:aboutgroovy.com,2006:/item/show/${item.id}")
                title(type:"text", item.title)
                content(type:"xhtml"){
                    div(xmlns:"http://www.w3.org/1999/xhtml"){
                        p("Category: ${item.type}")
                        p{a(href:item.url, "Original Source")}
                        p(item.shortDescription)
                        p(item.description)
                    }
                }
            }
            feed.append(sw.toString() + "\n")
        }
        // feed footer
        def feedFooter = "</feed>"
        response.setContentType("application/atom+xml")
        render "${feedHeader}${feed}${feedFooter}"
    }
}

设置Atom Feed的流程如下:
1. 从表中提取 Item 并排序。
2. 格式化日期。
3. 生成Feed头部。
4. 遍历 Item 列表生成Feed主体。
5. 生成Feed尾部。
6. 设置响应的内容类型并渲染Feed。

通过以上内容,我们了解了Grails中数据验证、表关系管理以及各种Web服务的实现方法,这些功能可以帮助开发者更高效地构建强大的Web应用程序。

5. 总结与拓展

5.1 关键技术点回顾

  • 数据验证 :通过静态约束块 constraints 对数据进行验证,支持多种验证设置,如 blank email inList 等。验证方法有 save() validate() ,还能自定义验证消息。
  • 表关系管理 :利用 hasMany belongsTo 实现一对多、一对一和多对多关系。在处理遗留数据库时,可使用静态映射块进行类与表的映射。
  • Web服务实现 :能返回XML、JSON、Excel电子表格和设置Atom Feed。返回XML和JSON时,使用 as XML as JSON 转换器;返回Excel表格利用HTML和HTTP技巧;设置Atom Feed需按特定流程生成头部、主体和尾部。

5.2 技术应用场景

技术 应用场景
数据验证 表单提交时确保数据的有效性,如注册表单、订单表单等。
表关系管理 构建数据库模型,如电商系统中的商品与分类、用户与订单关系。
返回XML 与其他系统进行数据交互,如RESTful API。
返回JSON 前端与后端的数据交互,如单页面应用(SPA)。
返回Excel电子表格 数据导出,如报表生成。
设置Atom Feed 内容发布,如博客文章、新闻资讯的订阅。

5.3 拓展建议

  • 性能优化 :在处理大量数据时,可考虑使用缓存技术,如Redis,减少数据库查询次数。
  • 安全性提升 :对返回的XML和JSON数据进行加密处理,防止数据泄露。在处理用户输入时,进行严格的输入验证和过滤,防止SQL注入和XSS攻击。
  • 功能扩展 :结合第三方插件,如Spring Security实现用户认证和授权,提高应用的安全性和功能完整性。

5.4 流程图总结

graph LR
    A[Grails开发] --> B[数据验证]
    A --> C[表关系管理]
    A --> D[Web服务实现]
    B --> B1[静态约束块]
    B --> B2[验证设置]
    B --> B3[验证方法]
    B --> B4[自定义消息]
    C --> C1[一对多关系]
    C --> C2[一对一关系]
    C --> C3[多对多关系]
    C --> C4[映射到遗留数据库]
    D --> D1[返回XML]
    D --> D2[返回JSON]
    D --> D3[返回Excel]
    D --> D4[设置Atom Feed]

5.5 操作步骤总结

5.5.1 数据验证操作步骤
  1. 在类中定义 static constraints 块,设置验证规则。
  2. 使用 save() validate() 方法进行验证。
  3. grails-app/i18n/messages.properties 中添加自定义验证消息。
5.5.2 表关系管理操作步骤
  • 一对多关系 :在“一”方类中使用 static hasMany ,在“多”方类中使用 static belongsTo
  • 一对一关系 :在一方类中添加另一方类的字段,可添加唯一约束确保关系唯一性。
  • 多对多关系 :在双方类中使用 static hasMany
  • 映射到遗留数据库 :在类中使用 static mapping 块进行映射。
5.5.3 Web服务实现操作步骤
  • 返回XML :导入 grails.converters.* ,使用 render 方法和 as XML 转换器。
  • 返回JSON :导入 grails.converters.* ,使用 render 方法和 as JSON 转换器。
  • 返回Excel电子表格 :设置 contentType application/vnd.ms-excel ,使用 StreamingMarkupBuilder 构建HTML表格。
  • 设置Atom Feed :从表中提取数据并排序,格式化日期,生成头部、主体和尾部,设置响应内容类型并渲染。

通过对Grails开发中数据验证、表关系管理和Web服务实现的深入学习和实践,开发者可以更好地利用这些技术构建出功能强大、稳定可靠的Web应用程序。同时,不断拓展和优化这些技术的应用,能够满足更多复杂的业务需求。

已经博主授权,源码转载自 https://pan.quark.cn/s/053f1da40351 在计算机科学领域,MIPS(Microprocessor without Interlocked Pipeline Stages)被视作一种精简指令集计算机(RISC)的架构,其应用广泛存在于教学实践和嵌入式系统设计中。 本篇内容将深入阐释MIPS汇编语言中涉及数组处理的核心概念实用操作技巧。 数组作为一种常见的数据结构,在编程中能够以有序化的形式储存及访问具有相同类型的数据元素集合。 在MIPS汇编语言环境下,数组通常借助内存地址索引进行操作。 以下列举了运用MIPS汇编处理数组的关键要素:1. **数据存储**: - MIPS汇编架构采用32位地址系统,从而能够访问高达4GB的内存容量。 - 数组元素一般以连续方式存放在内存之中,且每个元素占据固定大小的字节空间。 例如,针对32位的整型数组,其每个元素将占用4字节的存储空间。 - 数组首元素的地址被称为基地址,而数组任一元素的地址可通过基地址加上元素索引乘以元素尺寸的方式计算得出。 2. **寄存器运用**: - MIPS汇编系统配备了32个通用寄存器,包括$zero, $t0, $s0等。 其中,$zero寄存器通常用于示恒定的零值,$t0-$t9寄存器用于暂存临时数据,而$s0-$s7寄存器则用于保存子程序的静态变量或参数。 - 在数组处理过程中,基地址常被保存在$s0或$s1寄存器内,索引则存储在$t0或$t1寄存器中,运算结果通常保存在$v0或$v1寄存器。 3. **数组操作指令**: - **Load/Store指令**:这些指令用于在内存寄存器之间进行数据传输,例如`lw`指令用于加载32位数据至寄存器,`sw`指令...
根据原作 https://pan.quark.cn/s/cb681ec34bd2 的源码改编 基于Python编程语言完成的飞机大战项目,作为一项期末学习任务,主要呈现了游戏开发的基本概念和技术方法。 该项目整体构成约500行代码,涵盖了游戏的核心运作机制、图形用户界面以及用户互动等关键构成部分。 该项目配套提供了完整的源代码文件、相关技术文档、项目介绍演示文稿以及运行效果展示视频,为学习者构建了一个实用的参考范例,有助于加深对Python在游戏开发领域实际应用的认识。 我们进一步研究Python编程技术在游戏开发中的具体运用。 Python作为一门高级编程语言,因其语法结构清晰易懂和拥有丰富的库函数支持,在开发者群体中获得了广泛的认可和使用。 在游戏开发过程中,Python经常Pygame库协同工作,Pygame是Python语言下的一款开源工具包,它提供了构建2D游戏所需的基础功能模块,包括窗口系统管理、事件响应机制、图形渲染处理、音频播放控制等。 在"飞机大战"这一具体游戏实例中,开发者可能运用了以下核心知识点:1. **Pygame基础操作**:掌握如何初始化Pygame环境,设定窗口显示尺寸,加载图像和音频资源,以及如何启动和结束游戏的主循环流程。 2. **面向对象编程**:游戏中的飞机、子弹、敌人等游戏元素通常通过类的设计来实现,利用实例化机制来生成具体的游戏对象。 每个类都定义了自身的属性(例如位置坐标、移动速度、生命值状态)和方法(比如移动行为、碰撞响应、状态更新)。 3. **事件响应机制**:Pygame能够捕获键盘输入和鼠标操作事件,使得玩家可以通过按键指令来控制飞机的移动和射击行为。 游戏会根据这些事件的发生来实时更新游戏场景状态。 4. **图形显示刷新**:...
【顶级SCI复现】高比例可再生能源并网如何平衡灵活性储能成本?虚拟电厂多时间尺度调度及衰减建模(Matlab代码实现)内容概要:本文围绕高比例可再生能源并网背景下虚拟电厂的多时间尺度调度储能成本优化问题展开研究,重点探讨如何在保证系统灵活性的同时降低储能配置运行成本。通过构建多时间尺度(如日前、日内、实时)协调调度模型,并引入储能设备衰减建模,提升调度精度经济性。研究结合Matlab代码实现,复现顶级SCI论文中的优化算法建模方法,涵盖鲁棒优化、分布鲁棒、模型预测控制(MPC)等先进手段,兼顾风光出力不确定性需求响应因素,实现虚拟电厂内部多能源协同优化。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事新能源、智能电网、能源互联网领域的工程技术人员。; 使用场景及目标:① 掌握虚拟电厂多时间尺度调度的核心建模思路实现方法;② 学习如何将储能寿命衰减纳入优化模型以提升经济性;③ 复现高水平SCI论文中的优化算法仿真流程,服务于科研论文写作项目开发。; 阅读建议:建议结合文中提供的Matlab代码逐模块分析,重点关注目标函数设计、约束条件构建及求解器调用过程,配合实际案例数据进行调试验证,深入理解优化模型物理系统的映射关系。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值