上回书说到,Sam成功地利用AST在编译期往grails doamin class中注入了客制化的属性。由于grails自己注入的id默认为Long,而我需要String(这样才能把UUID String赋给id)。怎样把Long改成String呢?
可以在domain class中加入一行声明
String id
同时还可以这样指定id生成规则:
static mapping = { id generator:'uuid.hex', params:[separator:'-'] }
个人不喜欢这样做:每个domain class都出现这种boilerplate code,无疑是件很令人不爽的事情。虽然id可以是Long也可以是String,但我不喜欢1,2,3这种东西来做id。
而且因为我抛弃了RDBMS,使用OODBMS,已经把GORM, Hibernate plugin从Grails中移除了(另外仿照它的样子,山寨了它的validation和dynamic finders)。
开始Hacking:
1. 尝试注入String类型的属性ID,直接就成功了。但每个domain class中同时含有Long id和String ID,ID利用上了,而id一直闲置着 - 怎么看都不爽。
2. 尝试删除Long id,注入String ID。Grails无法启动,报错: 它要求domain class必须有一个名为id的属性。
3. 方向已经很清楚了:不要注入String ID,把id的类型由Long改成String。怎么改?大胆尝试后发现竟如此的简单:
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class GrsDomainTransformation implements ASTTransformation {
//...
private void transform(ClassNode classNode, boolean auditable) {
// 'id' defaults to Long, so a little hacking is needed: change it to String so that I can assign UUID String to it
changeIdPropertyType2String(classNode);
injectDeletedProperty(classNode);
if(auditable) {
injectDateCreatedProperty(classNode);
injectHistoryProperty(classNode);
}
}
//...
private void changeIdPropertyType2String(ClassNode classNode) {
List<PropertyNode> properties = classNode.getProperties();
for(PropertyNode p : properties) {
if(GrailsDomainClassProperty.IDENTITY.equals(p.getName())) {
p.setType(new ClassNode(String.class));
break;
}
}
}
}
就这么一句
p.setType(new ClassNode(String.class));
轻松搞掂。