使用注解做ORM
1. 注解在Nutz中的应用
在nutz中,和数据库对应的pojo,我们可以使用注解的方式来配置field和table的column之间的对应,也可以用注解来配置两个pojo之间的关系,例如我有一个POJO,名字叫"Box",里面包含两个字段:
(1)
@Column("boxid") @Id private int id;
对于字段id,@Column("boxid")说明在数据库中对应column "boxid", @Id说明,这个是主键。
(2)
@Many(target = BoxInfo.class, field = "boxId") private BoxInfo[] infos;
这个注解声明了Box和BoxInfo这两个POJO之间存在一对多的关系:Box----->多个 BoxInfo,也就是说给定一个boxid,您可以在BoxInfo对应的table里面找到多条记录,通过"boxId"与"Box"关联。如果我没有说清楚这个例子,您可以去参考nutz项目的wiki,你会发现更多实用的注解和介绍。
2.注解的简单介绍
我相信,在此之前,您一定使用过注解,我猜测您用过junit4,hibernate3,等等框架或工具,最起码你用过@Override(我不信你没用过这个)。
Annotation是jdk1.5引进的,它的引入给我们了一种新的手段来增加程序的信息,在src,class或runtime级别。程序员可以自己定义一些Annotation,然后把它用到自己的程序中,当然您还得编写自己的Annotation处理器。
3.简单的例子
(3.1) 假如我想定义两个Annotation(@TableName ,@Column)给POJO使用,@TableName用在POJO的类上,声明对应于数据库的哪个table,@Column用在POJO的field上,用于指明对应的column,那么我可以这么做:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TableName{ String value(); }
@Target(ElementType.TYPE)说明了这是一个"类、接口(包括注释类型)或枚举声明",打开你的JDKAPI,去找“java.lang.annotation.ElementType”,您会发现更多的可选Target。
@Retention(RetentionPolicy.RUNTIME)指明"编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取"。
再来定义一个
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { String value() default ""; }
(3.2) 把这两个注解应用到我的code中
@TableName("tbl_user") public class User { @Column private int id; @Column private String name; @Column("age") private int userAge; public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public int getUserAge() {return userAge;} public void setUserAge(int userAge) {this.userAge = userAge;} }
(3.3) 假如我想写一个处理器,为使用这两个注解的POJO生成Insert语句,那么我可以这么做。(为了不使示例复杂,这里只考虑最天真幼稚的情况,并且把所有代码放在一块,您明白什么意思就行)。
public class SqlGetter { public static void main(String[] args) throws Exception { User user = new User(); user.setId(1); user.setName("Tom"); user.setUserAge(12); System.out.println(new SqlGetter().getInsertSql(user)); } public String getInsertSql(Object obj) throws Exception { //获取table名称 String tableName=obj.getClass().getAnnotation(TableName.class).value(); // 存放column---value HashMap<String, Object> kvs = new HashMap<String, Object>(); Field[] fs = obj.getClass().getDeclaredFields(); for (Field f : fs) { String cn = this.getFieldColumn(f); if (cn != null) { kvs.put(f.getName(), this.getFieldValue(obj,f)); } } //硬拼SQl StringBuilder prefix = new StringBuilder(); StringBuilder suffix = new StringBuilder(); for(Iterator<String> it=kvs.keySet().iterator();it.hasNext();){ String key=it.next(); prefix.append(key); suffix.append(kvs.get(key)); if (it.hasNext()) { prefix.append(","); suffix.append(","); } } return String.format("INSERT INTO %s (%s) VALUES (%s)", tableName,prefix,suffix); } private String getFieldColumn(Field field) { Column column = field.getAnnotation(Column.class); if (column != null) { if ("".equals(column.value())) return field.getName(); else { return column.value(); } } return null; } private Object getFieldValue(Object obj,Field field) throws Exception { String name = field.getName(); String c = name.substring(0, 1); name = name.replaceFirst(c, c.toUpperCase()); Method m = obj.getClass().getMethod("get" + name, new Class<?>[] {}); return m.invoke(obj, new Object[] {}); } }
不出意外,输出应该是"INSERT INTO tbl_user (id,name,userAge) VALUES (1,Tom,12)"。 通过这个例子您会发现许多问题:
(1)代码结构差 (2)异常处理差 (3)结果都不对,因为sql语句没有给string类型加上引号。(4).....
但是,如果您之前对Annotation不是特别了解,但愿本文能起到一个抛砖引玉的作用。
最后,您可以去使用一下Nutz的dao功能,去体验一下Annotation给我们带来的便利。http://code.google.com/p/nutz/