手把手教你写一个java的orm(完)

生成sql:select

上一篇讲了怎样生成一个sqlwhere的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就顺利的做好了。

现在我们开始写生成查询语句的sql。一个查询语句大致上是这样的:

SELECT name, id, create_date, age, mark, status FROM user

这里可以看出来,一个基础的查询语句基本上就是一个 SELECT 后面加上需要查询的字段,跟上 FROM 和要查询的表名称就好了。 最多后面可能需要加上 ORDER BY/GROUP BY/LIMIT ....之类的就好了,因为比较简单,这里就不写了。(太复杂的就直接写sql就好了,我自己不需要这种操作)

思路

  1. 从之前拿到的映射关系中拿到属性和字段名的映射,然后拼接sql。
  2. 执行sql,并取出结果。
  3. 实例化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;

    }
}

这个类具体在哪里使用,会在下面说明。

实现

现在说一下怎么实现上面的思路,首先因为第一步比较简单,就不写了。我直接从第二步开始。

  1. 执行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。

  2. 将结果转换为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 BYLIMIT 。可以在我的github上找到。地址在这里:https://github.com/hjx601496320/JdbcPlus 。

生成sql:delete

思路

诶呀, 这个太简单了。不写了哦~~~

参照我之前写的,分析一下,想一想思路,然后每一步要怎么做,一点一点的就写好了。

~~~

实现

你自己写咯~~~。

其他的

这一篇写的真快~~

生成sql:update

最后一部分了,马上就写完了。写东西真的好累啊~~~

思路

更新的语句也比较好做,sql后面的条件因为在之前已经写了where这一篇,所以这里就只写sqlwhere左边的一部分。现在还是先分析一下 update 语句:

UPDATE user SET name = ? , id = ? , create_date = ? , age = ? , status = ? WHERE id = ?

