知识屋:更实用的电脑技术知识网站
所在位置:首页 > 教育

java 通用dao

发表时间:2022-03-26来源:网络

前言

  反射在框架中的应用还是很多的,比如spring框架里面AOP用到的动态代理,而动态代理就是基于反射的。本文主要通过学习java的反射,最后编写通用的DAO类。最近在复习java web,在做数据库访问的时候做了一个事情。

  写一个CustomerDAO类(获取数据库连接,执行sql,获取resultSet,获取对象,关闭数据库等操作),用于根据用户名来获取一个用户对象。这个时候有一个新的需求,需要根据根据学号来获取一个学生对象,又需要编写一个StudentDAO类。当有更多的需求是时,我们就需要写大量的重复性代码。解决这个问题最好的方法是把他们公共的部分都提取出来做一个类。也就是说把获取一个类对象的方法抽取出来。

public class DAO{ //sql表示需要执行的sql语句,args表示查询用到的参数,比如可以根据学号,姓名等进行查询 T getObject(String sql, Object ... args){ //获取数据库连接 //执行sql //...... //关闭连接 } }

  这里用到了泛型,这个方法可以理解为返回的是一个随便什么类对象。那么问题来了,我们通过sql执行完查询之后,怎么知道他到底要返回什么样的对象呢。这里就需要用到反射了。

1.什么是反射

先说一下反射都有些什么功能:
    1. 在运行时可以构造一个类对象
    2. 可以获取这个类的属性
    3. 可以获取他的方法
反射就是在运行时可以获取一个类的内部信息的一个机制。用前面提到的来解释,在编写继承DAO< T >的类的时候,T可能写成Customer,也可能是Student;

public class CustomerDAO extends DAO{ public Costomer getCustomer(long CustomerId){ String sql = "SELECT * FROM student_tb WHERE ID = ?"; return getObject(sql,CustomerId); } }

  而在DAO< T >中我们需要从DAO< Customer >获取Customer类,并且在执行完sql过后还要把获取到的属性封装成一个Customer。这个过程中我们获取Customer类,还可以造一个对象,还可以执行这个对象的方法。这个过程就是一个反射。有的时候我们可能就知道一个对象,甚至就知道一个全类名。想要获取这个类的其他信息,就需要用到反射。

2.反射需要掌握哪些内容?

  1. Class类
  2.获取方法Method
  3.获取属性

3.Class类

  注意这里的Class中的"C"是大写

3.1Class是什么

