日期:2014-05-16  浏览次数:20465 次

Grails的数据库相关开发

1.开发domain和service



?在出来的输入框里输入domain的名字,可以包括包名。

这里我输入test.domain.House,点finish

?

创建了两个groovy文件,一个当然是test.domain.House.groovy,另一个是test.domain.HouseTests.groovy.

先说test.domain.House.groovy。这个就是传说中的POGO。Grails会在运行时给他注入很多方法。

?

现在给他增加两个属性:

?

package test.domain

class House {
	
	String name
	String address

    static constraints = {

    }
}

?

?

新建一个service(方法参见上一篇),名称可以直接输入House的类全名。

编辑生成的service单元测试:

?

package test.domain



import grails.test.mixin.*
import org.junit.*

/**
 * See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions
 */
@TestFor(HouseService)
@Mock(House)
class HouseServiceTests {

    void testFindByAddress() {
		new House(name:"Beautiful House",address:"No.1").save();
        def house = service.findByAddress("No.1")
		assert house != null
		println house.id
    }
}

?

?注意这一行:

println house.id

?

之前并没有给House定义ID,GORM会默认给他加上一个ID。

另外说一下@Mock(House)。因为domain要在grails运行时才会给domain注入方法,如果在单元测试的时候可以使用Mock这个annotation,给House这个domain注入运行时的模拟方法。

?

实现一下service:

?

package test.domain

class HouseService {

    def findByAddress(String address) {
		return House.findByAddress(address)
    }
}

?

?findByAddress就是一个动态生成的方法,可以让我们按地址查找。

?

运行一下HouseServiceTests这个单元测试:


?

当然也可以先debug,跑完以后会弹出打开 TESTS-TestSuites.xml ?这个文件。点下面的sheet切换:


就可以看到test里打的println了:

?

<system-out><![CDATA[--Output from testFindByAddress--

1

]]></system-out>

?

?

这个XML的格式不解释。

?

service需要加事务吧:

?

package test.domain

import org.springframework.transaction.annotation.Transactional;

class HouseService {

	@Transactional(readOnly=true)
    def findByAddress(String address) {
		return House.findByAddress(address)
    }
}

?依然可以使用spring的@Transactional。当然也有别的方法。暂时不写了。

2.domain的验证

test.domain.HouseTests.groovy 不明白为什么要生成这个测试单元。方法都是Grails注入的,MS没什么好测的。

现在拿来做测试验证吧。

给test.domain.House.groovy加多几个属性:

?

package test.domain

class House {
	
	String name
	String type
	String desc
	String address
	Date buildedDate
	Float price;

    static constraints = {
		type inList:["common","bungalow","villa"],nullable:true
		desc maxSize:1000,nullable:true
		buildedDate max:new Date(),nullable:true
		price max:98765432109876543210f,scale:2,nullable:true
    }
}

?

?

重点看下边的constraints。这里定义属性的约束 ,选了几个典型的。

inList,适用于枚举。

desc这种属于大文本,字符串默认在数据库里会变成?varchar(255) ,定义了最大值就会成为TEXT

bulidedDate这种属于历史时间,不应当晚于当前时间。参照这种定义方法。

price属于浮点,定义最大最小值的时候需要注意要是浮点数(注意最后的f),默认的scale是3

?

?

测试代码如下:

?

package test.domain



import grails.test.mixin.*
import grails.test.mixin.domain.DomainClassUnitTestMixin;

import org.junit.*

/**
 * See the API for {@link grails.test.mixin.domain.DomainClassUnitTestMixin} for usage instructions
 */
@TestFor(House)
class HouseTests {

	void testSomething() {
		mockForConstraintsTests(House)
		def house = new House(name:"House1",type:"unknown");
		
		assert !house.validate()
		println house.errors["type"]
	}
}
?

?

mockFo