我是靠谱客的博主 落寞薯片,最近开发中收集的这篇文章主要介绍不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理版权声明:本文为博主原创文章,未经博主允许不得转载。一、写实体类--Demo的起步二、注解类--反射的前奏三、JdbcUtils.java和TxQueryRunner.java--工具类方便操作数据库四、BaseDao.java--好戏来了五、CustomerDao.java和UserDao.java--开始测试BaseDao六、总结 版权声明:本文为博主原创文章,未经博主允许不得转载。,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

今天写了个万能的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

 

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

 

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

 

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

 

1、User.java

 1 package com.project.domain;
 2
 3 import java.util.List;
 4
 5 @Table("t_user")
 6 public class User {
 7
/**
 8 
* 该id表示主键
 9
*/
10
@ID("Id")
11
private String id;
12
13
@Column("Uname")
14
private String uname;//用户名
15
16
@Column("Pwd")
17
private String pwd;//密码
18
19
@Column("Email")
20
private String email;
21
22
public String getId() {
23
return id;
24 
}
25
public void setId(String id) {
26
this.id = id;
27 
}
28
public String getUname() {
29
return uname;
30 
}
31
public void setUname(String uname) {
32
this.uname = uname;
33 
}
34
public String getPwd() {
35
return pwd;
36 
}
37
public void setPwd(String pwd) {
38
this.pwd = pwd;
39 
}
40
public String getEmail() {
41
return email;
42 
}
43
public void setEmail(String email) {
44
this.email = email;
45 
}
46 
@Override
47
public String toString() {
48
return "User [id=" + id + ", uname=" + uname + ", pwd=" + pwd
49
+ ", email=" + email + "]";
50 
}
51
52 }

 

  • User类在数据库中对应的表结构

 

 

 

2、Customer.java

 1 package com.project.domain;
 2
 3 @Table("t_customer")
 4 public class Customer {
 5
@ID("Cid")
 6
private String cid;
 7
 8
@Column("Cname")
 9
private String cname;
10
11
@Column("Spent")
12
private int spent;
13
14
public String getCid() {
15
return cid;
16 
}
17
18
public void setCid(String cid) {
19
this.cid = cid;
20 
}
21
22
public String getCname() {
23
return cname;
24 
}
25
26
public void setCname(String cname) {
27
this.cname = cname;
28 
}
29
30
public int getSpent() {
31
return spent;
32 
}
33
34
public void setSpent(int spent) {
35
this.spent = spent;
36 
}
37
38 
@Override
39
public String toString() {
40
return "Customer [cid=" + cid + ", cname=" + cname + ", spent=" + spent
41
+ "]";
42 
}
43
44
45 }

 

  • Customer类在数据库中对应的表结构

 

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

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

 

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

1 package com.project.domain;
2
3 import java.lang.annotation.Retention;
4 import java.lang.annotation.RetentionPolicy;
5
6 @Retention(value=RetentionPolicy.RUNTIME)
7 public @interface Table {
8 
String value();
9 }

 

 

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

 

1 package com.project.domain;
2
3 import java.lang.annotation.Retention;
4 import java.lang.annotation.RetentionPolicy;
5
6 @Retention(value=RetentionPolicy.RUNTIME)
7 public @interface ID {
8 
String value();
9 }

 

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

 

1 package com.project.domain;
2
3 import java.lang.annotation.Retention;
4 import java.lang.annotation.RetentionPolicy;
5
6 @Retention(value=RetentionPolicy.RUNTIME)
7 public @interface Column {
8 
String value();
9 }

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

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

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

 

 

 1 package com.project.util;
 2
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5
 6 import javax.sql.DataSource;
 7
 8 import com.mchange.v2.c3p0.ComboPooledDataSource;
 9
