之前的随笔一直都在介绍c#,主要公司最近的业务都是做桌面程序,那么目前c#中的WPF肯定是我做桌面程序的不二之选,做了半年的WPF,也基本摸清了c#写代码的套路和规则(本人之前是两年多的JAVA开发者,除了大学没有接触过任何c#的编程),我在大三开始搞工作室,接网站的单子,最难过的时候,一个人要写前台后台,说实话是JAVA把我引上了这条程序员的"不归路",我在南京上的一所985大学,当然大家也能猜到,南京总共就两个985,我的专业是网络工程,我是实在没有兴趣,唯独对JAVA编程感兴趣,我记得那时候我们的Java老师是一个浙大的年轻硕士,上了一学期从没写过一行代码,我就自己买书自学,直到现在我在苏州一家高新技术公司做软工,JAVA给我生活带来了色彩,也养活了我。

说这么多,是因为今天逛园子看见一个7年园龄的.net开发者,在一篇关于servlet的文章下,直言JAVA很简单,我是有点不服气的,但是我也没有反驳,毕竟人家7年园龄,我17年才毕业。

今天开始会把我这接近三年的JAVA经验,和死啃的技术文档所获得的心得分享出来,我这个人写文章不是很厉害,所以将就看看吧。

反射我就不详细解释了,不是很了解的可以看看园子里关于反射的知识贴。

一、反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

#二、框架思路

获取数据库数据,反射获取类模型的字段,以及set方法,通过invoke我们的set方法,将数据set到类模型对象之中,将行数据作为对象返回出来,多条数据则返回对象集合

#三、工具类,辅助类编写

1.首先是封装类的对象,作用是将类的字段和方法都存储到两个数组之中,拆分模型类

package com.warrenwell.dao.util.bean;

import java.lang.reflect.Field;
import java.lang.reflect.Method; /**
* 类的信息,用于封装类的结构
* @author Administrator
*
*/
public class ClassInfo {
//属性
private Field[] fields;
//方法
private Method[] methods; public Field[] getFields() {
return fields;
}
public void setFields(Field[] fields) {
this.fields = fields;
}
public Method[] getMethods() {
return methods;
}
public void setMethods(Method[] methods) {
this.methods = methods;
} }

2.有了模型类的封装类,自然就有数据库列的封装类,设定两个字段一个是列类型,一个是列名字,这样之后只要一个此类的数组就能表示一个表的结构

package com.warrenwell.dao.util.bean;
/**
* 列数据
* @author Administrator
*
*/
public class ColumnData {
private String columnName;
private int columnType; public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public int getColumnType() {
return columnType;
}
public void setColumnType(int columnType) {
this.columnType = columnType;
} }

3.接下来就是数据库的连接等工具类的编写,我把数据库的配置写在了配置文件,下面是我的DaoUtil中的数据库工具类的编写以及配置文件的书写方式,配置文件名字为DBConfig,在src路径下。获取连接等加锁是出于安全的考虑

