Grails 开发手册

Grails 开发手册

1. 简介 1

2. 安装 Grails 1

3. 基本命令 1

4. 目录结构 2

5. Domain 2

6. Controller 5

7. Service 7

8. Filter 8

9. Plugin 10

10. 整合前端框架 JQuery EasyUI 11

11. 配置说明 11

12. 碰到的问题 14

1. 简介

Grails 是根据约定优于配置的原则来搭建,整个框架其实也是对java比较流行的框架进行了封装,MVC部分是用的Spring MVC,持久层则是用的Hibernate。但整个开发的模式还有很大的变化,它的开发语言是Groovy,并且不需要自己去写数据访问层来实现基本CRUD功能,这些都已经包含在Domain里。

2. 安装 Grails

a) 下载 http://grails.org/download

b) 创建一个GRAILS_HOME的环境变量

c) 将 $GRAILS_HOME/bin 添加到PATH中

安装完成后可以输入grails –version进行检查。

3. 基本命令

d) 创建项目

grails create-app [appName]

生成一个基础的Grails项目目录。

e) grails create-domain-class [className]

创建一个Domain类

f) grails create-controller [controllerName]

创建一个Controller类

g) grails create-service [serviceName]

创建一个service类

h) grails compile

编译Groovy 及 Java 源码

i) clean

清除编译文件,在idea下开发Grails项目时新加jar包后可能会出现项目启动缺少jar包的情况,需要clean一下。

j) grails list-plugins

列出所有可用的插件

k) grails install-plugin [pluginName]

安装插件

l) grails uninstall-plugin  [pluginName]

删除已安装的插件

m) grails war

将项目打成war包,方便部署到web应用服务器

 

4. 目录结构

l grails-app 项目源码的顶级目录

l conf 配置文件

l controllers 控制器文件

l domain 域文件

l i18n 国际化支持

l services 服务层文件

l taglib 页面自定义标签库

l views 视图层文件

l lib   jar包

l scripts 脚本文件

l src  Groovy与Java源文件

l test 单元测试文件

5. Domain

n) 定义属性

i. 例子

class Person {      

        String name

        Integer age

        Date lastVisit

}

 

o) CRUD操作

i. Create新增

def p=new Person(name:"Fred", age:40, lastVisit:new Date())

p.save()

 

ii. Read读取

Grails 是为domain类默认一个id属性。

def p = Person.get(1)

加载一个只读的对象

def p = Person.read(1)

iii. Update更新

def p = Person.get(1)

p.name = "Bob"

p.save()

iv. Delete删除

def p = Person.get(1)

p.delete()

p) 关联

i. one-to-one

class Face {

    Nose nose

}

class Nose {       

        Face face

}

 

ii. one-to-many

class Author {                              

static hasMany = [ books : Book ]

String name

}

class Book {

String title

 }

对于 hasMany 设置,Grails将自动注入一个java.util.Set类型的属性到domain类。 用于迭代集合:

def a = Author.get(1)

a.books.each { println it.title }

默认的级联行为是级联保存和更新,但不删除,除非 belongsTo 被指定:

class Author {    

static hasMany = [ books : Book ]

String name

}

class Book {

static belongsTo = [author:Author]

String title

}

iii. many-to-many

 

通过在关联双方定义 hasMany ,并在关联拥有方定义 belongsTo

 

 

class Book {  

 static belongsTo = Author

 static hasMany = [authors:Author]   

 String title

}

class Author {   

static hasMany = [books:Book]   

String name

}

 

 

在这种情况下,Author 负责持久化关联,并且是唯一可以级联保存另一端的一方 。例如,下面这个可以进行正常级联保存工作:

 

 

new Author(name:"Stephen King").addToBooks(new Book(title:"The Stand")).addToBooks(new Book(title:"The Shining")).save()

 

 

iv. domain 映射配置

static mapping = {

        id column:'goods_id'  //id字段与数据库goods_id字段关联

        goodsSuppCode column: 'goodssuppcode'

        goodsSuppName column: 'goodssuppname'

        goodsSuppLink column: 'goodssupplink'

        table 'who_goods'  //表名(默认是类名)

        version false //是否加version字段,用于乐观锁

        datasources(['brand','home']) //数据源配置

}

 