10 /**
11  * 使用本类的方法,必须提供c3p0-copnfig.xml文件
12  * @author qdmmy6
13
*/
14 public class JdbcUtils {
15
// 饿汉式
16
private static DataSource ds = new ComboPooledDataSource();
17
18
/**
19 
* 它为null表示没有事务
20 
* 它不为null表示有事务
21 
* 当开启事务时,需要给它赋值
22 
* 当结束事务时,需要给它赋值为null
23 
* 并且在开启事务时,让dao的多个方法共享这个Connection
24
*/
25
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
26
27
public static DataSource getDataSource() {
28
return ds;
29 
}
30
31
/**
32 
* dao使用本方法来获取连接
33 
* @return
34 
* @throws SQLException
35
*/
36
public static Connection getConnection() throws SQLException {
37
/*
38 
* 如果有事务,返回当前事务的con
39 
* 如果没有事务,通过连接池返回新的con
40
*/
41
Connection con = tl.get();//获取当前线程的事务连接
42
if(con != null) return con;
43
return ds.getConnection();
44 
}
45
46
/**
47 
* 开启事务
48 
* @throws SQLException
49
*/
50
public static void beginTransaction() throws SQLException {
51
Connection con = tl.get();//获取当前线程的事务连接
52
if(con != null) throw new SQLException("已经开启了事务,不能重复开启!");
53
con = ds.getConnection();//给con赋值,表示开启了事务
54
con.setAutoCommit(false);//设置为手动提交
55
tl.set(con);//把当前事务连接放到tl中
56 
}
57
58
/**
59 
* 提交事务
60 
* @throws SQLException
61
*/
62
public static void commitTransaction() throws SQLException {
63
Connection con = tl.get();//获取当前线程的事务连接
64
if(con == null) throw new SQLException("没有事务不能提交!");
65
con.commit();//提交事务
66
con.close();//关闭连接
67
con = null;//表示事务结束!
68 
tl.remove();
69 
}
70
71
/**
72 
* 回滚事务
73 
* @throws SQLException
74
*/
75
public static void rollbackTransaction() throws SQLException {
76
Connection con = tl.get();//获取当前线程的事务连接
77
if(con == null) throw new SQLException("没有事务不能回滚!");
78 
con.rollback();
79 
con.close();
80
con = null;
81 
tl.remove();
82 
}
83
84
/**
85 
* 释放Connection
86 
* @param con
87 
* @throws SQLException
88
*/
89
public static void releaseConnection(Connection connection) throws SQLException {
90
Connection con = tl.get();//获取当前线程的事务连接
91
if(connection != con) {//如果参数连接,与当前事务连接不同,说明这个连接不是当前事务,可以关闭!
92
if(connection != null &&!connection.isClosed()) {//如果参数连接没有关闭,关闭之!
93 
connection.close();
94 
}
95 
}
96 
}
97 }

 

  • TxQueryRunner.java,该类重写了dbutils.jar中的QueryRunner.java,为每个方法添加了打开连接和关闭的操作,这样优化了数据库连接。
 1 package com.project.util;
 2
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5
 6 import org.apache.commons.dbutils.QueryRunner;
 7 import org.apache.commons.dbutils.ResultSetHandler;
 8
 9 public class TxQueryRunner extends QueryRunner {
10
11 
@Override
12
public int[] batch(String sql, Object[][] params) throws SQLException {
13
Connection con = JdbcUtils.getConnection();
14
int[] result = super.batch(con, sql, params);
15 
JdbcUtils.releaseConnection(con);
16
return result;
17 
}
18
19 
@Override
20
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
21
throws SQLException {
22
Connection con = JdbcUtils.getConnection();
23
T result = super.query(con, sql, rsh, params);
24 
JdbcUtils.releaseConnection(con);
25
return result;
26 
}
27
28 
@Override
29
public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
30
Connection con = JdbcUtils.getConnection();
31
T result = super.query(con, sql, rsh);
32 
JdbcUtils.releaseConnection(con);
33
return result;
34 
}
35
36 
@Override
37
public int update(String sql) throws SQLException {
38
Connection con = JdbcUtils.getConnection();
39
int result = super.update(con, sql);
40 
JdbcUtils.releaseConnection(con);
41
return result;
42 
}
43
44 
@Override
45
public int update(String sql, Object param) throws SQLException {
46
Connection con = JdbcUtils.getConnection();
47
int result = super.update(con, sql, param);
48 
JdbcUtils.releaseConnection(con);
49
return result;
50 
}
51
52 
@Override
53
public int update(String sql, Object... params) throws SQLException {
54
Connection con = JdbcUtils.getConnection();
55
int result = super.update(con, sql, params);
56 
JdbcUtils.releaseConnection(con);
57
return result;
58 
}
59 }

