手把手教你写一个java的orm(完)
生成sql:select
上一篇讲了怎样生成一个sql中where的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就顺利的做好了。
现在我们开始写生成查询语句的sql。一个查询语句大致上是这样的:
SELECT name, id, create_date, age, mark, status FROM user
这里可以看出来,一个基础的查询语句基本上就是一个 SELECT 后面加上需要查询的字段,跟上 FROM 和要查询的表名称就好了。 最多后面可能需要加上 ORDER BY/GROUP BY/LIMIT ....之类的就好了,因为比较简单,这里就不写了。(太复杂的就直接写sql就好了,我自己不需要这种操作)
思路
- 从之前拿到的映射关系中拿到属性和字段名的映射,然后拼接sql。
- 执行sql,并取出结果。
- 实例化class,使用反射给class的属性赋值。
这几步都还是比较好做的,第一步很简单,仿照着之前写的就可以了。因为这里在执行sql的时候,我使用的是JdbcTemplate,这里有一个不大不小的坑,下面我说一下。
一个不大不小的坑
这个坑是我在使用我写好的这个项目给公司做报表的时候碰到的。原因是这样,因为数据库中有些字段是datetime类型的,这个字段有时候在表中的值是:0000-00-00 00:00:00,(我也不知道这个值是怎么进去的,但是就是存在/(ㄒoㄒ)/~~)但是这个值是无法转换成为java中的Date类型。所以这里会报错。
我在这里写了一个继承SpringJdbc中的ColumnMapRowMapper的类,是这样的:
import org.springframework.jdbc.core.ColumnMapRowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 捕获取值的错误
*
* @author hjx
*/
public class PlusColumnMapRowMapper extends ColumnMapRowMapper {
/**
* 数据库类型为时间时, 如果值为 0000-00-00 00:00:00
* 会报错,所以重写此方法,返回null
*
* @param rs
* @param index
* @return
* @throws SQLException
*/
@Override
protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
Object columnValue = null;
try {
columnValue = super.getColumnValue(rs, index);
} catch (SQLException e) {
e.printStackTrace();
}
return columnValue;
}
}
这个类具体在哪里使用,会在下面说明。
实现
现在说一下怎么实现上面的思路,首先因为第一步比较简单,就不写了。我直接从第二步开始。
执行sql,并取出结果。
这里我用的是JdbcTemplate的方法,这给我们提供了一个方法:
<T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper)
这里前两个参数比较好理解,一个是sql,一个是sql中的参数。第三个是需要传一个接口RowMapper,这个接口具体是干啥的上网一查就知道了~~~
这里面有一个方法:
T mapRow(ResultSet rs, int rowNum) throws SQLException
第一个参数是查询的结果,第二个是指现在在第几行结果,返回值是你要返回什么对象。这里我们需要重写这个方法,把查询出的结果转换成为我们需要的对象。我们可以这么写:
/**
* 把数据库查询的结果与对象进行转换
*
* @param resultSet
* @param rowNum
* @return
* @throws SQLException
*/
@Override
public T mapRow(ResultSet resultSet, int rowNum) throws SQLException {
Map<String, Object> resultMap = columnMapRowMapper.mapRow(resultSet, rowNum);
。。。。
这个方法中的columnMapRowMapper 就是上面我们写的PlusColumnMapRowMapper,它的作用就是将查询结果第 rowNum 拿出来,并且将结果转换过成为一个 Map<String, Object>。其中:
key :是表字段名称。
Object :该字段的值。
上面写的PlusColumnMapRowMapper主要作用就是在获取值的时候如果发生异常,返回一个null。
在这一步里我们已经拿到了执行sql的结果,现在我们要将结果转换过为我们需要的class。
将结果转换为class
在上一步我们拿到了存放结果Map,现在只需要将map遍历一下,然后实例化java对象,根据字段和属性的映射关系使用反射将属性一个个的set进去就好了。现在贴上上一步的完整代码:
public T mapRow(ResultSet resultSet, int rowNum) throws SQLException {
Map<String, Object> resultMap = columnMapRowMapper.mapRow(resultSet, rowNum);
T instance = getInstance(tableClass);
for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
//数据库字段名
String key = entry.getKey();
if (!columnFieldMapper.containsKey(key)) {
continue;
}
Field declaredField = columnFieldMapper.get(key);
if (declaredField == null) {
continue;
}
//数据库字段值
Object value = entry.getValue();
setFieldValue(instance, declaredField, value);
}
return instance;
}
其中 columnFieldMapper 是一个Map<String, Field>。key是表的字段个名称。value是对应的class的属性。
下面是 setFieldValue的具体代码:
boolean setFieldValue(T t, Field field, Object value) {
field.setAccessible(true);
try {
if (value != null) {
field.set(t, value);
return true;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
这样,就可以将查询出的结果根据映射关系转换成为我们需要的class了。
其他的
如果查询需要添加条件的话,可以使用之前讲的 生成条件的工具将条件的sql拼接在这里的sql后面,相应的,where里的参数也要按照顺序添加进数组就好了。
相同的,如果要添加 ORDER BY/GROUP BY/LIMIT这些东西的话也是一样的操作。主要还是要看自己的代码是怎么设计的了。我自己用的只写了ORDER BY 和 LIMIT 。可以在我的github上找到。地址在这里:https://github.com/hjx601496320/JdbcPlus 。
生成sql:delete
思路
诶呀, 这个太简单了。不写了哦~~~
参照我之前写的,分析一下,想一想思路,然后每一步要怎么做,一点一点的就写好了。
~~~
实现
你自己写咯~~~。
其他的
这一篇写的真快~~
生成sql:update
最后一部分了,马上就写完了。写东西真的好累啊~~~
思路
更新的语句也比较好做,sql后面的条件因为在之前已经写了where这一篇,所以这里就只写sql中where左边的一部分。现在还是先分析一下 **update **语句:
UPDATE user SET name = ? , id = ? , create_date = ? , age = ? , status = ? WHERE id = ?
可以看到的,大体上就是 UPDATE 表名称 SET 字段名称 = ? 这个样子的。(因为现在不写WHERE右边的)
所以具体的思路就是:
根据映射关系拼装sql。
这里可能有一个可以选择的地方,就是如果某一个属性的值是null,这时要不要把这个属性更新为null。
拿到要更新的值。
执行sql。
实现
从映射中拿到所有的属性。
这一步的代码就不放了~~~,和前面写的没有什么区别。
拿到要更新的属性名称,和值。
这里我们需要三个参数:
1:用来标示更新的时候是否需要忽略值是null的属性。 boolean ignoreNull
2:用来保存需要更新的字段的有序集合。 List updataColumn
3:保存需要更新的字段的值的有序集合。 List values
代码是这样的:
List<String> columnNames = new ArrayList<>(entityTableRowMapper.getColumnNames());
Map<String, Field> columnFieldMapper = entityTableRowMapper.getColumnFieldMapper();
List<Object> values = new ArrayList<>();
for (int i = 0; i < columnNames.size(); i++) {
String columnName = columnNames.get(i);
if (!sqlColumns.contains(columnName)) {
continue;
}
Field field = columnFieldMapper.get(columnName);
Object value = EntityUtils.getValue(entity, field);
//如果class中的值是null,并且设置忽略null,跳过
if (ignoreNull && value == null) {
continue;
}
updataColumn.add(columnName);
values.add(value);
}
根据拿到的数据拼装sql
拿到上面需要的数据后,我们还需要拿到表的名称,这一步直接从映射关系中取就好了。下面的是拼装sql的代码:
StringBuilder sql = new StringBuilder();
sql.append("UPDATE ").append(getTableName()).append(StringUtils.SPACE);
sql.append("SET ");
for (int i = 0; i < updataColumn.size(); i++) {
String column = updataColumn.get(i);
if (i == 0) {
sql.append(StringUtils.append(column, " = ? "));
} else {
sql.append(StringUtils.append(", ", column, " = ? "));
}
}
这样就好了,大致上是这样的:
UPDATE user SET name = ? , id = ? , create_date = ? , age = ? , status = ?
条件的话,用之前写的where生成就好了,where中的值加在集合values的后面就好了。
执行sql。
太简单了,就不写了~
最后
终于写完了。
还是说一下,因为代码已经在github上了,所以没有把全部的代码写在上面,主要还是以说明思路为主。另外刚开始写博客,有些可能表达的不是很明白。吃了没文化的亏啊~~~
这个项目还有很多可以但是还没有实现的功能,比如一些比较复杂的查询,执行函数之类的。我并没去写它。一是不需要,因为这个东西平时主要是做导出报表的时候用的,二是我自己写项目的话压根就不会用到这些东西,能用java写的我都用java写了。数据库嘛,对我来说就存个数据就好了,数据处理上的事情还是交给java来做好一点。
完了
手把手教你写一个java的orm(完)的更多相关文章
- 手把手教你写一个java的orm(一)
写之前的说明 其实吧. 这个东西已经写好了,地址在:https://github.com/hjx601496320/JdbcPlus 这系列文章算是我写的过程的总结吧.(恩系列,说明我可能会写好久,╮ ...
- 手把手教你写一个java的orm(五)
生成sql:where 上一篇里我们实现了生成insert的sql,下面要开始实现update,delete,select的sql语句了.但是这些语句有一个比较麻烦的地方是:它们一般后面都会有wher ...
- 手把手教你写一个java的orm(二)
创建映射关系 想要实现一个orm的功能,我觉得就是要将class和数据库中的表创建映射关系.把class的名称和表的名称,class属性名称和表的字段名称,属性类型与表的字段类型一一对应起来.可以 ...
- 手把手教你写一个java的orm(三)
使用反射解析class 上一篇我们完成了class到表映射关系的建立,但是这个并不能被代码正确处理,我们还需要让程序能够正确的识别这些映射关系. 这一篇主要讲的是建立一个从class到表的模型,使我们 ...
- 手把手教你写一个java的orm(四)
开始准备生成sql 在上一篇里,我们已经取到了我们在生成sql语句中所需要的信息,这一篇里我们开始根据class来生成我们需要的sql.在这之前我们先确认几件事情 sql里的参数我们使用占位符的形式. ...
- 只有20行Javascript代码!手把手教你写一个页面模板引擎
http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...
- 手把手教你写一个RPC
1.1 RPC 是什么 定义:RPC(Remote Procedure Call Protocol)--远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数 ...
- 让我手把手教你写一个强大、方便使用的 IOC 容器
一.介绍 1.介绍 最近无聊,也没什么事做,没事做总是要给自己找点事情做吧,毕竟人的生活在与折腾.于是,决定自己手动写一个 IOC 的框架.我们知道在 NetCore 的版本里面已经内置了 IOC 容 ...
- 手把手教你写一个SpringMVC框架
一.介绍 在日常的 web 开发中,熟悉 java 的同学一定知道,Spring MVC 可以说是目前最流行的框架,之所以如此的流行,原因很简单:编程简洁.上手简单! 我记得刚开始入行的时候,最先接触 ...
随机推荐
- HTML、CSS
表格标签: 表格标签有:<table> <tr> <th> <td> 让内容居中的标签:<center> 按钮标签:<button&g ...
- 廖雪峰Python学习笔记——序列化
序列化 定义:程序运行时所有变量都存在内存中,把变量从内存中变成可存储或可传输的过程称为序列化pickling,在其他语言中称为serialization,marshalling,flattening ...
- 二,PHP会话机制---session的基本使用
1,思考:登录网站后,在每个网页都能拿到用户信息 (1) 使用超链接传递用户名,这样太繁琐了,不建议使用 . (2) 使用数据库,每打开一个页面都查询一次用户信息表,这样网页加载速度变慢,用户体验变差 ...
- jvm特性(3)( 收集算法和收集器的概念)
java内存模型和线程规范 JVM高级特性与实践(三):垃圾收集算法 与 垃圾收集器实现 大致知识点如下: 4种垃圾收集算法概念的学习 7种垃圾收集器特征的学习 一. 垃圾收集算法 1. 标记-清除算 ...
- C# 证书打印《六》
整理思路,从新出发. 加载模版 public void loadtemplate(Label lable) { string p_tempateFile = @"fomate.xml&quo ...
- 转的很好的js 入门
JavaScript概述 JavaScript的历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase.( ...
- day 17python 面对对象之继承
一:什么面向对象的继承? 比较官方的说法就是: 继承(英语:inheritance)是面向对象软件技术当中的一个概念.如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A ...
- spring基础回顾
1.什么是Spring框架?Spring框架有哪些主要模块? Spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题, ...
- h5 端图片上传
1.upload.js (function($) { $.extend($.fn, { images : new Array(), initImages:function (images) { $.e ...
- C/C++ -- Gui编程 -- Qt库的使用 -- 信号与槽的关联
Qt信号与槽的三种关联方法:1.设计界面关联,编辑信号/槽,自动关联 2.手动关联(1).头文件中定义槽 -----mywidget.h----- #ifndef MYWIDGET_H #define ...