v. domain 数据验证

在domain中对属性设置一些验证规则,在执行save操作时会进行验证。如:

static constraints = {

        tmallId(unique: true) //唯一性

        goodsSn(blank: true) //可否为空

  login(size:5..15, blank:false, unique:true)

  password(size:5..15, blank:false)

  email(email:true, blank:false) age(min:18, nullable:false)    

}

 

vi. 基于domain的查询

1) 使用Hibernate CriteriaBuilder 进行查询,例子如下:

def searchClosure = {

if (params.qStatus) {

       eq('status',Integer.parseInt(params.qStatus))

             }         

}//过滤条件status  = 传入的qStatus

def c = GoodsTmallInfo.createCriteria()

def relates = c.list(params, searchClosure)

2) 使用domain中的find进行查询

 

GoodsTmallRelate.findAllByIsAvaliableAndUpdateTimeLessThanEquals(1,time,[offset:0,max:20,sort:"updateTime",order: "desc"])

6. Controller

控制器处理请求并返回响应,调用业务逻辑返回视图或者文本。控制器位于 grails-app/controllers 目录下,创建控制器的命令是 grails create-controller User,这就创建了一个控制器类UserController

class UserController {

    def index() { }

}

一个控制器里可以定义多个操作,用于处理不同的请求url。默认情况下,一个控制器名和一个操作名组合而成的url,对应了相应的控制器和处理方法,比如 /user/list对应的处理类就是UserController和处理方法list。也可以通过修改配置文件grails-app/conf/UrlMappings.groovy来修改默认的映射关系。默认情况下,每次请求都会新建一个Controller实例,也可以修改默认的生命周期。

3) 控制器中可用的变量

a) request – http请求对象

b) response – http响应对象

c) session – 会话对象

d) servletContext -  ServletContext 对象,整个应用共享的对象

e) flash – flash对象,该对象的生命周期是两次请求,下次请求到来后即消失

f) params – 请求参数对象,可以从中获取参数

g) actionName – 当前正在被调用的操作名

h) controllerName – 当前正在被调用的控制器名

i) grailsApplication – 当前正在运行的应用对象,可以从中获取application.properties文件中的配置

j) applicationContext – Spring上下文对象

4) 请求转发渲染render

a) 直接返回文本  renders “Hello”

b) 转发到一个视图,并且传入model参数。render(view: "viewName", model: [book: theShining])

c) 转发到一个视图,并且以当前控制器作为model参数。render(view: "viewName")

d) 返回json格式的文本。render Book.list(params) as JSON

e) 返回xml格式的文本。render Book.get(params.id) as XML

5) 重定向redirect

a) 重定向到当前控制器的一个操作 :redirect(action: "show"),表示重定向到当前控制器的show方法

b) 重定向到某个控制器的某个方法:redirect(controller: "book", action: "list")

c) 带参数的重定向:redirect(action: "show", id: 4, params: [author: "Stephen King"])

d) 重定向到uri:redirect(uri: "book/list")

e) 重定向到其他域名:redirect(url: "http://www.baidu.com")

6) URL映射

默认情况下一个url:/controller/action/id对应了处理的控制器和处理方法,以及传入的id参数。通过修改grails-app/conf/UrlMappings.groovy文件来修改默认配置。

a) 映射到控制器和操作:"/product"(controller: "product", action: "list")

b) 映射到uri"/hello"(uri: "/hello.dispatch")

c) 映射到视图:"/hello"(uri: "/hello.dispatch"),直接映射到GSP文件grails-app/views/index.gsp 

d) 映射到响应码

static mappings = {

   "403"(view: "/errors/forbidden")

   "404"(view: "/errors/notFound")

   "500"(view: "/errors/serverError")

}

http响应码为不同的数字时,对应不同的gsp页面。

e) 映射到http方法:

static mappings = {

   "/product/$id"(controller:"product") {

       action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]

   }

}

当请求方法为GET时对应的处理方法为show,PUT对应的处理方法是update,以此类推。

 

 

