使用反射及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

[java]  view plain  copy
  1. /** 
  2.  * 是否是主键 
  3.  * @author Administrator 
  4.  * 
  5.  */  
  6. @Target(ElementType.FIELD)  
  7. @Retention(RetentionPolicy.RUNTIME)  
  8. public @interface Identity {  
  9.     boolean value() default false;  
  10. }  


Table.java

[java]  view plain  copy
  1. /** 
  2.  * vo类对应的表名 
  3.  * @author Administrator 
  4.  * 
  5.  */  
  6. @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)  
  7. @Target({ElementType.FIELD,ElementType.TYPE})  
  8. public @interface Table {  
  9.     String value();   
  10. }  



BaseDao.java

[java]  view plain  copy
  1. package com.oracle.dao;  
  2.   
  3.   
  4. import java.lang.reflect.Field;  
  5. import java.lang.reflect.ParameterizedType;  
  6. import java.sql.Connection;  
  7. import java.sql.DriverManager;  
  8. import java.sql.PreparedStatement;  
  9. import java.sql.ResultSet;  
  10. import java.util.ArrayList;  
  11. import java.util.List;  
  12.   
  13. import com.mysql.jdbc.Statement;  
  14. import com.oracle.annotation.Identity;  
  15. import com.oracle.annotation.Table;  
  16.   
  17. public class BaseDao<T> {  
  18.     private String url = "jdbc:mysql://localhost:3306/books";  
  19.     private String userName = "root";  
  20.     private String password = "tiger";  
  21.   
  22.     /** 
  23.      * 获得数据库连接 
  24.      *  
  25.      * @return 
  26.      */  
  27.     private Connection getConnection() {  
  28.         Connection conn = null;  
  29.         try {  
  30.             conn = DriverManager.getConnection(url, userName, password);  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.         return conn;  
  35.     }  
  36.   
  37.     /** 
  38.      * 关闭资源 
  39.      *  
  40.      * @param auto 
  41.      */  
  42.     private void close(AutoCloseable auto) {  
  43.         if (auto != null) {  
  44.             try {  
  45.                 auto.close();  
  46.             } catch (Exception e) {  
  47.                 e.printStackTrace();  
  48.             }  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * 获得表名 
  54.      *  
  55.      * @param c 
  56.      * @return 
  57.      */  
  58.     private String getTableName(Class<?> c) {  
  59.         String table_name = c.getSimpleName();  
  60.         if (c.isAnnotationPresent(Table.class)) {  
  61.             table_name = c.getAnnotation(Table.class).value();  
  62.         }  
  63.         return table_name;  
  64.     }  
  65.   
  66.     /** 
  67.      * 获得主键属性 
  68.      *  
  69.      * @param c 
  70.      * @return 
  71.      */  
  72.     private Field getIdentifyField(Class<?> c) {  
  73.         // 获得所有的类属性信息;  
  74.         Field[] fs = c.getDeclaredFields();  
  75.         // 主键属性  
  76.         for (Field f : fs) {  
  77.             if (f.isAnnotationPresent(Identity.class)) {  
  78.                 return f;  
  79.             }  
  80.         }  
  81.         return null;  
  82.     }  
  83.   
  84.     /** 
  85.      * 获得普通的列属性 
  86.      *  
  87.      * @param c 
  88.      * @return 
  89.      */  
  90.     private Field[] getColumnField(Class<?> c) {  
  91.         List<Field> list = new ArrayList<Field>();  
  92.         // 获得所有的类属性信息;  
  93.         Field[] fs = c.getDeclaredFields();  
  94.         // 主键属性  
  95.         for (Field f : fs) {  
  96.             if (!f.isAnnotationPresent(Identity.class)) {  
  97.                 list.add(f);  
  98.             }  
  99.         }  
  100.         Field[] columnField = new Field[list.size()];  
  101.         return list.toArray(columnField);  
  102.     }  
  103.   
  104.     /** 
  105.      * 生成插入的sql语句 
  106.      * @param c 
  107.      * @return 
  108.      */  
  109.     private String makeInsertSql(Class<?> c) {  
  110.         // 1.获得表名  
  111.         String table_name = this.getTableName(c);  
  112.   
  113.         // 2.定义插入的sql语句  
  114.         StringBuffer str = new StringBuffer("insert into " + table_name + "(");  
  115.         StringBuffer str_v = new StringBuffer(" values(");  
  116.   
  117.         // 3.  
  118.         Field[] fs = this.getColumnField(c);  
  119.   
  120.         // 4.遍历所有属性,生成sql  
  121.         for (int i = 0; i < fs.length; i++) {  
  122.             Field f = fs[i];  
  123.             str.append(f.getName());  
  124.   
  125.             if (i == fs.length - 1) {  
  126.                 str.append(")");  
  127.                 str_v.append("?)");  
  128.             } else {  
  129.                 str.append(",");  
  130.                 str_v.append("?,");  
  131.             }  
  132.         }  
  133.   
  134.         // 输入sql  
  135.         str.append(str_v);  
  136.         System.out.println(str);  
  137.         return str.toString();  
  138.     }  
  139.   
  140.       
  141.     /** 
  142.      * 持久化一个vo对象 
  143.      *  
  144.      * @param t 
  145.      */  
  146.     protected void save(T t) {  
  147.         Class<?> c = t.getClass();  
  148.   
  149.         //1.主键属性  
  150.         Field keyField = this.getIdentifyField(c);  
  151.         Field[] fs = this.getColumnField(c);  
  152.           
  153.         //2.定义sql  
  154.         String sql=this.makeInsertSql(c);  
  155.   
  156.         //3.给ps赋值(1.赋值;主键不赋值 ;2.返回生成的主键)  
  157.         Connection conn = this.getConnection();  
  158.         PreparedStatement ps = null;  
  159.         ResultSet rs = null;  
  160.           
  161.         try {  
  162.             // 是否需要返回主键  
  163.             if (keyField!=null) {  
  164.                 ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);  
  165.             } else {  
  166.                 ps = conn.prepareStatement(sql);  
  167.             }  
  168.             // 给所有非主键的字段赋值  
  169.             int i = 1;  
  170.             for (Field f : fs) {  
  171.                 f.setAccessible(true);  
  172.                 ps.setObject(i++, f.get(t));  
  173.                   
  174.             }  
  175.             // 4.执行sql语句  
  176.             ps.execute();  
  177.   
  178.             // 5.是否返回主键,如果需要返回主键,则获得主键,并赋给vo的主键属性  
  179.             if (keyField!=null) {  
  180.                 rs = ps.getGeneratedKeys();  
  181.                 rs.next(); // 移动光标  
  182.                 Object key = rs.getInt(1); // 主键  
  183.                 keyField.setAccessible(true);  
  184.                 keyField.set(t, key);  
  185.             }  
  186.   
  187.         } catch (Exception e) {  
  188.             e.printStackTrace();  
  189.         } finally {  
  190.             // 8.关闭资源  
  191.             this.close(rs);  
  192.             this.close(ps);  
  193.             this.close(conn);  
  194.         }  
  195.     }  
  196.   
  197.     /** 
  198.      * 根据条件查询记录,并组装成vo返回 
  199.      *  
  200.      * @param where 
  201.      * @param args 
  202.      * @return 
  203.      */  
  204.     public List<T> query(String where, Object[] args) {  
  205.   
  206.         List<T> list=new ArrayList<T>();  
  207.       
  208.         ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();  
  209.         Class<?> c = (Class<?>) pt.getActualTypeArguments()[0];  
  210.   
  211.         // 1.生成sql  
  212.         String sql=this.makeSelect(c, where);  
  213.           
  214.         //2.给ps赋值  
  215.         Connection conn = this.getConnection();  
  216.         PreparedStatement ps = null;  
  217.         ResultSet rs = null;      
  218.         Field[] fs=c.getDeclaredFields();  
  219.         try {  
  220.             ps=conn.prepareStatement(sql);  
  221.             if(args!=null) {  
  222.                 for(int i=0;i<args.length;i++) {  
  223.                     ps.setObject(i+1, args[i]);  
  224.                 }  
  225.             }         
  226.             rs=ps.executeQuery();  
  227.               
  228.             //遍历ResultSet  
  229.             while(rs.next()) {  
  230.                 @SuppressWarnings("unchecked")  
  231.                 T t=(T) c.newInstance();  
  232.                 for(Field f:fs) {  
  233.                     f.setAccessible(true);  
  234.                     f.set(t, rs.getObject(f.getName()));  
  235.                 }  
  236.                 list.add(t);  
  237.             }  
  238.   
  239.         } catch (Exception e) {  
  240.             e.printStackTrace();  
  241.         } finally {  
  242.             // 3.关闭资源  
  243.             this.close(rs);  
  244.             this.close(ps);  
  245.             this.close(conn);  
  246.         }  
  247.               
  248.         System.out.println(c);  
  249.   
  250.         return list;  
  251.     }  
  252.       
  253.     /** 
  254.      * 带条件,无参数的查询 
  255.      * @param where 
  256.      * @return 
  257.      */  
  258.     public List<T> query(String where){  
  259.         return this.query(where, null);  
  260.     }  
  261.       
  262.     /** 
  263.      * 查询全部 
  264.      * @param where 
  265.      * @return 
  266.      */  
  267.     public List<T> queryAll(){  
  268.         return this.query(nullnull);  
  269.     }  
  270.       
  271.       
  272.     /** 
  273.      * 查询 
  274.      * @param c 
  275.      * @param where 
  276.      * @return 
  277.      */  
  278.     private String makeSelect(Class<?> c,String where) {        
  279.         String sql="select * from "+this.getTableName(c)+" "+(where==null?"":where);  
  280.         return sql;  
  281.     }  
  282.   
  283. }  
[java]  view plain  copy
  1.   

Book.java

[java]  view plain  copy
  1. package com.oracle.vo;  
  2.   
  3. import com.oracle.annotation.Identity;  
  4. import com.oracle.annotation.Table;  
  5.   
  6. @Table("book")  
  7. public class Book {  
  8.   
  9.     @Identity  
  10.     private Integer isbn;  
  11.     private String bookName;  
  12.     private int price;  
  13.   
  14.     public Integer getIsbn() {  
  15.         return isbn;  
  16.     }  
  17.   
  18.     public void setIsbn(Integer isbn) {  
  19.         this.isbn = isbn;  
  20.     }  
  21.   
  22.     public String getBookName() {  
  23.         return bookName;  
  24.     }  
  25.   
  26.     public void setBookName(String bookName) {  
  27.         this.bookName = bookName;  
  28.     }  
  29.   
  30.     public int getPrice() {  
  31.         return price;  
  32.     }  
  33.   
  34.     public void setPrice(int price) {  
  35.         this.price = price;  
  36.     }  
  37.   
  38.     @Override  
  39.     public String toString() {  
  40.         return "Book [isbn=" + isbn + ", bookName=" + bookName + ", price=" + price + "]";  
  41.     }  
  42.   
  43.     public Book(Integer isbn, String bookName, int price) {  
  44.         super();  
  45.         this.isbn = isbn;  
  46.         this.bookName = bookName;  
  47.         this.price = price;  
  48.     }  
  49.   
  50.     public Book() {  
  51.         super();  
  52.     }  
  53.   
  54.     public Book(String bookName, int price) {  
  55.         super();  
  56.         this.bookName = bookName;  
  57.         this.price = price;  
  58.     }  
  59.   
  60. }  

BookDao.java

[java]  view plain  copy
  1. package com.oracle.dao;  
  2.   
  3. import java.util.List;  
  4.   
  5. import com.oracle.vo.Book;  
  6.   
  7. public class BookDao extends BaseDao<Book> {  
  8.   
  9.     @Override  
  10.     public void save(Book t) {  
  11.         // TODO Auto-generated method stub  
  12.         super.save(t);  
  13.     }  
  14.   
  15.       
  16.     public List<Book> query() {     
  17.         return super.queryAll();  
  18.     }  
  19.   
  20.       
  21.     public List<Book> getWhere(String where,Object[] args) {    
  22.         return super.query(where,args);  
  23.     }  
  24.       
  25.     public List<Book> getWhere(String where) {      
  26.         return super.query(where);  
  27.     }  
  28.   
  29. }  



TestBaseDao

[java]  view plain  copy
  1. package com.oracle.test;  
  2.   
  3. import java.util.List;  
  4.   
  5. import com.oracle.dao.BookDao;  
  6. import com.oracle.vo.Book;  
  7.   
  8. public class TestBaseDao {  
  9.   
  10.     public static void main(String[] args) {  
  11.         //定义一个dao  
  12.         BookDao dao=new BookDao();  
  13.         Book b=new Book("世界之大,无其不有",56);  
  14.         //保存  
  15.         dao.save(b);  
  16.         System.out.println(b.getIsbn());  
  17.         //查询  
  18.         List<Book> list=dao.getWhere("where price=56 limit 4,5");  
  19.   
  20.         for(Book k:list) {  
  21.             System.out.println(k);  
  22.         }  
  23.     }  
  24.   
  25. }  


4.总结

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

5.源码下载

猜你喜欢

转载自blog.csdn.net/zhangyu984722457/article/details/79563701