四、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>
 1 package com.project.util;
 2
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.ParameterizedType;
 6 import java.sql.SQLException;
 7 import java.util.ArrayList;
 8 import java.util.HashMap;
 9 import java.util.List;
10 import java.util.Map;
11
12 import org.apache.commons.dbutils.QueryRunner;
13 import org.apache.commons.dbutils.handlers.BeanHandler;
14 import org.apache.commons.dbutils.handlers.BeanListHandler;
15
16 import com.project.domain.Column;
17 import com.project.domain.ID;
18 import com.project.domain.Table;
19
20 /**
21  * 数据层基类:能够使数据层的dao直接使用BaseDao已经封装的方法
22  * @author Poter
23  * 2015年11月22日年下午3:47:16
24  * @param <T>
25
*/
26 public abstract class BaseDao<T> {
27
private QueryRunner qr = new TxQueryRunner();
28
private Class<?> clazz;//T的实例类型
29
private Table table;//表名的注解对象
30
private String tableName ;//表名
31
private ID mainId = null;//主键列
32
private List<Column> colus = new ArrayList<Column>();//普通列注解对象集合
33
34
private List<String> columns = new ArrayList<String>();//普通列
35
36
//所有的成员变量
37
private Field[] fields;
38
//包含主键列以及普通列名
39
private List<String> allParams = new ArrayList<String>();
40
//表示有赋值的所有属性,属性值为null的属性不会添加到havaValues
41
private List<String> havaValues = new ArrayList<String>();
42
43
44
/**
45 
* 获取子类的相关信息
46
*/
47
public BaseDao() {
48
//获取范型类的类类型
49
clazz = (Class)((ParameterizedType)this.getClass().
50
getGenericSuperclass()).getActualTypeArguments()[0];
51
System.out.println("T的实例类型->" + clazz.getSimpleName());
52
53
table = clazz.getAnnotation(Table.class);
54
tableName = table.value();
55
56
//实例成员变量
57
fields = clazz.getDeclaredFields();
58
59
//获取类的成员变量
60
for (int i = 0; i < fields.length; i++) {
61
ID id = fields[i].getAnnotation(ID.class);
62
if(id != null){
63
mainId = id;
64 
}
65
Column column = fields[i].getAnnotation(Column.class);
66
if(column != null){
67 //
System.out.println(column.value());
68 
colus.add(column);
69 
columns.add(column.value());
70 
}
71 
}
72
}
  •  void add(T t),向数据库中插入一条记录
  • 先封装sql语句
  • 然后利用反射来加载每个成员变量对应的值,会用到一个 loadData(t) 方法,这是因为反射的操作代码很繁琐,卸载一个方法里影响查阅,同时写成一个方法主要是因为后面update还会用到这个方法,这样就会提高代码的利用率。要知道反射的关键内容,请继续往下看。

 

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

  • public void add(T t){}
 1     /**
 2 
* 添加
 3 
* @param t
 4
*/
 5
public void add(T t){
 6
//insert into tb_user values(?,?,?,?) 
 7
String sql = "insert into " + tableName +" values(";
 8
for (int i = 0; i < fields.length ; i++) {
 9
sql+="?";
10
if(i < fields.length - 1){
11
sql+=",";
12 
}
13 
}
14
sql+=")";
15 
System.out.println(sql);
16
17
//为sql语句的字段装载数据
18
Map<String,Object> params
= loadData(t);
19
20 
allParams.add(mainId.value());
21
for (int i = 0; i < columns.size(); i++) {
22 
allParams.add(columns.get(i));
23 
}
24
System.out.println("allParams = "+ allParams);
25
26
//为执行sql语句的?赋值
27
Object [] params2 = new Object[params.size()];
28
for (int i = 0; i < params.size(); i++) {
29
params2[i] = params.get(allParams.get(i));
30 
}
31
32 //
for (int i = 0; i < params2.length; i++) {
33 //
System.out.println(params2[i]);
34 //
}
35
36
try {
37 
qr.update(sql, params2);
38
} catch (SQLException e) {
39
throw new RuntimeException(e);
40 
}
41
42
}

 

  •  loadData(T t),之所以会将范型T传给loadData方法,是因为下面使用反射时会使用对应的实体类型
  •  private <T> Map<String,Object> loadData(T t) {}
  • 该方法中有出现了两个方法:loadMethods() 和 loadValues(t,methodNames)
 1
/**
 2 
* 加载全部数据:按照实体类成员书写顺序先后赋值
 3 
* @param t
 4 
* @return
 5
*/
 6
private <T> Map<String,Object> loadData(T t) {
 7
String [] methodNames = loadMethods();
 8
Map<String,Object> params
= loadValues(t,methodNames);
 9
return params;
10
}

 

 

  • loadMethods(),该方法用来加载实体类对应的所有的Getter方法
  • private <T> Map<String,Object> loadData(T t) {}
 1
/**
 2 
* 加载所有的get方法
 3 
* @return
 4
*/
 5
private String[] loadMethods() {
 6
//获取该类自身声明的方法,这里包含Getter、Setter、toString
 7
Method[] methods = clazz.getDeclaredMethods();
 8
 9
//测试
10
try {
11 //
Method tostring = clazz.getDeclaredMethod("toString");
12 //
String res = (String) tostring.invoke(t);
13 //
System.out.println(res);
14
}
catch (Exception e) {
15 
e.printStackTrace();
16 
}
17
18 //
System.out.println(methods.length);
19
20
//用来存放所有的get方法名
21
String [] methodNames = new
String[columns.size()+1];//要加上id属性
22
23
int x = -1;
24
for (int i = 0; i < methods.length; i++) {
25
//获取所有的方法名称
26
String fun = methods[i].getName();
27
//获取所有的get方法
28
if(fun.contains("get")){
29
x++;
30
methodNames[x] = fun;
31 
}
32 
}
33
return methodNames;
34
}

 

  • loadValues(t,methodNames),用来获取传给BaseDao的实体类的实例中赋值了属性值,且属性值必须包括主键Id,即Id属性不能为空
  • 利用反射区实现属性名称对应的Getter方法,从而获取到实例的属性值,这是本文中利用反射最关键的地方
  • 根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
  • private <T>Map<String,Object> loadValues(T t,String [] methodNames){}
 1
 /**
 2 
* 加载属性值,并将属性名和属性值一起封装到Map中
 3 
* @param t
 4 
* @param methodNames
 5 
* @return
 6
*/
 7
private <T>Map<String,Object> loadValues(T t,String [] methodNames){
 8
//将属性名和属性值封装到Map中
 9
Map<String,Object> params
= new HashMap<String, Object>();
10
for (int i = 0; i < methodNames.length; i++) {
11
//每个get方法名
12
String fun = methodNames[i];
13
//System.out.println(fun);
14 
Method method;
15
try {
16
//通过反射拿到对象属性对应的值
17
method = clazz.getDeclaredMethod(fun);
18
Object result = method.invoke(t);
19
20
if(result != null){//设置了值属性
21
//
System.out.println(fun +" = " +result);
22
if ( fun.contains(mainId.value() )) {//根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
23 
params.put(mainId.value(), result);
24 
havaValues.add(mainId.value());
25
} else {
26
for (int j = 0; j < columns.size(); j++) {
27
if (fun.contains( columns.get(j) )) {
28 
params.put(columns.get(j) , result);
29 
havaValues.add(columns.get(j));
30
break;
31 
}
32 
}
33 
}
34 
}
35
}
catch (Exception e) {
36 
e.printStackTrace();
37 
}
38 
}
39
return params;
40
}

 

 

  •  到这里本文利用反射获取实例的信息基本上结束了,下面将是与数据库操作的其他方法,其中update在封装sql语句是和add有细微的差异
  • 除了update方法外,其他数据库操作方法并没有太大难度
  • public void update(T t) {}
 1 /**
 2 
* 更新:要求必须把主键带上
 3 
* @param t
 4
*/
 5
public void update(T t) {
 6
//为sql语句的字段装载数据
 7
Map<String,Object> params
= loadData(t);
 8
 9
String sql = "update ";
10
sql += tableName + " set ";
11
12 //
System.out.println("havaValues="+havaValues);
13
for (int i = 1; i < havaValues.size() ; i++) {
14
sql+= havaValues.get(i) + "=?";
15
if(i < havaValues.size() - 1){
16
sql+=" , ";
17 
}
18 
}
19
sql+=" where " + mainId.value() + "=?";
20 
System.out.println(sql);
21
22
//为执行sql语句的?赋值
23
Object [] params2 = new Object[params.size()];
24
for (int i = 1; i < params.size(); i++) {
25
params2[i-1] = params.get(havaValues.get(i));
26 
}
27
params2[params.size()-1] = params.get(havaValues.get(0));
28
29 //
for (int i = 0; i < params2.length; i++) {
30 //
System.out.println(params2[i]);
31 //
}
32
33
try {
34 
qr.update(sql, params2);
35
} catch (Exception e) {
36
throw new RuntimeException(e);
37 
}
38
}

 

  • public void delete(String id){}
 1
/**
 2 
* 根据主键删除某条记录
 3 
* @param id
 4
*/
 5
public void delete(String id){
 6
String sql = "delete
from ";
 7
sql += tableName;
 8
sql += " where " + mainId.value() + "=?";
 9 
System.out.println(sql);
10
11
try {
12 
qr.update(sql, id);
13
} catch (Exception e) {
14 
e.printStackTrace();
15
throw new RuntimeException(e);
16 
}
17
}

 

 

  • public T findById(String id){}
 1
/**
 2 
* 根据主键查找
 3 
* @param id
 4 
* @return
 5
*/
 6
public T findById(String id){
 7
String sql = "select * from ";
 8
sql += tableName;
 9
sql += " where " + mainId.value() + "=?";
10 
System.out.println(sql);
11 
T t;
12
try {
13
t = qr.query(sql, new BeanHandler<T>(
14
(Class<T>) clazz.newInstance().getClass()), id);
15
} catch (Exception e) {
16 
e.printStackTrace();
17
throw new RuntimeException(e);
18 
}
19
return t;
20
}

 

 

  •  public List<T> findAll(){}
 1
/**
 2 
* 查询所有
 3 
* @return
 4
*/
 5
public List<T> findAll(){
 6
String sql = "select * from ";
 7
sql += tableName;
 8 
System.out.println(sql);
 9
List<T> list = new ArrayList<T>();
10
try {
11
list = qr.query(sql, new BeanListHandler<T>(
12
(Class<T>) clazz.newInstance().getClass() ) );
13
} catch (Exception e) {
14 
e.printStackTrace();
15
throw new RuntimeException(e);
16 
}
17
return list;
18 
}
19 }//这个分号是类定义结束,请自动忽略

 

  •  BaseDao代码写完,下面给出BaseDao的完整代码,方便查阅

 