7. Service

一般复杂的业务逻辑处理放在服务层,在Controller中调用。

创建服务的命令是grails create-service user

该命令在目录grails-app/services/下创建一个服务Class,UserService.groovy

默认情况下,grails只创建一个实例,并且调用方法是非同步的。grails自动定义一个bean,实例名即是将类名首字母小写的字符串。可以定义scope变量来设置Service的生命周期。

· prototype – 每次调用都创建一个新的实例

· request –每一个请求创建一个新的实例

· flash – 只有当前请求和下次请求对以一个实例

· flow – 同一个webflow对以一个实例

· conversation – 同一个webflow会话对以一个实例

· session – 同一个用户会话对应一个实例

· singleton (default) – 整个应用公用一个实例,默认

事务处理:默认情况下,Service中的每一个方法都在同一个事务管理之下,默认事务的传播级别是PROPAGATION_REQUIRED。可以通过设置变量

static transactional = false 取消事务。

注意事项:

只有通过依赖注入的Service才有事务处理,即只有通过def userService这种方式获取才有事务处理,如果是通过New UserService()新建的Service对象没有事务处理。

l Service中的方法只有抛出java.lang.RuntimeException,事务才会回滚。即Service中抛出的异常必须是java.lang.RuntimeException的子类,而不能是java.lang. Exception子类,否则事务将失效。

Service调用:可以直接在Controller中引用Service

class UserController {

    def userService

    …

}

也可以在其他的Servcie或者Domain中直接引用。

也可以在java中引用Service bean,通过grails-app/conf/spring/resources.xml中依赖注入

<bean id="bookConsumer" class="bookstore.BookConsumer">

    <property name="userStore" ref="userService" />

</bean>

 

groovy sql事务:

def sql = new Sql(dataSource_brand)

sql.withTransaction {

            sql.executeUpdate("update who_goods_stock g set g.num = g.num - :outNum where g.num - g.lock_num >= :outNum and g.id = :stockid",[outNum:params.outNum,stockid:params.stockId])

        

        }

抛出异常时回滚

 

 

 

 

8. Filter

过滤器即servlet中的Filter,通过设置规则来限定某些url请求和响应必须经过Filter的拦截,一般用于登陆验证、编码转换或者日志处理等。过滤器是位于grails-app/conf 目录下的groovy类。Grails中创建过滤器的命令是

grails create-filters Security

class SecurityFilters {

    def filters = {

        loginCheck(controller: '*', action: '*') {

            before = {

                if (!session.user && !actionName.equals('login')) {

                    redirect(action: 'login')

                    return false

                }

            }

        }

    }

}

控制器Controller能使用的内置变量,在Filter中都可以使用。

过滤器匹配规则可以是按照controller名字或者action名字,也可以根据uri匹配

a) 根据控制器名和操作名:

all(controller: '*', action: '*') {

}

匹配所有的控制器和操作

b) 只匹配控制器:

justBook(controller: 'book', action: '*') {

}

只要控制器名为book即匹配

c) 使用反转关键字

notBook(controller: 'book', invert: true) {

}

只要控制器名不是book即匹配

d) 根据操作名匹配

saveInActionName(action: '*save*', find: true) {

}

只要操作名包含save即匹配

e) 组合匹配

actionBeginningWithBButNotBad(action: 'b*', actionExclude: 'bad*', find: true) {

}

操作名以b开头,但是不能以bad开头

f) 根据uri匹配

someURIs(uri: '/book/**') {

}

book开头的uri即可

g) 匹配所有的uri

allURIs(uri: '/**') {

}

h) 排除部分uri

all(controller:'*', action:'*',uriExclude:'/tbAuth/*') {

}

排出了以/tbAuth/开头的uri

 

关键字说明:

controller :控制器名,*表示所有控制器

controllerExclude:排出的控制器名

action:匹配的操作名

actionExclude :排除的操作名

regex :是否启用正则表达式,true或者false,默认true

uri :匹配的uri,如 /user/list

uriExclude:排除的uri,等价于uri:/user/list,invert:true

invert:是否反转规则,默认false

find :是否按照正则表达式匹配,默认true

