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.domainclass House {String nameString address static constraints = { }}?
?
新建一个service(方法参见上一篇),名称可以直接输入House的类全名。
编辑生成的service单元测试:
?
package test.domainimport 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 != nullprintln house.id }}?
?注意这一行:
println house.id
?
之前并没有给House定义ID,GORM会默认给他加上一个ID。
另外说一下@Mock(House)。因为domain要在grails运行时才会给domain注入方法,如果在单元测试的时候可以使用Mock这个annotation,给House这个domain注入运行时的模拟方法。
?
实现一下service:
?
package test.domainclass 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.domainimport 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.domainclass House {String nameString typeString descString addressDate buildedDateFloat price; static constraints = {type inList:["common","bungalow","villa"],nullable:truedesc maxSize:1000,nullable:truebuildedDate max:new Date(),nullable:trueprice max:98765432109876543210f,scale:2,nullable:true }}?
?
重点看下边的constraints。这里定义属性的约束 ,选了几个典型的。
inList,适用于枚举。
desc这种属于大文本,字符串默认在数据库里会变成?varchar(255) ,定义了最大值就会成为TEXT
bulidedDate这种属于历史时间,不应当晚于当前时间。参照这种定义方法。
price属于浮点,定义最大最小值的时候需要注意要是浮点数(注意最后的f),默认的scale是3
?
?
测试代码如下:
?
package test.domainimport 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"]}}??
mockForConstraintsTests(House)是内置的给domain加上验证框架的内置方法的一个mock。house.validate()用于验证所有定义的约束,可以用在web和service里,house.errors["属性名"]可以查违反的约束。
?
运行测试可以看到结果。
?
3.domain间的关系?
关于一对一,多对对,一对多,多对一的这些个关系,官方的reference写得挺清楚,不翻译了。
不过得注意一下这个belongsTo?
如让房子有个主人:
?
package test.domainclass House {String nameString typeString descString addressDate buildedDateFloat price;static belongsTo = [owner:Person] static constraints = {type inList:["common","bungalow","villa"],nullable:truedesc maxSize:1000,nullable:truebuildedDate max:new Date(),nullable:trueprice max:98765432109876543210f,scale:2,nullable:true }}?package test.domain
class Person {String namestatic hasMany = [houses:House] static constraints = { }}?
这时房子从属于人,当人删除的时候会对房子造成级联删除。有一定危险,使用的时候要注意。
?
由于不知道怎么在单元测试里检验下面要说的东西所以……
以下内容属于道听未经验证
?
4.多数据源假设第二个数据源叫datasoruce_second,配置如下
?
environments { development { dataSource { dbCreate = "create-drop" url = "jdbc:h2:mem:devDb" } dataSource_second { dialect = org.hibernate.dialect.MySQLInnoDBDialect driverClassName = 'com.mysql.jdbc.Driver' username = 'root' password = 'root' url = 'jdbc:mysql://localhost/root' dbCreate = 'update' } } test { dataSource { dbCreate = "update" url = "jdbc:h2:mem:testDb" } } production { dataSource { dbCreate = "update" url = "jdbc:h2:prodDb" } dataSource_second { dialect = org.hibernate.dialect.Oracle10gDialect driverClassName = 'oracle.jdbc.driver.OracleDriver' username = 'root' password = 'root' url = 'jdbc:oracle:thin:@localhost:1521:root' dbCreate = 'update' } }}?
?如果人这个表用的这个数据源:
package test.domainclass Person {String namestatic hasMany = [houses:House] static constraints = {datasources(["second"]) }}?
另外还可以引入别的datasource的包,把数据源当成spring的bean注入到程序里使用。
5.直接执行SQL一种方法当然是写java代码,然后groovy当bean来调用,这种感觉用groovy用得比较假。。。
直接写SQL的形如:
?
package test.sqlimport groovy.sql.Sqlclass SQLTestService {def dataSource def sqlRun() {def db = new Sql(dataSource)db.eachRow('select * from house'){ println it } }}?
以上两节留待以后验证