可以看到的,大体上就是 UPDATE 表名称 SET 字段名称 = ? 这个样子的。(因为现在不写WHERE右边的

所以具体的思路就是:

  1. 根据映射关系拼装sql。

    这里可能有一个可以选择的地方,就是如果某一个属性的值是null,这时要不要把这个属性更新为null

  2. 拿到要更新的值。
  3. 执行sql。

实现

  1. 从映射中拿到所有的属性。

    这一步的代码就不放了~~~,和前面写的没有什么区别。

  2. 拿到要更新的属性名称,和值。

    这里我们需要三个参数:

    1:用来标示更新的时候是否需要忽略值是null的属性。 boolean ignoreNull

    2:用来保存需要更新的字段的有序集合。 List

    3:保存需要更新的字段的值的有序集合。 List

  3. 根据拿到的数据拼装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的后面就好了。

  4. 执行sql。

    太简单了,就不写了~

最后

终于写完了。

还是说一下,因为代码已经在github上了,所以没有把全部的代码写在上面,主要还是以说明思路为主。另外刚开始写博客,有些可能表达的不是很明白。吃了没文化的亏啊~~~

这个项目还有很多可以但是还没有实现的功能,比如一些比较复杂的查询,执行函数之类的。我并没去写它。一是不需要,因为这个东西平时主要是做导出报表的时候用的,二是我自己写项目的话压根就不会用到这些东西,能用java写的我都用java写了。数据库嘛,对我来说就存个数据就好了,数据处理上的事情还是交给java来做好一点。

完了

原文地址:https://www.cnblogs.com/hebaibai/p/10330016.html

时间: 2024-10-08 21:13:45

手把手教你写一个java的orm(完)的相关文章

手把手教你写一个java的orm(二)

创建映射关系 ? 想要实现一个orm的功能,我觉得就是要将class和数据库中的表创建映射关系.把class的名称和表的名称,class属性名称和表的字段名称,属性类型与表的字段类型一一对应起来.可以通过配置文件,注解等等各种方式实现这个映射关系. 需要的依赖 ? 因为编写配置文件总是一件十分繁琐的事情,所以我决定使用注解的方式来实现这个映射.在项目刚开始写的时候我用的是自定义注解的方法.自己规定一套注解,后来觉得这样没有太大的必要,因为已经有jpa里的一套注解.所以直接用就好了.所以添加依赖:

大神手把手教你写一个页面模板引擎,只需20行Javascript代码!

只用20行Javascript代码就写出一个页面模板引擎的大神是AbsurdJS的作者,下面是他分享的全文,转需. 不知道你有木有听说过一个基于Javascript的Web页面预处理器,叫做AbsurdJS.我是它的作者,目前我还在不断地完善它.最初我只是打算写一个CSS的预处理器,不过后来扩展到了CSS和HTML,可以用来把Javascript代码转成CSS和HTML代码.当然,由于可以生成HTML代码,你也可以把它当成一个模板引擎,用于在标记语言中填充数据. 于是我又想着能不能写一些简单的代

手把手教你写一个RN小程序!

时间过得真快,眨眼已经快3年了! 1.我的第一个App 还记得我14年初写的第一个iOS小程序,当时是给别人写的一个单机的相册,也是我开发的第一个完整的app,虽然功能挺少,但是耐不住心中的激动啊,现在我开始学react native,那么现在对于react native也算是有所了解了,就用网上的接口开发一个小程序,现在带大家来写这个程序!接口是用看知乎的API,简简单单的只有get,可以从这里入门,也算是带大家入门吧,过后我会把源代码放在我的github上,前期项目肯定特别简陋,后面慢慢来优

手把手教你写一个用pytorch实现的Lenet5

最近为了实现HR-net在学习pytorch,然后突然发现这个框架简直比tensorflow要方便太多太多啊,我本来其实不太喜欢python,但是这个框架使用的流畅性真的让我非常的喜欢,下面我就开始介绍从0开始编写一个Lenet并用它来训练cifar10. 1.首先需要先找到Lenet的结构图再考虑怎么去实现它,在网上找了一个供参考 2.需要下载好cifar-10的数据集,在pytorch下默认的是下载cifar-10-python版本的,由于官网速度较慢,我直接提供度娘网盘的链接:链接:htt

手把手教你写一个通用的helm chart

[TOC] 1. 模板介绍 首先,放上此模板链接: https://github.com/ygqygq2/charts/tree/master/mod-chart 此chart可当作POD单image的通用模板,只需要使用sed替换下chart名,并修改下README.md和NOTES.txt就可以了.下文,我通过复制此chart成example-chart来作示范说明. [[email protected] mod-chart]# tree . ├── Chart.yaml ├── READM

Android开发之手把手教你写ButterKnife框架(二)

欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开发之手把手教你写ButterKnife框架(一)我们讲了ButterKnife是什么.ButterKnife的作用和功能介绍以及ButterKnife的实现原理. 本篇博客主要讲在android studio中如何使用apt. 一.新建个项目, 然后创建一个module名叫processor 新建m

Android开发之手把手教你写ButterKnife框架(三)

欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52672188 本文出自:[余志强的博客] 一.概述 上一篇博客讲了,如何在android studio使用apt < Android开发之手把手教你写ButterKnife框架(二)> 然后在Processor里生成自己的代码,把要输出的类,通过StringBuilder拼接字符串,然后输出. try { // write the file JavaFileObj

手把手教你写网络爬虫(3):开源爬虫框架对比

手把手教你写网络爬虫(3) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 介绍 大家好!我们从今天开始学习开源爬虫框架Scrapy,如果你看过<手把手>系列的前两篇,那么今天的内容就非常容易理解了.细心的读者也许会有疑问,为什么不学出身名门的Apache顶级项目Nutch,或者人气飙升的国内大神开发的Pyspider等框架呢?原因很简单,我们来看一下主流爬虫框架在GitHub上的活跃度: Project Language Star Watch Fork Nutch Java 1

[原创]手把手教你写网络爬虫(8):乱码

手把手教你写网络爬虫(8) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 字符编解码是爬虫里必学的一项知识,在我们的爬虫生涯中早晚会爬到乱码的网页,与其遇到时惊慌失措,不如早学早好,彻底避免乱码问题. 字符编码简介 什么是字符集 在介绍字符编码之前,我们先了解下什么是字符集. 字符(Character)是各种文字和符号的总称,包括各国家文字.标点符号.图形符号.数字等.字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集: