??xml version="1.0" encoding="utf-8" standalone="yes"?>
class Something{
byte[] image
}
控制器中展示囄的方?/p>
def image= {
def something = Something.get( params.id )
byte[] image = something.image
response.outputStream << image
}
面展示
<img src="${createLink(controller:'something', action:'image', id: something.id)}"/>
?a >https://rome.dev.java.net/下蝲RomeQ之后把rome-xxx.jar攑ֈ你的grails目的lib目录下。这里xxx是版本号。比如我的是rome-1.0RC1.jar
再到http://www.jdom.org/下蝲JDom。之后同h把jdom.jar攑ֈlib目录下?/p>
创徏一个controllerQ当然你也可以在你已l有的controller里面增加相应Ҏ。这里我们创Z个叫做FeedController的类?br />
之后讉K相应的页面,比如?a >http://www.ondev.net/feed/rss可以了
原脓地址Q?http://www.ondev.net/story/show/75
To install the YUI plugin type this command from your project's root folder:
grails install-plugin yui
To use Grails' adaptive AJAX support just add the folowing line in the head section:
<g:javascript library="yui" />
<yui:javascript dir="calendar" file="calendar-min.js" /> <yui:javascript dir="calendar" file="calendar-min.js" version="2.5.2" /> // version to be used in case multiple version installed <yui:stylesheet dir="calendar/assets" file="calendar.css" />
By default only yahoo-dom-event.js and connection-min.js are included when using <g:javascript library="yui" />. Adding additional libraries to the default list can be done in a BootStrap (+) class:
import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLibclass BootStrap { def init = { servletContext -> JavascriptTagLib.LIBRARY_MAPPINGS.yui += ["yui/2.5.2/calendar/calendar-min", "yui/2.5.2/container/container-min"] } def destroy = { } }
import grails.util.GrailsUtil import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLibclass BootStrap { def init = { servletContext -> if (GrailsUtil.isDevelopmentEnv()) { JavascriptTagLib.LIBRARY_MAPPINGS.yui = ["yui/2.5.2/yahoo/yahoo-debug", "yui/2.5.2/dom/dom-debug", "yui/2.5.2/event/event-debug", "yui/2.5.2/connection/connection-debug"] } } def destroy = { } }
It's also possible to serve the javascript from the Yahoo! servers. First delete the yui folder from web-appjs after installing the plugin. Then, in a BootStrap class, override the mapping which contains the javascript files to include by default:
import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLibclass BootStrap { def init = { servletContext -> JavascriptTagLib.LIBRARY_MAPPINGS.yui = [] } def destroy = { } }
If you want to upgrade:
class BootStrap {
def init = { servletContext ->
def user = new Users(id:1,username:'rain'
,password:'1102')
user.password = user.password.encodeAsPassword()
user.save()
}
def destroy = { }
}Between those three, you should have enough to figure things out. I still found it hard, despite those instructions and so to avoid having to figure things out again some time in the future, I'll write absolutely everything here.
Creating the Plugin
grails create-plugin SamplePlugin
Now you have a Grails plugin. However, at the same time it is just another Grails application, which means you can simply open it in NetBeans IDE. (I.e., there is no import process and no NetBeans artifacts are added to the plugin in order to be able to open it in the IDE.)
The Files window (Ctrl-2) however, shows a lot more:
Open the "SamplePluginGrailsPlugin.groovy" file and there you see the following:
class SamplePluginGrailsPlugin { def version = 0.1 def dependsOn = [:] def doWithSpring = { // TODO Implement runtime spring config (optional) } def doWithApplicationContext = { applicationContext -> // TODO Implement post initialization spring config (optional) } def doWithWebDescriptor = { xml -> // TODO Implement additions to web.xml (optional) } def doWithDynamicMethods = { ctx -> // TODO Implement registering dynamic methods to classes (optional) } def onChange = { event -> // TODO Implement code that is executed when this class plugin class is changed // the event contains: event.application and event.applicationContext objects } def onApplicationChange = { event -> // TODO Implement code that is executed when any class in a GrailsApplication changes // the event contain: event.source, event.application and event.applicationContext objects } }
I.e., you have hooks for integrating your code into meaningful places in the plugin.
import org.codehaus.groovy.grails.validation.AbstractConstraint import org.springframework.validation.Errors class BestFrameworkConstraint extends AbstractConstraint { private static final String DEFAULT_MESSAGE_CODE = "default.answer.invalid.message"; public static final String NAME = "oneCorrectResponse"; private boolean validateConstraint //The parameter which the constraint is validated against: @Override public void setParameter(Object constraintParameter) { if (!(constraintParameter instanceof Boolean)) throw new IllegalArgumentException("Parameter for constraint [" + NAME + "] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean value"); this.validateConstraint = ((Boolean) constraintParameter).booleanValue() super.setParameter(constraintParameter); } //Returns the default message for the given message code in the current locale: @Override protected void processValidate(Object target, Object propertyValue, Errors errors) { if (validateConstraint && !validate(target, propertyValue)) { def args = (Object[]) [constraintPropertyName, constraintOwningClass, propertyValue] super.rejectValue(target, errors, DEFAULT_MESSAGE_CODE, "not." + NAME, args); } } //Returns whether the constraint supports being applied against the specified type: @Override boolean supports(Class type) { return type != null && String.class.isAssignableFrom(type); } //The name of the constraint, which the user of the plugin will use //when working with your plugin. @Override String getName() { return NAME; } //Validate this constraint against a property value, //In this case, ONLY "Grails" is valid, everything else will cause an error: @Override boolean validate(target, propertyValue) { propertyValue ==~ /^Grails$/ } }
def doWithSpring = { org.codehaus.groovy.grails.validation.ConstrainedProperty.registerNewConstraint( BestFrameworkConstraint.NAME, BestFrameworkConstraint.class); }
grails package-plugin
Back in the IDE, examine the ZIP file that the above command created:
That ZIP file is your Grails plugin.
Installing the Plugin
Now we will install our plugin in a new application.
Thanks to "convention over configuration", Grails knows exactly where everything is—so that, for example, the "plugin.xml" file that you see above, if found within the folder structure you see above, is the indicator to Grails that a plugin is available for use.
Using the Functionality Provided By the Plugin
Note: The "oneCorrectResponse" constraint that you see above is the name of the constraint defined in the plugin.
Congratulations, you've created, installed, and used your first Grails plugin!
static constraints = {
login(length:5..15,blank:false,unique:true)
password(length:5..15,blank:false)
email(email:true,blank:false)
age(min:new Date(),nullable:false)
}
}
constraints必须声明为static?/p>
同时Q每个属性的U束属性都有与之对应的错误消息QError message codeQ,当表单未能通过验证的时候,会q回q些错误消息?br />
q些错误消息?strong>grails-app/i18n/message.properties里定义?br />
例如我们要让User的email为空时返?Please enter your email"Q则可以在message.properties定义Q?br />
user.email.blank=Please enter your email
如果用户没有自定义错误消息,pȝ则会用默认的讄。当焉认的消息肯定不会是你惌?#8230;…
Grails提供很多验证属性,可以满一些基本的验证需求:
blank
验证属性能否ؓI,不允ؓI则设ؓfalse?br />
Note: 如果在form里ؓI提交,则属性的值是一个空字符Ԍ而不是null?br />
Example: login(blank:false)
Error message code: className.propertyName.blank
creditCard
如果要求属性ؓ信用卡号码,则设为true?br />
Example: cardNumber(creditCard:true)
Error message code: className.propertyName.creditCard.invalid
email
如果要求属性ؓemial地址Q则设ؓtrue?br />
Example: contactEmail(email:true)
Error message code: className.propertyName.email.invalid
inList
如果要求属性的值必Mؓ规定的|则定义规定的倹{?br />
Example: name(inList:["Joe", "Fred", "Bob"] )
Error message code: className.propertyName.not.inList
length
U束字符串或者数l的长度?br />
q个U束属性在0.5版本是被取消Q用size代替?/font>
Example: login(length:5..15)
Error message code:
className.propertyName.length.toolong
className.propertyName.length.tooshort
matches
应用正则表达式对字符串进行验证?br />
Example: login(matches:"[a-zA-Z]+")
Error message code: className.propertyName.matches.invalid
max
讑֮属性的最大|值的cd必须跟属性一栗?br />
Example:
age(max:new Date())
price(max:999F)
Error message code: className.propertyName.max.exceeded
maxLength
讑֮字符串或者数l的最大长度?br />
?.5版本中被取消Q由maxSize代替?/font>
Example: login(maxLength:5)
Error message code: className.propertyName.maxLength.exceeded
maxSize
讑֮一个数字或者集合的最大大?br />
?.5版本中不被徏议用在数字上Q改用max?/font>
Example: children(maxSize:25)
Error message code: className.propertyName.maxSize.exceeded
min
讑֮属性的最倹{类型必跟属性一致?br />
Example:
age(min:new Date())
price(min:0F)
Error message code: className.propertyName.min.notmet
minLength
讑֮字符串属性或者数l属性的最长度?br />
?.5版本中被取消Q由minSize代替?/font>
Example: login(minLength:5)
Error message code: className.propertyName.minLength.notmet
minSize
讑֮一个数字或者集合的最大?br />
?.5版本中不被徏议用在数字属性上Q改用min?/font>
Example: children(minSize:5)
Error message code: className.propertyName.minSize.notmet
notEqual
验证属性的值是否跟指定的值相{?br />
Example: login(notEqual:"Bob")
Error message code: className.propertyName.notEqual
nullable
如果属性不可以为nullQ则设ؓfalse?br />
Note: 如果在表单里未填M东西而提交时Q则作ؓrequest parameterQ属性的gؓ一个空字符Ԍ而不是null?br />
Example: age(nullable:false)
Error message code: className.propertyName.nullable
range
限制属性的值在指定的范围里?br />
Example: age(range:minAge..maxAge)
Error message code:
className.propertyName.range.toosmall
className.propertyName.range.toobig
scale
版本0.4才开始出现的U束属性?br />
Ҏ讑֮的scale数|自动把Q点型数字数点后的位数调整ؓ讑֮的倹{?br />
适用于以下数值类型:java.lang.Float, Java.lang.Double, and Java.math.BigDecimal (and its subclasses)?br />
Example: salary(scale:2)
Error message code: 不返回错误信?/p>
size
规定一个数|集合或者字W串长度的大?br />
在版?.5中不被徏议用在数字类型的属性上Q改用range?/font>
Example: children(size:5..15)
Note: 不能使用q个U束属性如果blank设ؓtrue或者nullable设ؓtrue?br />
Error message code:
className.propertyName.size.toosmall
className.propertyName.size.toobig
unique
如果属性必Mؓ唯一Q则设ؓtrue?br />
Example: login(unique:true)
Note: 有可能会发生通过unique验证但是在随后的数据库储存出现错误的情况。预防这U情况发生的Ҏ是用连l事务隔ȝ别或者进行eception的处理?br />
从版?.5开始,unique的范_ScopeQ可以被指定?Scope"是同一个类里其他属性的名字Q或者这些属性名字的一个list?br />
Example: group(unique:'department')
上面的例子里group名在一个department里是唯一的,但是可能在其他department里有相同名字的groups?br />
Another Example: login(unique:['group','department'])
在这个例子,login在group和department里必L唯一的。可能在不同{group和department里会有相同的login?br />
Error message code: className.propertyName.unique
url
如果属性ؓ一个URL地址Q则设ؓtrue?br />
Example: homePage(url:true)
Error message code: className.propertyName.url.invalid
validator
在闭包里讑֮自定义的验证?br />
Example:
even( validator: {
return (it % 2) == 0
})
Error message code (default): className.propertyName.validator.invalid
会在另外的文章里进行介l?/p>
class UserService {
boolean transactional = true
public boolean register(User user, UserInfo userInfo) throws RuntimeException {
if (user.save()) {
userInfo.user = user
if (userInfo.save()) {
return true
} else {
throw new RuntimeException ('ServiceException: UserService.register()...');
}
} else {
throw new RuntimeException ('ServiceException: UserService.register()...');
}
}
}
class User {
...
static constraints = {
userName(size:5..15, blank:false, unique:true)
password(size:5..15, blank:false)
email(email:true, blank:false)
age(min:18, nullable:false)
}
}
q样在你每次保存一个对象的时候都回去调用validateҎ来验?
当然你可以在M实例上调用其validateҎ去验证领域类.
def user = new User(params)
if(user.validate()) {
// do something with user
} else {
user.errors.allErrors.each {
println it
}
}
如果你在注册的时? 比如要输入两ơ密? q时候想把错误消息也攑օ领域cȝerrors属性里? ׃领域cȝ errors属性是Spring的Errors接口实例Q?/span>
Errors接口提供了访问这些验证错误的ҎQƈ且还可以取得原始的倹{?/span>
具体使用的实现类是BeanPropertyBindingResult
Java.lang.Object
org.springframework.validation.AbstractErrors
org.springframework.validation.AbstractBindingResult
org.springframework.validation.AbstractPropertyBindingResult
org.springframework.validation.BeanPropertyBindingResult
每一个消息对应一?span style="color: green">org.springframework.validation.ObjectError对象.所有可以想领域cȝerrors属性里面直接添加ObjectError对象可以了.
def error = new ObjectError('password', message(code:'pinpin.register.valid.password.notequal'))
user.errors.addError(error)
def dataSource
def list = {
def template = new JdbcTemplate(dataSource)
def userList = template.queryForList("select ui.name as name, u.password as password, ui.age as age, ui.email as email, ui.address as address from user u, user_info ui where u.id = ui.id");
def map = [userList : userList]
render(view:"list", model:map)
}
<table>
<thead>
<tr>
<g:sortableColumn property="name" title="Name" />
<g:sortableColumn property="password" title="Password" />
<g:sortableColumn property="email" title="Emial" />
<g:sortableColumn property="age" title="Age" />
<g:sortableColumn property="address" title="Address" />
</tr>
</thead>
<tbody>
<g:each in="${userList}" status="i" var="user">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td>${user.name}</td>
<td>${user.password}</td>
<td>${user.email}</td>
<td>${user.age}</td>
<td>${user.address}</td>
</tr>
</g:each>
</tbody>
</table>
在特D情况下Q?/span>grails应用需要调用数据库的存储过E,q在grails的官Ҏ档里边好像没有提到过Q在james?/span>blog里介l如何解册个问题?/span>
代码转脓如下
Java 代码
需要说明的一些是Q?/span>grails本n没有提供讉K存储q程的便h法,?/span>groovy?/span>GSQL提供了,因此grails可以直接拿过来用了,当然也可以用spring?/span>JdbcTemplate?/span>
希望?/span>grails用户有点用?/span>
原文地址Q?/span>http://grails.group.javaeye.com/group/blog/86666
My experience with grails is getting richer the longer I use it for web application developing. It's very nice that grails is built on top of spring framework which we can take advantage of. I am not a spring user before but with a help from the nice people at the grails forum I was able to achieve what I want to do.
Calling a stored procedure from a MySQL database or any other database is simple. First we need a datasource which spring could provide for us. I have the following code place in the resources.xml found in the spring folder in your grails folder.
<bean id="dataSource" class=" org.apache.commons.dbcp.BasicDataSource ">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:hsql://localhost</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
I use connection pooling for better performance. In my controller here is how I use the datasource to call a store procedure.
class MainController {
def dataSource // using the datasource we define in the spring's resources.xml
def index = {
Sql sql = new Sql(dataSource)
def row = sql.execute("call create_daily_hours(${new Date()+1})")
}
}
That's it! Notice that I am using Groovy SQL instead of Spring JDBCTemplate. It's a lot more friendlier for a beginner.
Grails really makes everything easy here and provides a lot of flexibility thanks to it's nice integration with spring. From here everything is possible.
原文地址Q?/span>http://james-says.blogspot.com/2007/03/grails-little-of-spring-framework.html
先说一下grails的web.xml的生过E?/p>
Grails的web.xml是在Package.groovy的generateWebXml target中生成的。可以在%Grails_Home%/scripts下找到它。其实在q行grails war时也是先走的q一步?/p>
在generateWebXml?/p>
1.先判断有没有config.grails.config.base.webXml属性,如果有,则用指定的config.grails.config.base.webXmlg为最l的web.xml
2.如果没有config.grails.config.base.webXml属性。再判断是否存在%Project_Home%/src/templates/war/web.xmlQ如果存在就使用q个作ؓ最l的web.xml
3.如果上面两个都没有,׃?Grails_Home%/src/war/WEB-INF/web${servletVersion}.template.xml作ؓ最l的web.xml
Ҏ上面的顺序,我们?个方法来指定web.xml
W一Q可以修?%Project_Home%/grails-app/conf/Config.groovy 文gQ在文g中添?br />
grails.config.base.webXml = "file:${basedir}/web.xml"
其中${basedir}代表%Project_Home%Q就是说会将%Project_Home%/web.xml作ؓ最l的web.xml?/p>
W二Q?grails install-templates 命oQ它会创?Project_Home%/src/templates/war/web.xml 文g。这样只要修改这个web.xmlp了?/p>
W三Q修?Grails_Home%/src/war/WEB-INF/web${servletVersion}.template.xml文gQ但是最好不要这样做?/p>
q里使用Grails 1.0.1
static mapping = {
id generator:'uuid.hex', params:[separator:'-']
}
}
class Country {
String id
String name
Boolean active = true
static mapping = {
id generator:'uuid.hex'
}
}
q样q成格式ؓ: 2c9d004d1b247311011b2473ebf90003
Grails支持一些不同的Ҏ来生XML和JSON响应。第一个是隐式的通过renderҎ?
render
Ҏ可以传递一个代码块来执行标记生成器产生XML
def list = { def results = Book.list() render(contentType:"text/xml") { books { for(b in results) { book(title:b.title) } } } }
q段代码的结果将会像q样Q?
<books> <book title="The Stand" /> <book title="The Shining" /> </books>
注意Q当你用标记生成器Ӟ必须心避免命名冲突。例如,q段代码生一个错误:
def list = { def books = Book.list() // naming conflict here render(contentType:"text/xml") { books { for(b in results) { book(title:b.title) } } } }
原因是,q里的一个本地变?code>books企图作ؓҎ被调用?
render
Ҏ可以同样被用于输出JSON:
def list = { def results = Book.list() render(contentType:"text/json") { books { for(b in results) { book(title:b.title) } } } }
在这U情况下Q结果就会是大致相同的:
[ {title:"The Stand"}, {title:"The Shining"} ]
同样的命名冲H危险适用于JSON生成器?
Q译者注Q在此附上对于列?Marshalling)解释Q对函数参数q行打包处理得过E,因ؓ指针{数据,必须通过一定得转换Q才能被另一lg所理解。可以说列集(Marshalling)是一U数据格式的转换Ҏ。)
Grails同样支持自动列集(Marshalling)领域cMؓXML通过特定的{换器?
首先Q导?code>grails.converters cdC的控制器QControllersQ中Q?
import grails.converters.*
现在Q你可以使用下列高度易读的语法来自动转换领域cLXMLQ?
render Book.list() as XML
输出l果看上M像下列这P
<?xml version="1.0" encoding="ISO-8859-1"?> <list> <book id="1"> <author>Stephen King</author> <title>The Stand</title> </book> <book id="2"> <author>Stephen King</author> <title>The Shining</title> </book> </list>
一个用{换器的替代方法是使用Grails的codecsҎ。codecsҎ提供了encodeAsXML和encodeAsJSONҎ:
def xml = Book.list().encodeAsXML() render xml
Grails同样支持自动列集(Marshalling)为JSON通过同样的机制。简单替?code>XML ?code>JSON
render Book.list() as JSON
输出l果看上M像下列这P
[ {"id":1, "class":"Book", "author":"Stephen King", "title":"The Stand"}, {"id":2, "class":"Book", "author":"Stephen King", "releaseDate":new Date(1194127343161), "title":"The Shining"} ]
再次作ؓ一U替代,你可以?code>encodeAsJSON辑ֈ相同的效?
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize"> <value>1000000</value> </property> </bean>当然gsp面需要在form里面讄enctype="multipart/form-data"
<g:form method="post" action="save" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit"/> </g:form>
import org.springframework.web.multipart.MultipartHttpServletRequest
import org.springframework.web.multipart.commons.CommonsMultipartFile
class UploadController {
static String uploadDir = "uploadfile"
def index = {
render(view:"upload")
}
def save = {
if (request instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request
CommonsMultipartFile orginalFile = (CommonsMultipartFile) multiRequest.getFile("file")
// 判断是否上传文g
if (orginalFile != null && !orginalFile.isEmpty()) {
// 获取pȝ默认文g路径分隔W?br />
def separator = System.getProperty("file.separator")
println "file separator is ${separator} "
// 获取原文件名U?br />
String originalFilename = orginalFile.getOriginalFilename()
// 获取上传文g扩展?br />
def extension = originalFilename.substring(originalFilename.indexOf(".") + 1)
println "extension is ${extension}"
def name = ".." + separator + uploadDir + separator + orginalFile.getOriginalFilename()
println "file name is : ${name}"
// 使用存放文g的绝对\径创出流
/**
DataOutputStream out = new DataOutputStream(new FileOutputStream(name))
InputStream is = null
try {
is = orginalFile.getInputStream()
byte[] buffer = new byte[1024]
while (is.read(buffer) > 0) {
out.write(buffer) // 写入盘
}
} catch (IOException exception) {
exception.printStackTrace()
} finally {
if (is != null) {
is.close()
}
if (out != null) {
out.close()
}
}
*/
orginalFile.transferTo(new File(name))
render(view:"success")
}
} else {
println "No multipart"
}
}
}
def format = attrs.get('format')
if (!format) {
format = "yyyy-MM-dd HH:mm:ss z"
}
out << new Java.text.SimpleDateFormat(format).format(date)
}
def formatNumber = { attrs ->
def number = attrs.get('number')
if (!number) {
number = new Double(0)
}
def format = attrs.get('format')
if (!format) {
format = "0"
}
out << new Java.text.DecimalFormat(format).format((Double)number)
}
}
2、安装Grails