使用注解做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/

 

orm

相关推荐