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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值