1 package com.project.util;

2

3 import java.lang.reflect.Field;

4 import java.lang.reflect.Method;

5 import java.lang.reflect.ParameterizedType;

6 import java.sql.SQLException;

7 import java.util.ArrayList;

8 import java.util.HashMap;

9 import java.util.List;
 10 import java.util.Map;
 11
 12 import org.apache.commons.dbutils.QueryRunner;
 13 import org.apache.commons.dbutils.handlers.BeanHandler;
 14 import org.apache.commons.dbutils.handlers.BeanListHandler;
 15
 16 import com.project.domain.Column;
 17 import com.project.domain.ID;
 18 import com.project.domain.Table;
 19
 20 /**
 21  * 数据层基类:能够使数据层的dao直接使用BaseDao已经封装的方法
 22  * @author Poter
 23  * 2015年11月22日年下午3:47:16
 24  * @param <T>
 25
*/
 26 public abstract class BaseDao<T> {
 27
private QueryRunner qr = new TxQueryRunner();
 28
private Class<?> clazz;//T的实例类型
 29
private Table table;//表名的注解对象
 30
private String tableName ;//表名
 31
private ID mainId = null;//主键列
 32
private List<Column> colus = new ArrayList<Column>();//普通列注解对象集合
 33
 34
private List<String> columns = new ArrayList<String>();//普通列
 35
 36
//所有的成员变量
 37
private Field[] fields;
 38
//包含主键列以及普通列名
 39
private List<String> allParams = new ArrayList<String>();
 40
//表示有赋值的所有属性,属性值为null的属性不会添加到havaValues
 41
private List<String> havaValues = new ArrayList<String>();
 42
 43
 44
/**
 45 
* 获取子类的相关信息
 46
*/
 47
public BaseDao() {
 48
//获取范型类的类类型
 49
clazz = (Class)((ParameterizedType)this.getClass().
 50
getGenericSuperclass()).getActualTypeArguments()[0];
 51
System.out.println("T的实例类型->" + clazz.getSimpleName());
 52
 53
table = clazz.getAnnotation(Table.class);
 54
tableName = table.value();
 55
 56
//实例成员变量
 57
fields = clazz.getDeclaredFields();
 58
 59
//获取类的成员变量
 60
for (int i = 0; i < fields.length; i++) {
 61
ID id = fields[i].getAnnotation(ID.class);
 62
if(id != null){
 63
mainId = id;
 64 
}
 65
Column column = fields[i].getAnnotation(Column.class);
 66
if(column != null){
 67 //
System.out.println(column.value());
 68 
colus.add(column);
 69 
columns.add(column.value());
 70 
}
 71 
}
 72 
}
 73
 74
/**
 75 
* 添加
 76 
* @param t
 77
*/
 78
public void add(T t){
 79
//insert into tb_user values(?,?,?,?) 
 80
String sql = "insert into " + tableName +" values(";
 81
for (int i = 0; i < fields.length ; i++) {
 82
sql+="?";
 83
if(i < fields.length - 1){
 84
sql+=",";
 85 
}
 86 
}
 87
sql+=")";
 88 
System.out.println(sql);
 89
 90
//为sql语句的字段装载数据
 91
Map<String,Object> params
= loadData(t);
 92
 93 
allParams.add(mainId.value());
 94
for (int i = 0; i < columns.size(); i++) {
 95 
allParams.add(columns.get(i));
 96 
}
 97
System.out.println("allParams = "+ allParams);
 98
 99
//为执行sql语句的?赋值
100
Object [] params2 = new Object[params.size()];
101
for (int i = 0; i < params.size(); i++) {
102
params2[i] = params.get(allParams.get(i));
103 
}
104
105 //
for (int i = 0; i < params2.length; i++) {
106 //
System.out.println(params2[i]);
107 //
}
108
109
try {
110 
qr.update(sql, params2);
111
} catch (SQLException e) {
112
throw new RuntimeException(e);
113 
}
114
115 
}
116
117
/**
118 
* 加载全部数据:按照实体类成员书写顺序先后赋值
119 
* @param t
120 
* @return
121
*/
122
private <T> Map<String,Object> loadData(T t) {
123
String [] methodNames = loadMethods();
124
Map<String,Object> params
= loadValues(t,methodNames);
125
return params;
126 
}
127
128
/**
129 
* 加载所有的get方法
130 
* @return
131
*/
132
private String[] loadMethods() {
133
//获取该类自身声明的方法,这里包含Getter、Setter、toString
134
Method[] methods = clazz.getDeclaredMethods();
135
136
//测试
137
try {
138 //
Method tostring = clazz.getDeclaredMethod("toString");
139 //
String res = (String) tostring.invoke(t);
140 //
System.out.println(res);
141
}
catch (Exception e) {
142 
e.printStackTrace();
143 
}
144
145 //
System.out.println(methods.length);
146
147
//用来存放所有的get方法名
148
String [] methodNames = new
String[columns.size()+1];//要加上id属性
149
150
int x = -1;
151
for (int i = 0; i < methods.length; i++) {
152
//获取所有的方法名称
153
String fun = methods[i].getName();
154
//获取所有的get方法
155
if(fun.contains("get")){
156
x++;
157
methodNames[x] = fun;
158 
}
159 
}
160
return methodNames;
161 
}
162
163
/**
164 
* 加载属性值,并将属性名和属性值一起封装到Map中
165 
* @param t
166 
* @param methodNames
167 
* @return
168
*/
169
private <T>Map<String,Object> loadValues(T t,String [] methodNames){
170
//将属性名和属性值封装到Map中
171
Map<String,Object> params
= new HashMap<String, Object>();
172
for (int i = 0; i < methodNames.length; i++) {
173
//每个get方法名
174
String fun = methodNames[i];
175
//System.out.println(fun);
176 
Method method;
177
try {
178
//通过反射拿到对象属性对应的值
179
method = clazz.getDeclaredMethod(fun);
180
Object result = method.invoke(t);
181
182
if(result != null){//设置了值属性
183
//
System.out.println(fun +" = " +result);
184
if ( fun.contains(mainId.value() )) {//根据主键匹配其对应的Get方法,所以这里限制了列名必须包含在属性名类,目前还没有找到更好的解决方案
185 
params.put(mainId.value(), result);
186 
havaValues.add(mainId.value());
187
} else {
188
for (int j = 0; j < columns.size(); j++) {
189
if (fun.contains( columns.get(j) )) {
190 
params.put(columns.get(j) , result);
191 
havaValues.add(columns.get(j));
192
break;
193 
}
194 
}
195 
}
196 
}
197
}
catch (Exception e) {
198 
e.printStackTrace();
199 
}
200 
}
201
return params;
202 
}
203
204
/**
205 
* 更新:要求必须把主键带上
206 
* @param t
207
*/
208
public void update(T t) {
209
//为sql语句的字段装载数据
210
Map<String,Object> params
= loadData(t);
211
212
String sql = "update ";
213
sql += tableName + " set ";
214
215 //
System.out.println("havaValues="+havaValues);
216
for (int i = 1; i < havaValues.size() ; i++) {
217
sql+= havaValues.get(i) + "=?";
218
if(i < havaValues.size() - 1){
219
sql+=" , ";
220 
}
221 
}
222
sql+=" where " + mainId.value() + "=?";
223 
System.out.println(sql);
224
225
//为执行sql语句的?赋值
226
Object [] params2 = new Object[params.size()];
227
for (int i = 1; i < params.size(); i++) {
228
params2[i-1] = params.get(havaValues.get(i));
229 
}
230
params2[params.size()-1] = params.get(havaValues.get(0));
231
232 //
for (int i = 0; i < params2.length; i++) {
233 //
System.out.println(params2[i]);
234 //
}
235
236
try {
237 
qr.update(sql, params2);
238
} catch (Exception e) {
239
throw new RuntimeException(e);
240 
}
241 
}
242
243
/**
244 
* 根据主键删除某条记录
245 
* @param id
246
*/
247
public void delete(String id){
248
String sql = "delete
from ";
249
sql += tableName;
250
sql += " where " + mainId.value() + "=?";
251 
System.out.println(sql);
252
253
try {
254 
qr.update(sql, id);
255
} catch (Exception e) {
256 
e.printStackTrace();
257
throw new RuntimeException(e);
258 
}
259 
}
260
261
/**
262 
* 根据主键查找
263 
* @param id
264 
* @return
265
*/
266
public T findById(String id){
267
String sql = "select * from ";
268
sql += tableName;
269
sql += " where " + mainId.value() + "=?";
270 
System.out.println(sql);
271 
T t;
272
try {
273
t = qr.query(sql, new BeanHandler<T>(
274
(Class<T>) clazz.newInstance().getClass()), id);
275
} catch (Exception e) {
276 
e.printStackTrace();
277
throw new RuntimeException(e);
278 
}
279
return t;
280 
}
281
282
/**
283 
* 查询所有
284 
* @return
285
*/
286
public List<T> findAll(){
287
String sql = "select * from ";
288
sql += tableName;
289 
System.out.println(sql);
290
List<T> list = new ArrayList<T>();
291
try {
292
list = qr.query(sql, new BeanListHandler<T>(
293
(Class<T>) clazz.newInstance().getClass() ) );
294
} catch (Exception e) {
295 
e.printStackTrace();
296
throw new RuntimeException(e);
297 
}
298
return list;
299 
}
300 }
View Code

 

 


  • 下面进入测试部分

