不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理

时间:2023-12-16 18:23:32

今天写了个万能的BaseDao:有了这个BaseDao以后的Dao层直接继承这个BaseDao就能直接操作数据库了,增删改查,这是一个简易的Hibernate模型。写这个BaseDao的原因是最近在学习Hibernate框架,还有很多不足的地方希望谅解(自己能够独立的写出来还是挺开心的),其中的注释是在写代码的时候调试留下的,请自动忽略。

原文地址:

https://www.cnblogs.com/poterliu/p/4987233.html


主要用到了一下的Java知识:

①反射:用来加载实体类的Get方法,从而获取到实体类的属性值

②注解:用来给实体类的成员变量添加注解,从而能够和数据表的表名和字段动态对应

③范型:将BaseDao作为范型类,让所有的实体Dao都继承该类,传入一个实体类给BaseDao

④c3p0连接池和dbutils这两个工具jar包的使用

⑤改写了一个dbutils的QueryRunner类,主要是参照itcast的itcast-tools-1.4.2.jar,其中会用到JdbcUtils,也一起写上来了。


网上还有很多类似的代码,很多都会涉及到Hibernate框架的东西,而这个BaseDao是纯Java原生代码,它的意义就在于能够感受到反射机制的强大,同时还可以体会到一点儿很基础的Hibernate原理,这样对于刚接触Hibernate的人来说也是一个很好启示。


-----废话说得有点儿多

下面正式开始贴代码。


如果要测试代码,务必先导入以下jar包:

①c3p0-0.9.2-pre1.jar

②commons-dbutils-1.4.jar

③mchange-commons-0.2.jar

④数据库驱动,我用的MySQL当然是导入MySQL的驱动


当然最后还要使用到c3p0配置文件c3p0-config.xml

 <?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/bookstore</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">admin</property>
<property name="acquireIncrement">3</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">2</property>
<property name="maxPoolSize">8</property>
</default-config>
</c3p0-config>
  • 核心代码请看第四部分

一、写实体类--Demo的起步

会用到两个实体,用户(User)和顾客(Customer),其实两个都差不多,主要是两个测试起来更放心一点。实体里用到的三个注解:Table、ID、Column将会在第二部分介绍

1、User.java

 package com.project.domain;

 import java.util.List;

 @Table("t_user")
public class User {
/**
* 该id表示主键
*/
@ID("Id")
private String id; @Column("Uname")
private String uname;//用户名 @Column("Pwd")
private String pwd;//密码 @Column("Email")
private String email; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [id=" + id + ", uname=" + uname + ", pwd=" + pwd
+ ", email=" + email + "]";
} }
  • User类在数据库中对应的表结构

不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理

2、Customer.java

 package com.project.domain;

 @Table("t_customer")
public class Customer {
@ID("Cid")
private String cid; @Column("Cname")
private String cname; @Column("Spent")
private int spent; public String getCid() {
return cid;
} public void setCid(String cid) {
this.cid = cid;
} public String getCname() {
return cname;
} public void setCname(String cname) {
this.cname = cname;
} public int getSpent() {
return spent;
} public void setSpent(int spent) {
this.spent = spent;
} @Override
public String toString() {
return "Customer [cid=" + cid + ", cname=" + cname + ", spent=" + spent
+ "]";
} }
  • Customer类在数据库中对应的表结构

不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理

二、注解类--反射的前奏

  • 使用了Table、ID、Column三个注解类,分别对应了数据库、主键、列名(字段),会在BaseDao中利用反射获取实体类的注解信息和成员变量的注解信息
  • 注意:之所以会给每一个注解加一个:@Retention(value=RetentionPolicy.RUNTIME)的注解信息是因为Java中自定义的注解只有指定了这个注解,jvm才不会在编译时忽略掉该注解信息,才能够利用反射获取类对应的该注解信息。

1、Table.java,用来给实体类配置数据库中对应表名,而不是直接将实体类名作为数据库表名

 package com.project.domain;

 import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; @Retention(value=RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}

2、ID.java,用来表示数据库表的主键字段

 package com.project.domain;

 import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; @Retention(value=RetentionPolicy.RUNTIME)
public @interface ID {
String value();
}

3、Column.java,用来表示数据库中非主键字段的名称

 package com.project.domain;

 import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; @Retention(value=RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}

三、JdbcUtils.java和TxQueryRunner.java--工具类方便操作数据库