注意:全部用*,不要用.*

 

过滤器可以在请求处理过程中多个不同位置进行拦截处理

拦截器类型:

before:请求到来后,操作执行之前执行

after:操作执行后,视图渲染之前执行,可以修改model参数

afterView:视图渲染之后,布局之前执行,可以处理异常参数

9. Plugin

q) 插件机制是Grails的一个重要特性,它提供了大量的插件可以对项目方便的进行扩展。

r) 关于插件的命令:

i. grails list-plugins 查看可用的插件

ii. grails install-plugin [插件名] 安装插件

iii. grails uninstall-plugin [插件名]  删除插件

s) 实例 使用Quartz定时插件

i. 首先安装Quartz 插件

ii. 安装完后会在grails-app目录下生成jobs目录

iii. 创建定时任务,grails create-job [任务名]

iv. 定义执行时间规则

1) 定义间隔时间:static triggers = {simple repeatInterval: 28800000l // 执行间隔 8小时}

2) 或用Cron表达式更灵活的配置:

static triggers = {cron name: 'myTrigger', cronExpression: "0 0/1 * * * ?"}

推荐一下Cron表达式在线生成器:

http://www.becron.com/

 

10. 整合前端框架 JQuery EasyUI

t) 在GSP页面引入 EasyUI的js包。

u) 在不修改Easy UI js包的前提下,修改Easy UI关于分页的参数与GSP页面默认的分页参数进行结合。

//分页参数

Integer num = params.rows as Integer//每页记录数

params.max = Math.min(num, 100) //限制每页最大100条

params.offset =  page == 1?0:(page-1)*num //开始条数

v) 返回json格式的数据,并调整为适应Easy UI的格式

render(contentType: "text/json") {

            rows = array { //列表内容

                for (GoodsTmallRelate b in relates) {

                    tmallInfo id:b.id,

                            goodsId: b.goodsId ,

                           

                }

            }

            total = relates.totalCount  //总记录数

        }

11. 配置说明

w) Config.groovy

i. 配置GSP页面修改实时生效,可区分环境进行分别配置

environments {

    development {

        grails.logging.jul.usebridge = true

        //使修改的页面实时生效

        grails.gsp.enable.reload = true

        def  classpath = "${appName}/WEB-INF/grails-app";

        grails.reload.location=classpath

    }

    production {

        grails.logging.jul.usebridge = false

    }

}

ii. log4j的相关配置

log4j = {

   appenders {

        rollingFile name:"file", maxFileSize:1024, file:"web-app/log/info.log"

        console name: 'stdout'  ,layout: pattern(conversionPattern: "%d [%p] %l%n%m%n%n")

        appender new DailyRollingFileAppender(

                name: "dailyFile",

                file: "web-app/log/info.log",

               datePattern: "'.'yyyy-MM-dd",

              layout: pattern(

                        conversionPattern:

                                "%d [%p] %l%n%m%n%n"

                )

        )

    }

    environments {

        development { //开发环境

            root {

                info  'stdout'

                additivity = true

            }

        }

        production {  //生产环境

            root {

                info 'stdout','dailyFile'

                additivity = true

            }

        }

        test {    //测试环境

            root {

                debug 'dailyFile'

                additivity = true

            }

        }

    }

error  'org.codehaus.groovy.grails.web.servlet',        // controllers

            'org.codehaus.groovy.grails.web.pages',          // GSP

            'org.codehaus.groovy.grails.web.sitemesh',       // layouts

            'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping

            'org.codehaus.groovy.grails.web.mapping',        // URL mapping

            'org.codehaus.groovy.grails.commons',            // core / classloading

            'org.codehaus.groovy.grails.plugins',            // plugins

            'org.codehaus.groovy.grails.orm.hibernate',      // hibernate integration

            'org.springframework',

            'org.hibernate',

            'net.sf.ehcache.hibernate'

}

iii. hibernate 缓存配置

grails.hibernate.cache.queries = false

默认不使用缓存,但可在重要需要缓存的查询后面加上 cache: true 进行缓存

x) DataSource.groovy  数据源配置文件