五、CustomerDao.javaUserDao.java--开始测试BaseDao

  • public class UserDao extends BaseDao<User>{}
  • 包含了增删改查方法,同时给出了对应的测试部分,用到了Junit测试
 1 package com.project.dao;
 2
 3 import java.util.List;
 4
 5 import org.junit.Test;
 6
 7 import com.project.domain.User;
 8 import com.project.util.BaseDao;
 9
10 public class UserDao extends BaseDao<User>{
11
12 
@Override
13
public void add(User user) {
14
super.add(user);
15 
}
16
17 
@Test
18
public void fun1(){
19
User user = new User();
20
user.setId("110");
21
user.setUname("警察");
22
user.setPwd("admin");
23
user.setEmail("110@qq.com");
24 
add(user);
25 
}
26
27 
@Override
28
public List<User> findAll() {
29
return super.findAll();
30 
}
31
32 
@Test
33
public void fun2(){
34
List<User> list = findAll();
35 
System.out.println(list);
36 
}
37
38
39 
@Override
40
public User findById(String id) {
41
return super.findById(id);
42 
}
43
44 
@Test
45
public void fun3(){
46
User user = findById("hehe");
47 
System.out.println(user);
48 
}
49
50 
@Override
51
public void delete(String id) {
52
super.delete(id);
53 
}
54
55 
@Test
56
public void fun4(){
57
delete("haha");
58 
}
59
60 
@Override
61
public void update(User t) {
62
super.update(t);
63 
}
64
65 
@Test
66
public void fun5(){
67
User user = new User();
68
user.setId("110");
69
user.setUname("强盗");
70
user.setPwd("admin");
71
user.setEmail("911@qq.com");
72 
update(user);
73 
}
74
75 
@Test
76
public void fun6(){
77
User user = new User();
78
user.setId("110");
79
user.setEmail("kkk@qq.com");
80 
update(user);
81 
}
82
83
84
  • 数据库表中内容

 

 

  • public class CustomerDao extends BaseDao<Customer>{}

 

 1 package com.project.dao;
 2
 3 import java.util.List;
 4
 5 import org.junit.Test;
 6
 7 import com.project.domain.Customer;
 8 import com.project.util.BaseDao;
 9
10 public class CustomerDao extends BaseDao<Customer>{
11
12 
@Override
13
public void add(Customer customer) {
14
super.add(customer);
15 
}
16
17 
@Test
18
public void fun1(){
19
Customer c = new Customer();
20
c.setCid("66666666");
21
c.setCname("Pack");
22
c.setSpent(50);
23 
add(c);
24 
}
25
26 
@Override
27
public List<Customer> findAll() {
28
return super.findAll();
29 
}
30
31 
@Test
32
public void fun2(){
33
List<Customer> list = findAll();
34 
System.out.println(list);
35 
}
36
37 
@Override
38
public Customer findById(String id) {
39
return super.findById(id);
40 
}
41
42 
@Test
43
public void fun3(){
44
Customer customer = findById("66666666");
45 
System.out.println(customer);
46 
}
47
48 
@Override
49
public void delete(String id) {
50
super.delete(id);
51 
}
52
53 
@Test
54
public void fun4(){
55
delete("2222");
56 
}
57
58 
@Override
59
public void update(Customer customer) {
60
super.update(customer);
61 
}
62
63 
@Test
64
public void fun5(){
65
Customer customer = new Customer();
66
customer.setCid("66666666");
67 //
customer.setCname("boss");
68
customer.setSpent(99999999);
69 
update(customer);
70 
}
71
72 }

 

  • 数据库表中内容

 

 

 


 

  • 代码撸完,该总结了

六、总结

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

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

 

转载于:https://www.cnblogs.com/poterliu/p/4987233.html

最后

以上就是落寞薯片为你收集整理的不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理版权声明:本文为博主原创文章,未经博主允许不得转载。一、写实体类--Demo的起步二、注解类--反射的前奏三、JdbcUtils.java和TxQueryRunner.java--工具类方便操作数据库四、BaseDao.java--好戏来了五、CustomerDao.java和UserDao.java--开始测试BaseDao六、总结 版权声明:本文为博主原创文章,未经博主允许不得转载。的全部内容,希望文章能够帮你解决不依赖Hibernate的万能BaseDao---模仿了Hibernate底层的原理版权声明:本文为博主原创文章,未经博主允许不得转载。一、写实体类--Demo的起步二、注解类--反射的前奏三、JdbcUtils.java和TxQueryRunner.java--工具类方便操作数据库四、BaseDao.java--好戏来了五、CustomerDao.java和UserDao.java--开始测试BaseDao六、总结 版权声明:本文为博主原创文章,未经博主允许不得转载。所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部