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

来源:互联网 发布:java输出99乘法表对齐 编辑:程序博客网 时间:2024/05/24 03:25

引言

目前的持久化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.遍历所有属性,生成sqlfor (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("?,");}}// 输入sqlstr.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.定义sqlString 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.生成sqlString 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();//遍历ResultSetwhile(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 {@Identityprivate 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;}@Overridepublic 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> {@Overridepublic void save(Book t) {// TODO Auto-generated method stubsuper.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) {//定义一个daoBookDao 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.源码下载

原创粉丝点击