1.可以把数据库地址用户名等配置到一个properties文件中,方便部署后的更改。

def dataConf = PropertiesLoaderUtils.loadProperties(new ClassPathResource('application.properties'))

 

2. 可以分为开发环境,生产环境,测试环境分别配置相应的数据源。

 

3. 一个环境可以配置多个数据源,在domain类中声明可用于哪几个数据源。持久化操作时可以选择相应的数据源进行操作。

static mapping = {

        datasources(['brand','home']) //在domain中进行多数据源的配置

}

 

Goods.brand.get(id)  //使用时可以选择不同的数据源进行操作

4. 数据源配置可以开启使用连接池,默认使用DBCP连接池。

  pooled = true  //开启连接池

            properties{   //连接池相关配置

                maxActive = 50

                maxIdle = 25

                minIdle = 5

                initialSize = 5

                minEvictableIdleTimeMillis = 60000

                timeBetweenEvictionRunsMillis = 60000

                maxWait = 10000

                validationQuery = "/* ping */"

            }

 

y) UrlMappings.groovy

i. 配置访问连接与资源的映射。

默认为 http://domain/controller/action/param

z) resources.groovy

i. 定义一些需要spring控制的bean

 

12. 碰到的问题

aa) 不通过domain直接操作数据库

i. 直接在service里定义def dataSource变量,Grails会自动注入。

ii. 然后获取数据库链接 def conn = dataSource.getConnection()

iii. 通过jdbc操作数据库

1) PreparedStatement ps = conn.prepareStatement("SELECT s.shipping_id,s.`shipping_code`,s.`shipping_remark`,s.`logo_image`,s.`calc_rule`,s.`weight_limit`,s.`lenth_limit`,s.`first`,s.`additional`,sa.shipping_area_id,sa.first_price,s.shipping_name FROM `who_shipping` s RIGHT JOIN (SELECT * FROM `who_shipping_area` sa WHERE sa.`enabled`=1 AND sa.`shipping_area_id` IN (SELECT ar.`shipping_area_id` FROM `who_shipping_area_region` ar WHERE ar.`region_id` = ?)) AS sa ON s.shipping_id = sa.`shipping_id` ORDER BY s.`sort`");

2) ResultSet rs = ps.executeQuery();

ab) 定时任务重复执行

i. 通过自动部署工具部署Grails时,会发生Grails定时插件Quartz定时任务重复执行两次的情况。

1) 手工部署就没有这个问题

2) 修改tomcat的server.xml

a) <Host name="localhost" debug="0" appBase="" unpackWARs="true" autoDeploy="true">将 appBase留空。

13. 其它

a) 修改groovy代码后自动编译生效

i. Idea的Edit configurations 中配置VM options 如下:

 

-server

-javaagent:/opt/local/share/java/grails/lib/org.springsource.springloaded/springloaded-core/jars/springloaded-core-1.1.1.jar

-noverify

-Dspringloaded=profile=grails

 

黄色部分路径根据实际情况修改

 

