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