今天写了个万能的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类在数据库中对应的表结构

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类在数据库中对应的表结构

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

  • 使用了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);
} } 
  • 数据库表中内容

  • 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);
} }
  • 数据库表中内容


  • 代码撸完,该总结了

六、总结

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

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

不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理的更多相关文章

  1. HIbernate学习笔记(九) hibernate事务并发处理与乐观悲观锁

    事务并发处理 一. 数据库的隔离级别:并发性作用. 1.   ReadUncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到.)很少用 2.   ...

  2. Hibernate学习笔记(1)Hibernate构造

    一 准备工作 首先,我们将创建一个简单的基于控制台(console-based)Hibernate应用. 我们所做的第一件事就是创建我们的开发文件夹.并把所有需要用到的Java件放进去.解压缩从Hib ...

  3. hibernate框架学习第一天:hibernate介绍及基本操作

    框架辅助开发者进行开发,半成品软件,开发者与框架进行合作开发 Hibernate3Hibernate是一种基于Java的轻量级的ORM框架 基于Java:底层实现是Java语言,可以脱离WEB,在纯J ...

  4. 【Java EE 学习 44】【Hibernate学习第一天】【Hibernate对单表的CRUD操作】

    一.Hibernate简介 1.hibernate是对jdbc的二次开发 2.jdbc没有缓存机制,但是hibernate有. 3.hibernate的有点和缺点 (1)优点:有缓存,而且是二级缓存: ...

  5. 攻城狮在路上(壹) Hibernate(二)--- 第一个hibernate程序

    1.直接通过JDBC API持久化实体域对象: A.java.sql常用接口和类: DriverManager:驱动程序管理器,负责创建数据库连接. Connection:代表数据库连接. State ...

  6. org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xml

    org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xml at org.hibernate ...

  7. HIbernate学习笔记(二) hibernate对象的三种状态与核心开发接口

    1.在hibernate中持久化对象有三个状态,这个面试时可能会问到: (1)transient瞬时态:在数据库中没有与之匹配的数据,一般就是只new出了这个对象,并且在session缓存中也没有即此 ...

  8. Hibernate详解(5)——Hibernate核心接口和工作原理

    Hibernate核心接口 Hibernate有五大核心接口,分别是:Session Transaction Query SessionFactoryConfiguration .这五个接口构成了Hi ...

  9. [转]eclipse借助hibernate tool从数据库逆向生成Hibernate实体类

    如何从数据库逆向生成Hibernate实体类呢??? 1. 首先,要在eclipse中采用自带的数据库管理器(Data Management),连通你的数据库: 然后选择数据库,这里用的oracle, ...

随机推荐

  1. CentOS与Ubuntu修改主机名

    CentOS 1.执行hostname查看主机名 2.hostname + 主机名  使需要修改的主机名立即生效,但是下次重启会失效,故需要执行第三步 3.vim /etc/sysconfig/net ...

  2. Ubuntu上源码安装golang并设置开发环境

    安装go #wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz #tar -xzf go1.10.3.linux-amd64.tar.g ...

  3. PM2自动发布本地项目到服务器

    (一)开发环境 本地服务器 Ubunt14.04 远程服务器Ubuntu16.04 发布软件PM 2 (二)发布介绍 本地和我们的远程服务器,必须都安装有git,pm2,以及nodejs环境.实际上配 ...

  4. mui的picker组件填坑

    在公司项目用到mui,vue进行开发 import mui from './assets/js/mui.min.js'后 加window.mui = mui再 import './assets/js/ ...

  5. T-SQL查询处理执行顺序(一)

    对于T-SQL编程,用得最广泛的,莫过于查询(Querying).要想写出高质量.高性能的查询语句,必须深入地了解逻辑查询处理. 一.逻辑查询处理的各个阶段 (5)SELECT DISTINCT TO ...

  6. springboot+shiro+cas实现单点登录之shiro端搭建

    github:https://github.com/peterowang/shiro-cas 本文如有配置问题,请查看之前的springboot集成shiro的文章 1.配置ehcache缓存,在re ...

  7. spring data jpa 简单使用

    通过解析方法名创建查询 通过前面的例子,读者基本上对解析方法名创建查询的方式有了一个大致的了解,这也是 Spring Data JPA 吸引开发者的一个很重要的因素.该功能其实并非 Spring Da ...

  8. shareTo 网页版分享

    // share -------- var shareTo = function (dest, shareCode) { var appKey = "1667889534"; // ...

  9. encryptjs 加密 前端数据(vue 使用 RSA加密、java 后端 RSA解密)

    1.index.html引入 <script src="./static/js/jsencrypt.min.js"></script> 或者 npm i j ...

  10. git的常用操作指令

    git学习网址: http://www.backlogtool.com/git-guide/cn/intro/intro2_3.html 廖雪峰的git教程 git的工作区和暂存区(描述git的工作流 ...