1. 简介 2. 起步 2.1 下载并安装Grails 2.2 创建一个Grails应用 2.3 Hello World示例 2.4 使用IDE 2.5 规约配置 2.6 运行Grails应用 2.7 测试Grails应用 2.8 部署Grails应用 2.9 所支持的Java EE容器 2.10 创建工件 2.11 生成Grails应用 3. 配置 3.1 基本配置 3.1.1 内置选项 3.1.2 日志 3.2 环境 3.3 数据源 3.3.1 数据源和环境 3.3.2 JNDI数据源 3.3.3 自动数据库移植 3.4 外部配置 3.5 定义版本 4. 命令行 4.1 创建Gant脚本 4.2 可复用的Grails脚本 4.3 脚本中的事件 4.4 Ant和Maven 5. 对象关系映射(GORM) 5.1 快速指南 5.1.1 基本的CRUD 5.2 在GORM中进行领域建模 5.2.1 GORM中的关联 5.2.1.1 一对一 5.2.1.2 一对多 5.2.1.3 多对多 5.2.2 GORM的组合 5.2.3 GORM的继承 5.2.4 集合、列表和映射 5.3 持久化基础 5.3.1 保存和更新 5.3.2 删除对象 5.3.3 级联更新和删除 5.3.4 立即加载和延迟加载 5.3.4 悲观锁和乐观锁 5.4 GORM查询 5.4.1 动态查找器 5.4.2 条件查询 5.4.3 Hibernate查询语言 5.5 高级GORM特性 5.5.1 事件和自动实现时间戳 5.5.2 自定义ORM映射 5.5.2.1 表名和列名 5.5.2.2 缓存策略 5.5.2.3 继承策略 5.5.2.4 自定义数据库标识符 5.5.2.5 复合主键 5.5.2.6 数据库索引 5.5.2.7 乐观锁和版本定义 5.5.2.8 立即加载和延迟加载 5.6 事务编程 5.7 GORM和约束 6. Web层 6.1 控制器 6.1.1 理解控制器和操作 6.1.2 控制器和作用域 6.1.3 模型和视图 6.1.4 重定向和链 6.1.5 控制器拦截器 6.1.6 数据绑定 6.1.7 XML和JSON响应 6.1.8 上传文件 6.1.9 命令对象 6.2 Groovy Server Pages 6.2.1 GSP基础 6.2.1.1 变量和作用域 6.2.1.2 逻辑和迭代 6.2.1.3 页面指令 6.2.1.4 表达式 6.2.2 GSP标签 6.2.2.1 变量和作用域 6.2.2.2 逻辑和迭代 6.2.2.3 搜索和过滤 6.2.2.4 链接和资源 6.2.2.5 表单和字段 6.2.2.6 标签作为方法调用 6.2.3 视图和模板 6.2.4 使用Sitemesh布局 6.3 标签库 6.3.1 简单标签 6.3.2 逻辑标签 6.3.3 迭代标签 6.3.4 标签命名空间 6.4 URL映射 6.4.1 映射到控制器和操作 6.4.2 嵌入式变量 6.4.3 映射到视图 6.4.4 映射到响应代码 6.4.5 映射到HTTP方法 6.4.6 映射通配符 6.4.7 自动重写链接 6.4.8 应用约束 6.5 Web Flow 6.5.1 开始和结束状态 6.5.2 操作状态和视图状态 6.5.3 流执行事件 6.5.4 流的作用域 6.5.5 数据绑定和验证 6.5.6 子流程和会话 6.6 过滤器 6.6.1 应用过滤器 6.6.2 过滤器的类型 6.6.3 过滤器的功能 6.7 Ajax 6.7.1 用Prototype实现Ajax 6.7.1.1 异步链接 6.7.1.2 更新内容 6.7.1.3 异步表单提交 6.7.1.4 Ajax事件 6.7.2 用Dojo实现Ajax 6.7.3 用GWT实现Ajax 6.7.4 服务端的Ajax 6.8 内容协商 7. 验证 7.1 声明约束 7.2 验证约束 7.3 客户端验证 7.4 验证和国际化 8. 服务层 8.1 声明式事务 8.2 服务的作用域 8.3 依赖注入和服务 8.4 使用Java的服务 9. 测试 9.1 单元测试 9.2 集成测试 9.3 功能测试 10. 国际化 10.1 理解信息绑定 10.2 改变Locales 10.3 读取信息 11. 安全 11.1 预防攻击 11.2 字符串的编码和解码 11.3 身份验证 11.4 关于安全的插件 11.4.1 Acegi 11.4.2 JSecurity 12 插件 12.1 创建和安装插件 12.2 理解插件的结构 12.3 提供基础的工件 12.4 评估规约 12.5 参与构建事件 12.6 参与运行时配置 12.7 运行时添加动态方法 12.8 参与自动重载 12.9 理解插件加载的顺序 13. Web服务 13.1 REST 13.2 SOAP 13.3 RSS和Atom 14. Grails和Spring 14.1 Grails的支柱 14.2 配置其他Bean 14.3 通过Beans DSL运行Spring 14.4 配置属性占位 14.5 配置属性重载 15. Grails和Hibernate 15.1 通过Hibernate注释进行映射 15.2 深入了解 16. 脚手架
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值