这两个类参照了itcast.jar中代码

  • JdbcUtils.java,该类封装类连接数据库的一些操作,TxQueryRunner.java中会用到该类
 package com.project.util;

 import java.sql.Connection;
import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; /**
* 使用本类的方法,必须提供c3p0-copnfig.xml文件
* @author qdmmy6
*/
public class JdbcUtils {
// 饿汉式
private static DataSource ds = new ComboPooledDataSource(); /**
* 它为null表示没有事务
* 它不为null表示有事务
* 当开启事务时,需要给它赋值
* 当结束事务时,需要给它赋值为null
* 并且在开启事务时,让dao的多个方法共享这个Connection
*/
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); public static DataSource getDataSource() {
return ds;
} /**
* dao使用本方法来获取连接
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
/*
* 如果有事务,返回当前事务的con
* 如果没有事务,通过连接池返回新的con
*/
Connection con = tl.get();//获取当前线程的事务连接
if(con != null) return con;
return ds.getConnection();
} /**
* 开启事务
* @throws SQLException
*/
public static void beginTransaction() throws SQLException {
Connection con = tl.get();//获取当前线程的事务连接
if(con != null) throw new SQLException("已经开启了事务,不能重复开启!");
con = ds.getConnection();//给con赋值,表示开启了事务
con.setAutoCommit(false);//设置为手动提交
tl.set(con);//把当前事务连接放到tl中
} /**
* 提交事务
* @throws SQLException
*/
public static void commitTransaction() throws SQLException {
Connection con = tl.get();//获取当前线程的事务连接
if(con == null) throw new SQLException("没有事务不能提交!");
con.commit();//提交事务
con.close();//关闭连接
con = null;//表示事务结束!
tl.remove();
} /**
* 回滚事务
* @throws SQLException
*/
public static void rollbackTransaction() throws SQLException {
Connection con = tl.get();//获取当前线程的事务连接
if(con == null) throw new SQLException("没有事务不能回滚!");
con.rollback();
con.close();
con = null;
tl.remove();
} /**
* 释放Connection
* @param con
* @throws SQLException
*/
public static void releaseConnection(Connection connection) throws SQLException {
Connection con = tl.get();//获取当前线程的事务连接
if(connection != con) {//如果参数连接,与当前事务连接不同,说明这个连接不是当前事务,可以关闭!
if(connection != null &&!connection.isClosed()) {//如果参数连接没有关闭,关闭之!
connection.close();
}
}
}
}
  • TxQueryRunner.java,该类重写了dbutils.jar中的QueryRunner.java,为每个方法添加了打开连接和关闭的操作,这样优化了数据库连接。
 package com.project.util;

 import java.sql.Connection;
import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler; public class TxQueryRunner extends QueryRunner { @Override
public int[] batch(String sql, Object[][] params) throws SQLException {
Connection con = JdbcUtils.getConnection();
int[] result = super.batch(con, sql, params);
JdbcUtils.releaseConnection(con);
return result;
} @Override
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.query(con, sql, rsh, params);
JdbcUtils.releaseConnection(con);
return result;
} @Override
public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.query(con, sql, rsh);
JdbcUtils.releaseConnection(con);
return result;
} @Override
public int update(String sql) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con, sql);
JdbcUtils.releaseConnection(con);
return result;
} @Override
public int update(String sql, Object param) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con, sql, param);
JdbcUtils.releaseConnection(con);
return result;
} @Override
public int update(String sql, Object... params) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con, sql, params);
JdbcUtils.releaseConnection(con);
return result;
}
}

四、BaseDao.java--好戏来了

  • BaseDao使用abstract修饰表示不能直接new,而是必须要被一个类继承同时传入实体类型
  • 定义了一些反射相关的成员变量以及实体类和表之间对应关系要用到的成员变量
  • 在构造方法中加载子类传递给BaseDao的实体类信息:

在构造方法中加载子类传递给BaseDao的实体类信息:

clazz = (Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];

*  接着在下面获取子类的注解信息

table = clazz.getAnnotation(Table.class);
tableName = table.value();

//实例成员变量
fields = clazz.getDeclaredFields();

  •  类定义和构造方法定义:BaseDao<T>
 package com.project.util;

 import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler; import com.project.domain.Column;