private static String driver;
private static String url;
private static String user;
private static String password;
protected static int size;
private static DataSource dataSource;
/**
* 加载配置文件
*/
static{
ResourceBundle bundle = ResourceBundle.getBundle("DBConfig");
System.out.println("加载了配置文件");
//解析配置文件中的数据
driver = bundle.getString("driver");
user = bundle.getString("user");
password = bundle.getString("password");
url = bundle.getString("url");
try {
size = Integer.parseInt(bundle.getString("pageSize"));
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("size格式非法!");
} }
/**
* 获取数据库连接
* @return
*/
public static synchronized Connection getConnection(){
Connection con = null;
try {
//驱动加载
Class.forName(driver);
//获取连接对象
con = DriverManager.getConnection(url,user,password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return con;
} /**
* 关闭数据库对象
* @param con 连接
* @param stmt 处理器
* @param rs 结果集
*/
public static synchronized void close(Connection con, Statement stmt, ResultSet rs){
try {
if(rs != null){
rs.close();
//rs = null;
}
if(stmt != null){
stmt.close();
//stmt = null;
}
if(con != null){
//如果该connection对象是由数据池创建的,其close方法表示将连接放回连接池
//如果该Connection对象是由数据库驱动提供的,close方法表示断开数据库连接,并清除开销的资源
con.close();
//con = null;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.3.100:1521:orcl
user=system
password=niit
pageSize=4

4.我们从数据库查询到结果集就要被解析封装成列数据的数组了,相当于解析出结果集的结构

/**
* 解析元数据
* @param data
* @return
*/
public static ColumnData[] parseResultSetMetaData(ResultSetMetaData data){
ColumnData[] columnData = null;
if(data != null){
try {
columnData = new ColumnData[data.getColumnCount()];
for(int i = 0; i < data.getColumnCount(); i++){
ColumnData column = new ColumnData();
//封装列的名称
column.setColumnName(data.getColumnName(i+1));
//封装列的类型
column.setColumnType(data.getColumnType(i+1));
columnData[i] = column;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return columnData;
}

映射实体类也要被解析到类模型之中,说白了就是将实体类的字段和方法进行解析,以便之后使用,这里我们需要考虑到有的实体类可能是继承自父类的,我们这里需要反射的时候都获取到就可能出现重复的字段或者方法,所以我们选择set集合存储这些字段和方法,利用的就是set集合的自动去重复,之后再将set集合转为数组

/**
* 解析类
* @param classObj
* @return
*/
public static ClassInfo parseClass(Class classObj){
//存储所有属性的集合
//方式一
Set<Field> setF = new HashSet<Field>(); for(Field f : classObj.getDeclaredFields()){
setF.add(f);
}
for(Field f : classObj.getFields()){
setF.add(f);
} //存储所有方法
Set<Method> setM = new HashSet<Method>();
for(Method m : classObj.getDeclaredMethods()){
setM.add(m);
}
for(Method m : classObj.getMethods()){
setM.add(m);
} //封装类的属性和方法
ClassInfo info = new ClassInfo();
Field[] arrayF = new Field[setF.size()];
//将集合转换成数组
setF.toArray(arrayF);
Method[] arrayM = new Method[setM.size()];
setM.toArray(arrayM); info.setFields(arrayF);
info.setMethods(arrayM);
return info;
} /**
* 首字母大写转换
* @param str
* @return
*/
public static String upperFirstWorld(String str){
return str.substring(0,1).toUpperCase().concat(str.substring(1));
}

到了这里所有的辅助类和工具类就都完成了,我们就可以去编程我们的数据的增删改查的框架了

#四、核心框架代码编写

1.增删改的数据框架,没有什么需要反射的,主要是做到sql注入的时候,我们把参数都写到一个object数据,因为我们也不知道参数的个数和参数的类型

public static synchronized void executeUpdate(String sql,Object[] params){
Connection con = DaoUtil.getConnection();
PreparedStatement pstmt = null;
if(con != null){
try {
pstmt = con.prepareStatement(sql);
//注入参数
if(params != null){
for(int i = 0; i < params.length; i++){
if(params[i].getClass() == String.class){
pstmt.setString(i+1, params[i].toString());
}
else if(params[i].getClass() == int.class || params[i].getClass() == Integer.class){
pstmt.setInt(i+1, (Integer)params[i]);
}
//....else if() 判断各种事情类型情况
else{
pstmt.setObject(i+1, params[i]);//其实只要这句话上面的判断就不需要了
}
}
//执行增删改
pstmt.executeUpdate();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
DaoUtil.close(con, pstmt, null);
} } }

2.这也是重点来了,查询数据,我这里以查询多行数据为例,从而返回的应该是实体映射类对象的集合下图是我们数据库中表的大概结构

从而对应的实体类应为

package com.warrenwell.pojo;

/**
* 数据持久层
* @author 123
*此处为用户类
*/
public class User {
private String userName;
private int userid;
private String tel;
private String birthday;
private String nation;
private String origin;
private String password;
private int flag;
private String headimg;
private int ismarry;
private int sex; public int getIsmarry() {
return ismarry;
}
public void setIsmarry(int ismarry) {
this.ismarry = ismarry;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
} public String getHeadimg() {
return headimg;
}
public void setHeadimg(String headimg) {
this.headimg = headimg;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
} public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getNation() {
return nation;
}
public void setNation(String nation) {
this.nation = nation;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
} }

我们需要获取到实体类所拥有的属性和set方法,再去解析结果集的结构,依次比较实体类中有的字段在数据库之中也有的,那么就是我们所需要的,从而invoke该实体类的set方法,将值set到实体类的对应字段之中。

/**
* 通用的数据查询方法
* @return 多行数据
*/
public static synchronized <T> List<T> executeQueryForMultipl(String sql, Object[] params, Class<T> classObj){
List<T> list = new ArrayList<T>();
T t = null;
Connection con = DaoUtil.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
if(con != null){
try {
pstmt = con.prepareStatement(sql);
if(params != null){
//循环注入参数
for(int i = 0; i < params.length; i++){
pstmt.setObject(i+1, params[i]);
}
}
//执行查询
rs = pstmt.executeQuery();
//解析结果集的数据结构
ColumnData[] data = DaoUtil.parseResultSetMetaData(rs.getMetaData());
//解析映射类的结构
ClassInfo info = DaoUtil.parseClass(classObj);
//读取结果集
while(rs.next()){
//实例化
t = classObj.newInstance();
//每列的值
Object value = null;
//根据结果集的数据结构获取每个列的数据
for(int i = 0; i < data.length; i++){
ColumnData column = data[i]; //判断映射类中是否存在对应的属性
for(Field f : info.getFields()){
if(f.getName().equalsIgnoreCase(column.getColumnName())){
//判断映射类是否存在该属性对应的setter方法
for(Method m : info.getMethods()){
if(m.getName().equals("set"+DaoUtil.upperFirstWorld(f.getName()))){
if(f.getType() == Integer.class || f.getType() == int.class){
value = rs.getInt(i+1);
}
else if(f.getType() == Double.class || f.getType() == double.class){
value = rs.getDouble(i+1);
}
else if(f.getType() == String.class){
value = rs.getString(i+1);
}
else if(f.getType() == Timestamp.class){
value = rs.getTimestamp(i+1);
}
//如果方法存在则invoke执行
//System.out.println(m.getName()+"\t"+value);
m.invoke(t, value);
break;
}
}
break;
}
}
}//end-for
list.add(t);
}//end-while
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
DaoUtil.close(con, pstmt, rs);
}
}
return list;
}

以后我们在Dao之中只需要调用我们的核心方法即可,如下图:

public class UserDao {
public User findUserById(int userId){
return DaoHandle.executeQueryForSingle("select * from users where userid=?", new Object[]{userId}, User.class);
} public void saveUser(User user){
DaoHandle.executeUpdate("insert into users values(userid.nextval,?,?,?,?,?)", new Object[]{user.getUserName(),user.getUserPwd(),user.getHead(),user.getRegTime(),user.getSex()});
} public User findUserByName(String userName){
return DaoHandle.executeQueryForSingle("select * from users where username=?", new Object[]{userName}, User.class);
}
}

我们的数据层框架就会把结果依次的映射到对象中,并且返回出来。

单行和分页查询大家可以去试试,我这里只是提供了多行数据返回对象集合的代码。希望大家工作愉快,需要源码的dd我。拜拜!

#五、源代码地址

源代码下载

JAVA 利用反射自定义数据层框架的更多相关文章

  1. Java工程师高薪训练营-第一阶段 开源框架源码解析-模块一 持久层框架涉及实现及MyBatis源码分析-任务一:自定义持久层框架

    目录 任务一:自定义持久层框架 1.1 JDBC回顾及问题分析 1.2 自定义持久层框架思路分析 1.3 IPersistence_Test编写 1.3.1 XXXMapper.xml详解 1.3.2 ...

  2. 【笔记】拉勾Java工程师高薪训练营-第一阶段 开源框架源码解析-模块一 持久层框架涉及实现及MyBatis源码分析-任务一:自定义持久层框架

    以下笔记是我看完视频之后总结整理的,部分较为基础的知识点也做了补充,如有问题欢迎沟通. 目录 任务一:自定义持久层框架 1.1 JDBC回顾及问题分析 1.2 自定义持久层框架思路分析 1.3 IPe ...

  3. Java精进-手写持久层框架

    前言 本文适合有一定java基础的同学,通过自定义持久层框架,可以更加清楚常用的mybatis等开源框架的原理. JDBC操作回顾及问题分析 学习java的同学一定避免不了接触过jdbc,让我们来回顾 ...

  4. Mybatis学习之自定义持久层框架(七) 自定义持久层框架优化

    前言 接上文,这里只是出于强迫症,凭借着半年前的笔记来把之前没写完的文章写完,这里是最后一篇了. 前面自定义的持久层框架存在的问题 Dao层若使用实现类,会存在代码重复,整个操作的过程模版重复(加载配 ...

  5. Mybatis学习之自定义持久层框架(六) 自定义持久层框架:完善CRUD方法并进行测试

    前言 没想到会等到半年以后才来写这篇文章,我已经不记得当初自己想要在这篇文章中写什么了,还好有一些零散的笔记留着,就对照着上一篇文章及零散的笔记,把内容给补充完吧. 完善CRUD方法 完善Defaul ...

  6. java利用反射机制判断对象的属性是否为空以及获取和设置该属性的值

    1.java利用反射机制判断对象的属性是否为空: Map<String,String> validateMap = new LinkedHashMap<String, String& ...

  7. 【mybatis xml】数据层框架应用--Mybatis(三)关系映射之一对一关系映射

    实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系. 针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关 ...

  8. Mybatis学习之自定义持久层框架(三) 自定义持久层框架:读取并解析配置文件

    前言 前两篇文章分别讲解了JDBC和Mybatis的基本知识,以及自定义持久层框架的设计思路,从这篇文章开始,我们正式来实现一个持久层框架. 新建一个项目 首先我们新建一个maven项目,将其命名为I ...

  9. Mybatis学习之自定义持久层框架(二) 自定义持久层框架设计思路

    前言 上一篇文章讲到了JDBC的基本用法及其问题所在,并提出了使用Mybatis的好处,那么今天这篇文章就来说一下该如何设计一个类似Mybatis这样的持久层框架(暂时只讲思路,具体的代码编写工作从下 ...

随机推荐

  1. C# 中使用using的三种方法

    1.using指令 using+命名空间,这种方法基本学习过C#的都用过,好处在于,写代码的时候不需要指定详细的命名空间 using System.Windows.Media; using Syste ...

  2. Dynamically loading unmanaged OCX in C#

    You'll have to perform a number of steps that are normally taken of automatically when you use the t ...

  3. iOS应用打包完后再在开发者网站添加应用测试ID能够加入测试吗

    1.明确指出 不行: 1.打包测试包前一定要先添加测试设备的UDID 2.添加测试的设备UDID一定要先于打包测试包,否则设备无法参加测试 3.使用蒲公英分享测试包,查看可参加测试的设备UDID 2. ...

  4. [SinGuLaRiTy] 2017 百度之星程序设计大赛 初赛A

    [SinGuLaRiTy-1036] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 小C的倍数问题 Time Limit: 2000/100 ...

  5. WCF进阶(一)——概述

    前言 时间的朋友>里面几句特别有预见性的话来说明当今儿世界互联网发展趋势: 市场上有一种叫做"父爱算法"的需求.将会诞生很多伟大的公司.背后的精神就是六个字:你不用懂,听我的 ...

  6. winform发布桌面程序后提示需开启“目录浏览”

    把发布文件里的publish.htm名字改为index.htm就好了

  7. mybatis 学习笔记(三):mapper 代理开发 dao 层

    mybatis 学习笔记(三):mapper 代理开发 dao 层 优势 通过使用mapper 代理,我们可以不需要去编写具体的实现类(使用 getMapper() 方法自动生成),只需编写接口即可, ...

  8. luoguP1401 城市

    https://www.luogu.org/problemnew/show/P1401 二分答案网络流判断是否可行即可 #include <bits/stdc++.h> using nam ...

  9. Linux--CentOS7使用firewalld打开关闭防火墙与端口

    1.firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status f ...

  10. Python之路Python作用域、匿名函数、函数式编程、map函数、filter函数、reduce函数

    Python之路Python作用域.匿名函数.函数式编程.map函数.filter函数.reduce函数 一.作用域 return 可以返回任意值例子 def test1(): print(" ...