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