使用反射及Annotation自定义Dao组件

引言

目前的持久化O/R框架很多,包括Hibernate,Mybats和JPA等,同时还有Apache的DBUtil组件等,通过使用这些框架,程序员避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。这些框架都可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。这些框架使用的技术主要用反射机制,Annotation,XML等,为了更好的理解持久层框架的实现原理,下面通过反射和Annotation来手动实现一个持久化的组件;

1.使用技术

annotation:自定义Annotation,用来定义两个注解,@Table用来说明VO对应的表名,如果列名与类名相同,无需使用此注解,@Identity用来标识对应主键的属性,同时可以用来指定是否需要将数据库生成的主键返回

reflaction:通过反射可以动态的生成sql语句,动态的给PreparedStatement的点位符赋值,也可以动态的生成VO对象,将记录类型转成Vo返回

泛型:通过泛型,程序员可以直接继承 BaseDao<T>来实现自己的DAO类

JDBC:访问数据库的基础

2.工程结构

文件说明

Identity:标识属性为主键的注解

Table:标识VO类对应表名的注解

Book:VO类

BaseDao:Dao的父类,提供了插入和查询两个基本方法;

BookDao:Book类的DAO操作类

estBaseDao:测试类

3.代码

Identity.java

/**
 * 是否是主键
 * @author Administrator
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Identity {
	boolean value() default false;
}


Table.java

/**
 * vo类对应的表名
 * @author Administrator
 *
 */
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface Table {
	String value(); 
}



BaseDao.java

package com.oracle.dao;


import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.mysql.jdbc.Statement;
import com.oracle.annotation.Identity;
import com.oracle.annotation.Table;

public class BaseDao<T> {
	private String url = "jdbc:mysql://localhost:3306/books";
	private String userName = "root";
	private String password = "tiger";

	/**
	 * 获得数据库连接
	 * 
	 * @return
	 */
	private Connection getConnection() {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(url, userName, password);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;
	}