  Class类的实例表示正在java程序中运行的类。Class封装了这个运行的类的属性、方法等信息。在Object类中可以发现有一个方法是:getClass();在文档中它的解释是返回运行时类。这个类里面有很多方法,比如获取这个运行类的名称,获取它的方法,还可以执行一些方法等等。
  

3.2获取Class的方式

方式一:通过类的class属性。已知类名 Class clazz = String.class; 方式二:通过getClass()方法来获取。已经有对象的情况 Class clazz = "这是一个字符串".getClass(); 方式三:通过全类名的方式获取。需要知道全类名 Class clazz = Class.forName("java.lang.String");

方式三可能用的比较多,比如我们在使用spring 的时候用到 IOC 容器的时候,配置一个bean就是用的这种方式。

另外还有一个很重要的方法就是 newInstance(); 在后面我们会用到,可以使用这个方法创建一个实例,前提是这个类要有默认的构造方法。 所以我在编写一个实体类的时候,写了带参数的构造方法就一定要写一个无参数的构造方法。(根据java基础我们知道,如果不写构造方法,系统会默认创建无参数的构造方法。一旦写了构造方法就不会默认创建了)

4.获取方法

方法也有相关的封装类------Method,在Class的对象clazz里面可以获取,
获取Method的方法有:

方法描述clazz.getMethods()获取类中的所有方法包括父类,但不能获取私有方法。返回Method数组clazz.getDeclaredMethods()获取本类的方法包括私有方法。返回Method数组public class TestMain { public static void main(String[] args) throws ClassNotFoundException { Class clazz = Customer.class; Method[] methods = clazz.getMethods(); //Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i String name; String age; public void publicSayHello(){ System.out.println("Hello!!"); } private void privateSayHello(String name){ System.out.println("Hello "+name); } }

下面是clazz.getMethods();执行的结果。它能够获取父类的Method。所有类的父类都是Object,所以会有equals、wait等方法。

这是clazz.getDeclaredMethods()执行的结果

我们还可以通过方法名和参数类型来获取一个Method对象

public class TestMain { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { Class clazz = Customer.class; Method method = clazz.getDeclaredMethod("publicSayHello",String.class); System.out.println(method.getName()); Object obj = clazz.newInstance();//使用newInsstance()可以创建一个对象 methods.invoke(obj,"阿璐4r");//可以通过这个invoke执行获取到的方法,obj代表对象后面表示参数 } } class Customer{ String name; String age; public void publicSayHello(String name){ System.out.println("Hello!!" + name); } }


通过看api文档会发现还有很多相关的方法,这里不一一说了

5.获取属性

关于属性/字段也有一个相应的封装类Field。Field的使用基本上和Method差不多

5.1获取字段

方法描述clazz.getDeclaredFields()获取类中的字段,包括私有的属性clazz.getDeclaredField(String name)通过字段名来获取属性

获取字段相关代码:

public class TestMain { public static void main(String[] args) throws NoSuchFieldException { Class clazz = Student.class; System.out.println("获取Student中的全部字段"); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName()); } System.out.println("根据字段名来获取字段"); Field field = clazz.getDeclaredField("name"); System.out.println(field.getName()); } } class Student{ private Integer id; String name; }

5.2设置和获取字段的值

public class TestMain { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Student student = new Student(110,"阿璐4r"); Class clazz = student.getClass(); Field field = clazz.getDeclaredField("name"); //设置字段的值 field.set(student,"修改后的值"); //获取字段的值 Object val = field.get(student); System.out.println(val); } } class Student{ private Integer id; String name; public Student() { } public Student(Integer id, String name) { this.id = id; this.name = name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public String getName() { return name; } }


这里值得我们一说的是,字段值的设置和修改是通过实体类的getXxx,setXxx方法的。这就是我们为什么在写实体类的时候需要写get,set方法,其实他留给了反射来使用。 注意的是get、set的方法名的写法是有讲究的,如:getName。get后面是字段名,但第一个字母大写。

6.回到最初的问题

对反射有了一个大致的了解了,现在在回到开始的问题。这个时候我们就可以引入一个新的技巧。先上代码。

public class DAO{ Class clazz; public DAO(){ clazz = 实际的Class;如果数CustomerDAO继承DAO, 那么这clazz就是Customer.class; } //sql表示需要执行的sql语句,args表示查询用到的参数,比如可以根据学号,姓名等进行查询 T getObject(String sql, Object ... args){ //获取数据库连接 //执行sql //...... //关闭连接 } }

我们在写CustomerDAO的时候可以继承DAO< Customer >,我们在写StudentDAO可以继承DAO< Student >。在DAO构造器中就可以获得相应的Class了。有了Class。我们的DAO代码就通用了,这样我们就是在获取getObject()方法中获取到相应的对象了。那么现在的问题是如何获取这个DAO< T >,T具体是什么。

7.编写通用DAO的构造器代码

public class DAO{ Class clazz; public DAO(){ Class cla = this.getClass();//根据我们学的基础知识,这里的this指的是CustomerDAO Type type = cla.getGenericSuperclass();//获取父类带里面的类型 if(type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type[] args = parameterizedType.getActualTypeArguments(); if(args != null && args.length > 0){ clazz = (Class) args[0]; } } System.out.println("DAO的构造方法 clazz:"+clazz.getName()); } //sql表示需要执行的sql语句,args表示查询用到的参数,比如可以根据学号,姓名等进行查询 T getObject(String sql, Object ... args){ //获取数据库连接 //执行sql //...... //关闭连接 } }

这里获取到了实际的Class

这段代码用到了与泛型相关东西
  1.Tyep:指的是一个类型
  2.ParameterizedType:指的是泛型类型,就是描述尖括号里面的东西的一个类
  3.getGenericSuperclass():获取父类的泛型类型。
  4.getActualTypeArguments() :获取实际泛型类型的数组

为了方便解释,这段代码写的不是很严谨。

8.反射工具类

通常我们会把反射写一个工具类,这个可以在网上找一下,包括spring里面都有相应反射工具类,大家可以研究研究。

import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * 反射的 Utils 函数集合 * 提供访问私有变量, 获取泛型类型 Class, 提取集合中元素属性等 Utils 函数 * @author Administrator */ public class ReflectionUtils { /** * 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型 * 如: public EmployeeDao extends BaseDao * @param clazz * @param index * @return */ public static Class getSuperClassGenricType(Class clazz, int index){ Type genType = clazz.getGenericSuperclass(); if(!(genType instanceof ParameterizedType)){ return Object.class; } Type [] params = ((ParameterizedType)genType).getActualTypeArguments(); if(index >= params.length || index return Object.class; } return (Class) params[index]; } /** * 通过反射, 获得 Class 定义中声明的父类的泛型参数类型 * 如: public EmployeeDao extends BaseDao * @param * @param clazz * @return */ @SuppressWarnings("unchecked") public static Class getSuperGenericType(Class clazz){ return getSuperClassGenricType(clazz, 0); } /** * 循环向上转型, 获取对象的 DeclaredMethod * @param object * @param methodName * @param parameterTypes * @return */ public static Method getDeclaredMethod(Object object, String methodName, Class[] parameterTypes){ for(Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){ try { //superClass.getMethod(methodName, parameterTypes); return superClass.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { //Method 不在当前类定义, 继续向上转型 } //.. } return null; } /** * 使 filed 变为可访问 * @param field */ public static void makeAccessible(Field field){ if(!Modifier.isPublic(field.getModifiers())){ field.setAccessible(true); } } /** * 循环向上转型, 获取对象的 DeclaredField * @param object * @param filedName * @return */ public static Field getDeclaredField(Object object, String filedName){ for(Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){ try { return superClass.getDeclaredField(filedName); } catch (NoSuchFieldException e) { //Field 不在当前类定义, 继续向上转型 } } return null; } /** * 直接调用对象方法, 而忽略修饰符(private, protected) * @param object * @param methodName * @param parameterTypes * @param parameters * @return * @throws InvocationTargetException * @throws IllegalArgumentException */ public static Object invokeMethod(Object object, String methodName, Class [] parameterTypes, Object [] parameters) throws InvocationTargetException{ Method method = getDeclaredMethod(object, methodName, parameterTypes); if(method == null){ throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]"); } method.setAccessible(true);//让私有方法变为可访问类型 try { return method.invoke(object, parameters); } catch(IllegalAccessException e) { System.out.println("不可能抛出的异常"); } return null; } /** * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter * @param object * @param fieldName * @param value */ public static void setFieldValue(Object object, String fieldName, Object value){ Field field = getDeclaredField(object, fieldName); if (field == null) throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]"); makeAccessible(field); try { field.set(object, value); } catch (IllegalAccessException e) { System.out.println("不可能抛出的异常"); } } /** * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter * @param object * @param fieldName * @return */ public static Object getFieldValue(Object object, String fieldName){ Field field = getDeclaredField(object, fieldName); if (field == null) throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]"); makeAccessible(field); Object result = null; try { result = field.get(object); } catch (IllegalAccessException e) { System.out.println("不可能抛出的异常"); } return result; } }

9.DAO< T>类

这里写了一个DAO< T >类,这里主要使用了两个工具类,JDBCutils,与数据库相关的工具类,方便获取连接,关闭连接等等,其次使用了DButils,可以预编译执行sql语句,返回对象,返回对象的集合,查询、单数、填充占位符等等。

import com.javawebmvc.db.JdbcUtils; import org.apache.commons.dbutils.; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.sql.Connection; import java.sql.S; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class DAO { //DBUtils public = new (); private Class clazz; public DAO() { //clazz = ReflectionUtils.getSuperGenericType(getClass()); Class cla = this.getClass(); Type type = cla.getGenericSuperclass();//获取父类带里面的类型 if(type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type[] args = parameterizedType.getActualTypeArguments(); if(args != null && args.length > 0){ clazz = (Class) args[0]; } } System.out.println("DAO的构造方法 clazz:"+clazz.getName()); } //返回一个对象 public T getObject(String sql, Object... args){ Connection connection = null; T result = null; try { connection = JdbcUtils.getConnection(); result = .query(connection, sql, new BeanHandler(clazz), args); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.releaseConnection(connection); } return result; } /** * 返回一个值 * * @param sql * @param args * @param * @return */ public E getforVal(String sql, Object... args) { Connection connection = null; try { connection = JdbcUtils.getConnection(); return (E) .query(connection,sql,new ScalarHandler(),args); }catch (S e) { e.printStackTrace(); } finally { JdbcUtils.releaseConnection(connection); } return null; } /** * 返回一个集合list * * @param sql * @return */ public List listObject(String sql,Object ... args) { Connection connection = null; List list = new ArrayList(); try { connection = JdbcUtils.getConnection(); list = .query(connection, sql, new BeanListHandler(clazz), args); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.releaseConnection(connection); } return list; } /** * 封装增加,删除,更新操作。 * * @param sql * @param args */ public void update(String sql, Object... args) throws S { Connection connection = null; try { connection = JdbcUtils.getConnection(); .update(connection, sql, args); } finally { JdbcUtils.releaseConnection(connection); } } }

10.写在最后

学到了后面的框架,反射的用处还是很大,对理解框架的原理也有帮助。在这里整理了一下反射的知识,希望对大家有帮助。本知识的学习来源于尚硅谷佟刚老师。如果需要大家可以下载
不对之处,请多指教!!

收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