import com.project.domain.ID;
import com.project.domain.Table; /**
* 数据层基类:能够使数据层的dao直接使用BaseDao已经封装的方法
* @author Poter
* 2015年11月22日年下午3:47:16
* @param <T>
*/
public abstract class BaseDao<T> {
private QueryRunner qr = new TxQueryRunner();
private Class<?> clazz;//T的实例类型
private Table table;//表名的注解对象
private String tableName ;//表名
private ID mainId = null;//主键列
private List<Column> colus = new ArrayList<Column>();//普通列注解对象集合 private List<String> columns = new ArrayList<String>();//普通列 //所有的成员变量
private Field[] fields;
//包含主键列以及普通列名
private List<String> allParams = new ArrayList<String>();
//表示有赋值的所有属性,属性值为null的属性不会添加到havaValues
private List<String> havaValues = new ArrayList<String>(); /**
* 获取子类的相关信息
*/
public BaseDao() {
//获取范型类的类类型
clazz = (Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
System.out.println("T的实例类型->" + clazz.getSimpleName()); table = clazz.getAnnotation(Table.class);
tableName = table.value(); //实例成员变量
fields = clazz.getDeclaredFields(); //获取类的成员变量
for (int i = 0; i < fields.length; i++) {
ID id = fields[i].getAnnotation(ID.class);
if(id != null){
mainId = id;
}
Column column = fields[i].getAnnotation(Column.class);
if(column != null){
// System.out.println(column.value());
colus.add(column);
columns.add(column.value());
}
}
}
  • void add(T t),向数据库中插入一条记录
  • 先封装sql语句
  • 然后利用反射来加载每个成员变量对应的值,会用到一个 loadData(t) 方法,这是因为反射的操作代码很繁琐,卸载一个方法里影响查阅,同时写成一个方法主要是因为后面update还会用到这个方法,这样就会提高代码的利用率。要知道反射的关键内容,请继续往下看。

//为sql语句的字段装载数据
Map<String,Object> params = loadData(t);

  • public void add(T t){}
     /**
* 添加
* @param t
*/
public void add(T t){
//insert into tb_user values(?,?,?,?)
String sql = "insert into " + tableName +" values(";
for (int i = 0; i < fields.length ; i++) {
sql+="?";
if(i < fields.length - 1){
sql+=",";
}
}
sql+=")";
System.out.println(sql); //为sql语句的字段装载数据
Map<String,Object> params = loadData(t); allParams.add(mainId.value());
for (int i = 0; i < columns.size(); i++) {
allParams.add(columns.get(i));
}
System.out.println("allParams = "+ allParams); //为执行sql语句的?赋值
Object [] params2 = new Object[params.size()];
for (int i = 0; i < params.size(); i++) {
params2[i] = params.get(allParams.get(i));
} // for (int i = 0; i < params2.length; i++) {
// System.out.println(params2[i]);
// } try {
qr.update(sql, params2);
} catch (SQLException e) {
throw new RuntimeException(e);
} }
  • loadData(T t),之所以会将范型T传给loadData方法,是因为下面使用反射时会使用对应的实体类型
  • private <T> Map<String,Object> loadData(T t) {}
  • 该方法中有出现了两个方法:loadMethods() 和 loadValues(t,methodNames)
    /**
* 加载全部数据:按照实体类成员书写顺序先后赋值
* @param t
* @return
*/
private <T> Map<String,Object> loadData(T t) {
String [] methodNames = loadMethods();
Map<String,Object> params = loadValues(t,methodNames);
return params;
}
  • loadMethods(),该方法用来加载实体类对应的所有的Getter方法
  • private <T> Map<String,Object> loadData(T t) {}
    /**
* 加载所有的get方法
* @return
*/
private String[] loadMethods() {
//获取该类自身声明的方法,这里包含Getter、Setter、toString
Method[] methods = clazz.getDeclaredMethods(); //测试
try {
// Method tostring = clazz.getDeclaredMethod("toString");
// String res = (String) tostring.invoke(t);
// System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
} // System.out.println(methods.length); //用来存放所有的get方法名
String [] methodNames = new String[columns.size()+1];//要加上id属性 int x = -1;
for (int i = 0; i < methods.length; i++) {
//获取所有的方法名称
String fun = methods[i].getName();
//获取所有的get方法
if(fun.contains("get")){
x++;
methodNames[x] = fun;
}
}
return methodNames;
}
  • loadValues(t,methodNames),用来获取传给BaseDao的实体类的实例中赋值了属性值,且属性值必须包括主键Id,即Id属性不能为空
  • 利用反射区实现属性名称对应的Getter方法,从而获取到实例的属性值,这是本文中利用反射最关键的地方
  • 根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
  • private <T>Map<String,Object> loadValues(T t,String [] methodNames){}
 1    /**
* 加载属性值,并将属性名和属性值一起封装到Map中
* @param t
* @param methodNames
* @return
*/
private <T>Map<String,Object> loadValues(T t,String [] methodNames){
//将属性名和属性值封装到Map中
Map<String,Object> params = new HashMap<String, Object>();
for (int i = 0; i < methodNames.length; i++) {
//每个get方法名
String fun = methodNames[i];
//System.out.println(fun);
Method method;
try {
//通过反射拿到对象属性对应的值
method = clazz.getDeclaredMethod(fun);
Object result = method.invoke(t); if(result != null){//设置了值属性
// System.out.println(fun +" = " +result);
if ( fun.contains(mainId.value() )) {//根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
params.put(mainId.value(), result);
havaValues.add(mainId.value());
} else {
for (int j = 0; j < columns.size(); j++) {
if (fun.contains( columns.get(j) )) {
params.put(columns.get(j) , result);
havaValues.add(columns.get(j));
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return params;
}
  • 到这里本文利用反射获取实例的信息基本上结束了,下面将是与数据库操作的其他方法,其中update在封装sql语句是和add有细微的差异
  • 除了update方法外,其他数据库操作方法并没有太大难度
  • public void update(T t) {}
 /**
* 更新:要求必须把主键带上
* @param t
*/
public void update(T t) {
//为sql语句的字段装载数据
Map<String,Object> params = loadData(t); String sql = "update ";
sql += tableName + " set "; // System.out.println("havaValues="+havaValues);
for (int i = 1; i < havaValues.size() ; i++) {
sql+= havaValues.get(i) + "=?";
if(i < havaValues.size() - 1){
sql+=" , ";
}
}
sql+=" where " + mainId.value() + "=?";
System.out.println(sql); //为执行sql语句的?赋值
Object [] params2 = new Object[params.size()];
for (int i = 1; i < params.size(); i++) {
params2[i-1] = params.get(havaValues.get(i));
}
params2[params.size()-1] = params.get(havaValues.get(0)); // for (int i = 0; i < params2.length; i++) {
// System.out.println(params2[i]);
// } try {
qr.update(sql, params2);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
  • public void delete(String id){}
    /**
* 根据主键删除某条记录
* @param id
*/
public void delete(String id){
String sql = "delete from ";
sql += tableName;
sql += " where " + mainId.value() + "=?";
System.out.println(sql); try {
qr.update(sql, id);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
  • public T findById(String id){}
    /**
* 根据主键查找
* @param id
* @return
*/
public T findById(String id){
String sql = "select * from ";
sql += tableName;
sql += " where " + mainId.value() + "=?";
System.out.println(sql);
T t;
try {
t = qr.query(sql, new BeanHandler<T>(
(Class<T>) clazz.newInstance().getClass()), id);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return t;
}
  • public List<T> findAll(){}
    /**
* 查询所有
* @return
*/
public List<T> findAll(){
String sql = "select * from ";
sql += tableName;
System.out.println(sql);
List<T> list = new ArrayList<T>();
try {
list = qr.query(sql, new BeanListHandler<T>(
(Class<T>) clazz.newInstance().getClass() ) );
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return list;
}
}//这个分号是类定义结束,请自动忽略
  • BaseDao代码写完,下面给出BaseDao的完整代码,方便查阅

 package com.project.util;

 import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler; import com.project.domain.Column;
import com.project.domain.ID;
import com.project.domain.Table; /**
* 数据层基类:能够使数据层的dao直接使用BaseDao已经封装的方法
* @author Poter
* 2015年11月22日年下午3:47:16
* @param <T>
*/
public abstract class BaseDao<T> {
private QueryRunner qr = new TxQueryRunner();
private Class<?> clazz;//T的实例类型
private Table table;//表名的注解对象
private String tableName ;//表名
private ID mainId = null;//主键列
private List<Column> colus = new ArrayList<Column>();//普通列注解对象集合 private List<String> columns = new ArrayList<String>();//普通列 //所有的成员变量
private Field[] fields;
//包含主键列以及普通列名
private List<String> allParams = new ArrayList<String>();
//表示有赋值的所有属性,属性值为null的属性不会添加到havaValues
private List<String> havaValues = new ArrayList<String>(); /**
* 获取子类的相关信息
*/
public BaseDao() {
//获取范型类的类类型
clazz = (Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
System.out.println("T的实例类型->" + clazz.getSimpleName()); table = clazz.getAnnotation(Table.class);
tableName = table.value(); //实例成员变量
fields = clazz.getDeclaredFields(); //获取类的成员变量
for (int i = 0; i < fields.length; i++) {
ID id = fields[i].getAnnotation(ID.class);
if(id != null){
mainId = id;
}
Column column = fields[i].getAnnotation(Column.class);
if(column != null){
// System.out.println(column.value());
colus.add(column);
columns.add(column.value());
}
}
} /**
* 添加
* @param t
*/
public void add(T t){
//insert into tb_user values(?,?,?,?)
String sql = "insert into " + tableName +" values(";
for (int i = 0; i < fields.length ; i++) {
sql+="?";
if(i < fields.length - 1){
sql+=",";
}
}
sql+=")";
System.out.println(sql); //为sql语句的字段装载数据
Map<String,Object> params = loadData(t); allParams.add(mainId.value());
for (int i = 0; i < columns.size(); i++) {
allParams.add(columns.get(i));
}
System.out.println("allParams = "+ allParams); //为执行sql语句的?赋值
Object [] params2 = new Object[params.size()];
for (int i = 0; i < params.size(); i++) {
params2[i] = params.get(allParams.get(i));
} // for (int i = 0; i < params2.length; i++) {
// System.out.println(params2[i]);
// } try {
qr.update(sql, params2);
} catch (SQLException e) {
throw new RuntimeException(e);
} } /**
* 加载全部数据:按照实体类成员书写顺序先后赋值
* @param t
* @return
*/
private <T> Map<String,Object> loadData(T t) {
String [] methodNames = loadMethods();
Map<String,Object> params = loadValues(t,methodNames);
return params;
} /**
* 加载所有的get方法
* @return
*/
private String[] loadMethods() {
//获取该类自身声明的方法,这里包含Getter、Setter、toString
Method[] methods = clazz.getDeclaredMethods(); //测试
try {
// Method tostring = clazz.getDeclaredMethod("toString");
// String res = (String) tostring.invoke(t);
// System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
} // System.out.println(methods.length); //用来存放所有的get方法名
String [] methodNames = new String[columns.size()+1];//要加上id属性 int x = -1;
for (int i = 0; i < methods.length; i++) {
//获取所有的方法名称
String fun = methods[i].getName();
//获取所有的get方法
if(fun.contains("get")){
x++;
methodNames[x] = fun;
}
}
return methodNames;
} /**
* 加载属性值,并将属性名和属性值一起封装到Map中
* @param t
* @param methodNames
* @return
*/
private <T>Map<String,Object> loadValues(T t,String [] methodNames){
//将属性名和属性值封装到Map中
Map<String,Object> params = new HashMap<String, Object>();
for (int i = 0; i < methodNames.length; i++) {
//每个get方法名
String fun = methodNames[i];
//System.out.println(fun);
Method method;
try {
//通过反射拿到对象属性对应的值
method = clazz.getDeclaredMethod(fun);
Object result = method.invoke(t); if(result != null){//设置了值属性
// System.out.println(fun +" = " +result);
if ( fun.contains(mainId.value() )) {//根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
params.put(mainId.value(), result);
havaValues.add(mainId.value());
} else {
for (int j = 0; j < columns.size(); j++) {
if (fun.contains( columns.get(j) )) {
params.put(columns.get(j) , result);
havaValues.add(columns.get(j));
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return params;
} /**
* 更新:要求必须把主键带上
* @param t
*/
public void update(T t) {
//为sql语句的字段装载数据
Map<String,Object> params = loadData(t); String sql = "update ";
sql += tableName + " set "; // System.out.println("havaValues="+havaValues);
for (int i = 1; i < havaValues.size() ; i++) {
sql+= havaValues.get(i) + "=?";
if(i < havaValues.size() - 1){
sql+=" , ";
}
}
sql+=" where " + mainId.value() + "=?";
System.out.println(sql); //为执行sql语句的?赋值
Object [] params2 = new Object[params.size()];
for (int i = 1; i < params.size(); i++) {
params2[i-1] = params.get(havaValues.get(i));
}
params2[params.size()-1] = params.get(havaValues.get(0)); // for (int i = 0; i < params2.length; i++) {
// System.out.println(params2[i]);
// } try {
qr.update(sql, params2);
} catch (Exception e) {
throw new RuntimeException(e);
}
} /**
* 根据主键删除某条记录
* @param id
*/
public void delete(String id){
String sql = "delete from ";
sql += tableName;
sql += " where " + mainId.value() + "=?";
System.out.println(sql); try {
qr.update(sql, id);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} /**
* 根据主键查找
* @param id
* @return
*/
public T findById(String id){
String sql = "select * from ";
sql += tableName;
sql += " where " + mainId.value() + "=?";
System.out.println(sql);
T t;
try {
t = qr.query(sql, new BeanHandler<T>(
(Class<T>) clazz.newInstance().getClass()), id);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return t;
} /**
* 查询所有
* @return
*/
public List<T> findAll(){
String sql = "select * from ";
sql += tableName;
System.out.println(sql);
List<T> list = new ArrayList<T>();
try {
list = qr.query(sql, new BeanListHandler<T>(
(Class<T>) clazz.newInstance().getClass() ) );
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return list;
}
}

  • 下面进入测试部分

五、CustomerDao.java和UserDao.java--开始测试BaseDao

  • public class UserDao extends BaseDao<User>{}
  • 包含了增删改查方法,同时给出了对应的测试部分,用到了Junit测试
 package com.project.dao;

 import java.util.List;

 import org.junit.Test;

 import com.project.domain.User;
import com.project.util.BaseDao; public class UserDao extends BaseDao<User>{ @Override
public void add(User user) {
super.add(user);
} @Test
public void fun1(){
User user = new User();
user.setId("110");
user.setUname("警察");
user.setPwd("admin");
user.setEmail("110@qq.com");
add(user);
} @Override
public List<User> findAll() {
return super.findAll();
} @Test
public void fun2(){
List<User> list = findAll();
System.out.println(list);
} @Override
public User findById(String id) {
return super.findById(id);
} @Test
public void fun3(){
User user = findById("hehe");
System.out.println(user);
} @Override
public void delete(String id) {
super.delete(id);
} @Test
public void fun4(){
delete("haha");
} @Override
public void update(User t) {
super.update(t);
} @Test
public void fun5(){
User user = new User();
user.setId("110");
user.setUname("强盗");
user.setPwd("admin");
user.setEmail("911@qq.com");
update(user);
} @Test
public void fun6(){
User user = new User();
user.setId("110");
user.setEmail("kkk@qq.com");
update(user);
} } 
  • 数据库表中内容

不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理

  • public class CustomerDao extends BaseDao<Customer>{}
 package com.project.dao;

 import java.util.List;

 import org.junit.Test;

 import com.project.domain.Customer;
import com.project.util.BaseDao; public class CustomerDao extends BaseDao<Customer>{ @Override
public void add(Customer customer) {
super.add(customer);
} @Test
public void fun1(){
Customer c = new Customer();
c.setCid("66666666");
c.setCname("Pack");
c.setSpent(50);
add(c);
} @Override
public List<Customer> findAll() {
return super.findAll();
} @Test
public void fun2(){
List<Customer> list = findAll();
System.out.println(list);
} @Override
public Customer findById(String id) {
return super.findById(id);
} @Test
public void fun3(){
Customer customer = findById("66666666");
System.out.println(customer);
} @Override
public void delete(String id) {
super.delete(id);
} @Test
public void fun4(){
delete("2222");
} @Override
public void update(Customer customer) {
super.update(customer);
} @Test
public void fun5(){
Customer customer = new Customer();
customer.setCid("66666666");
// customer.setCname("boss");
customer.setSpent(99999999);
update(customer);
} }
  • 数据库表中内容

不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理


  • 代码撸完,该总结了

六、总结

在这个BaseDao里面没有考虑到事务处理问题,是这个BaseDao最严重的问题。当然本文也只是想说明利用反射机制实现的一点简单对数据库操作的封装,而没有考虑到如何更加细化的操作数据,如有更好的改进,欢迎在评论区讨论。

  • 版权声明:本文为博主原创文章,未经博主允许不得转载。