	/**
	 * 关闭资源
	 * 
	 * @param auto
	 */
	private void close(AutoCloseable auto) {
		if (auto != null) {
			try {
				auto.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 获得表名
	 * 
	 * @param c
	 * @return
	 */
	private String getTableName(Class<?> c) {
		String table_name = c.getSimpleName();
		if (c.isAnnotationPresent(Table.class)) {
			table_name = c.getAnnotation(Table.class).value();
		}
		return table_name;
	}

	/**
	 * 获得主键属性
	 * 
	 * @param c
	 * @return
	 */
	private Field getIdentifyField(Class<?> c) {
		// 获得所有的类属性信息;
		Field[] fs = c.getDeclaredFields();
		// 主键属性
		for (Field f : fs) {
			if (f.isAnnotationPresent(Identity.class)) {
				return f;
			}
		}
		return null;
	}

	/**
	 * 获得普通的列属性
	 * 
	 * @param c
	 * @return
	 */
	private Field[] getColumnField(Class<?> c) {
		List<Field> list = new ArrayList<Field>();
		// 获得所有的类属性信息;
		Field[] fs = c.getDeclaredFields();
		// 主键属性
		for (Field f : fs) {
			if (!f.isAnnotationPresent(Identity.class)) {
				list.add(f);
			}
		}
		Field[] columnField = new Field[list.size()];
		return list.toArray(columnField);
	}

	/**
	 * 生成插入的sql语句
	 * @param c
	 * @return
	 */
	private String makeInsertSql(Class<?> c) {
		// 1.获得表名
		String table_name = this.getTableName(c);

		// 2.定义插入的sql语句
		StringBuffer str = new StringBuffer("insert into " + table_name + "(");
		StringBuffer str_v = new StringBuffer(" values(");

		// 3.
		Field[] fs = this.getColumnField(c);

		// 4.遍历所有属性,生成sql
		for (int i = 0; i < fs.length; i++) {
			Field f = fs[i];
			str.append(f.getName());

			if (i == fs.length - 1) {
				str.append(")");
				str_v.append("?)");
			} else {
				str.append(",");
				str_v.append("?,");
			}
		}

		// 输入sql
		str.append(str_v);
		System.out.println(str);
		return str.toString();
	}

	
	/**
	 * 持久化一个vo对象
	 * 
	 * @param t
	 */
	protected void save(T t) {
		Class<?> c = t.getClass();

		//1.主键属性
		Field keyField = this.getIdentifyField(c);
		Field[] fs = this.getColumnField(c);
		
		//2.定义sql
		String sql=this.makeInsertSql(c);

		//3.给ps赋值(1.赋值;主键不赋值 ;2.返回生成的主键)
		Connection conn = this.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			// 是否需要返回主键
			if (keyField!=null) {
				ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			} else {
				ps = conn.prepareStatement(sql);
			}
			// 给所有非主键的字段赋值
			int i = 1;
			for (Field f : fs) {
				f.setAccessible(true);
				ps.setObject(i++, f.get(t));
				
			}
			// 4.执行sql语句
			ps.execute();

			// 5.是否返回主键,如果需要返回主键,则获得主键,并赋给vo的主键属性
			if (keyField!=null) {
				rs = ps.getGeneratedKeys();
				rs.next(); // 移动光标
				Object key = rs.getInt(1); // 主键
				keyField.setAccessible(true);
				keyField.set(t, key);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 8.关闭资源
			this.close(rs);
			this.close(ps);
			this.close(conn);
		}
	}

	/**
	 * 根据条件查询记录,并组装成vo返回
	 * 
	 * @param where
	 * @param args
	 * @return
	 */
	public List<T> query(String where, Object[] args) {

		List<T> list=new ArrayList<T>();
	
		ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
		Class<?> c = (Class<?>) pt.getActualTypeArguments()[0];

		// 1.生成sql
		String sql=this.makeSelect(c, where);
		
		//2.给ps赋值
		Connection conn = this.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;	
		Field[] fs=c.getDeclaredFields();
		try {
			ps=conn.prepareStatement(sql);
			if(args!=null) {
				for(int i=0;i<args.length;i++) {
					ps.setObject(i+1, args[i]);
				}
			}		
			rs=ps.executeQuery();
			
			//遍历ResultSet
			while(rs.next()) {
				@SuppressWarnings("unchecked")
				T t=(T) c.newInstance();
				for(Field f:fs) {
					f.setAccessible(true);
					f.set(t, rs.getObject(f.getName()));
				}
				list.add(t);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 3.关闭资源
			this.close(rs);
			this.close(ps);
			this.close(conn);
		}
			
		System.out.println(c);

		return list;
	}
	
	/**
	 * 带条件,无参数的查询
	 * @param where
	 * @return
	 */
	public List<T> query(String where){
		return this.query(where, null);
	}
	
	/**
	 * 查询全部
	 * @param where
	 * @return
	 */
	public List<T> queryAll(){
		return this.query(null, null);
	}
	
	
	/**
	 * 查询
	 * @param c
	 * @param where
	 * @return
	 */
	private String makeSelect(Class<?> c,String where) {		
		String sql="select * from "+this.getTableName(c)+" "+(where==null?"":where);
		return sql;
	}

}

Book.java

package com.oracle.vo;

import com.oracle.annotation.Identity;
import com.oracle.annotation.Table;

@Table("book")
public class Book {

	@Identity
	private Integer isbn;
	private String bookName;
	private int price;

	public Integer getIsbn() {
		return isbn;
	}

	public void setIsbn(Integer isbn) {
		this.isbn = isbn;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Book [isbn=" + isbn + ", bookName=" + bookName + ", price=" + price + "]";
	}

	public Book(Integer isbn, String bookName, int price) {
		super();
		this.isbn = isbn;
		this.bookName = bookName;
		this.price = price;
	}

	public Book() {
		super();
	}

	public Book(String bookName, int price) {
		super();
		this.bookName = bookName;
		this.price = price;
	}

}

BookDao.java

package com.oracle.dao;

import java.util.List;

import com.oracle.vo.Book;

public class BookDao extends BaseDao<Book> {

	@Override
	public void save(Book t) {
		// TODO Auto-generated method stub
		super.save(t);
	}

	
	public List<Book> query() {	
		return super.queryAll();
	}

	
	public List<Book> getWhere(String where,Object[] args) {	
		return super.query(where,args);
	}
	
	public List<Book> getWhere(String where) {	
		return super.query(where);
	}

}



TestBaseDao

package com.oracle.test;

import java.util.List;

import com.oracle.dao.BookDao;
import com.oracle.vo.Book;

public class TestBaseDao {

	public static void main(String[] args) {
		//定义一个dao
		BookDao dao=new BookDao();
		Book b=new Book("世界之大,无其不有",56);
		//保存
		dao.save(b);
		System.out.println(b.getIsbn());
		//查询
		List<Book> list=dao.getWhere("where price=56 limit 4,5");

		for(Book k:list) {
			System.out.println(k);
		}
	}

}


4.总结

以上例子虽然简单,但可以把持久层框架的主要功能的原理描述清楚,但还存在很多问题,数据库连接池,数据库参数的配置,事务控制,高级映射,缓存机制,数据的批量操作等功能都没有考虑;这些功能在以后的博客中还会持续增加;

5.源码下载

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值