概述
今天写了个万能的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类在数据库中对应的表结构
二、注解类--反射的前奏
- 使用了Table、ID、Column三个注解类,分别对应了数据库、主键、列名(字段),会在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 }
2、ID.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 }
3、Column.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.java和TxQueryRunner.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 }
- 下面进入测试部分
五、CustomerDao.java和UserDao.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六、总结 版权声明:本文为博主原创文章,未经博主允许不得转载。所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复