Latke持久层-新增add方法解读

简介: Latke持久层-新增add方法解读

说明:这篇文章不探讨Latke框架的IOC/DI部分,Latke框架中的IOC/DI功能跟Spring是很相似的,用起来会觉得很好上手,在这里只是说明为什么Latke可以将一个JSON用类似ORM的功能存储到关系型数据库。

add源码追溯

以solo新增一篇博客的后台全过程为例
  • 博客新增入口
@RequestProcessing(value = "/console/article/", method = HTTPRequestMethod.POST)
    public void addArticle(final HttpServletRequest request, final HttpServletResponse response, final HTTPRequestContext context,
                           final JSONObject requestJSONObject) throws Exception {
       //省略代码
       final String articleId = articleMgmtService.addArticle(requestJSONObject);
       //省略代码
    }
  • 上面接口调用的下一层方法
public String addArticleInternal(final JSONObject article) throws ServiceException {
         //省略代码,将前台传过来的json封装成最终新增所需要的JSON数据,调用新增方法
    articleRepository.add(article);
    //省略代码

    }
articleRepository是通过Latke框架的注解@Inject注入进来的,在它的实现类ArticleRepositoryImpl中没有add(JSONObject jsonObject)类型的方法,那么它调的肯定是其父类的add(JSONObject jsonObject)方法
  • 看看ArticleRepository的实现类ArticleRepositoryImpl的结构
public class ArticleRepositoryImpl extends AbstractRepository implements ArticleRepository {
    public ArticleRepositoryImpl() {
        super(Article.ARTICLE);
    }
}
  • 这个无参的构造方法,发现它没干什么事,只是调用了父类的无参构造方法,注意Article.ARTICLE后面会用到
public AbstractRepository(final String name) {
        try {
            Class<Repository> repositoryClass;

            final Latkes.RuntimeDatabase runtimeDatabase = Latkes.getRuntimeDatabase();
            switch (runtimeDatabase) {
                case MYSQL:
                case H2:
                case MSSQL:
                case ORACLE:
                    repositoryClass = (Class<Repository>) Class.forName("org.b3log.latke.repository.jdbc.JdbcRepository");

                    break;
                case NONE:
                    repositoryClass = (Class<Repository>) Class.forName("org.b3log.latke.repository.NoneRepository");

                    break;
                default:
                    throw new RuntimeException("The runtime database [" + runtimeDatabase + "] is not support NOW!");
            }

            final Constructor<Repository> constructor = repositoryClass.getConstructor(String.class);

            repository = constructor.newInstance(name);
        } catch (final Exception e) {
            throw new RuntimeException("Can not initialize repository!", e);
        }

        Repositories.addRepository(repository);
        LOGGER.log(Level.INFO, "Constructed repository [name={0}]", name);
    }
Latkes.getRuntimeDatabase()是从配置文件local.properties中获取变量名为runtimeDatabase(运行数据库类型)的值,从代码中可以看到,支持三种数据库:MYSQL、H2、ORACLE,根据runtimeDatabase的值通过反射实例化一个JdbcRepository对象,这个对象就是比较底层的一个持久方法了。
  • 接着再看看ArticleRepositoryImpl的父类AbstractRepository中的add(JSONObject jsonObject)方法
@Override
public String add(final JSONObject jsonObject) throws RepositoryException {
        if (!isWritable() && !isInternalCall()) {
            throw new RepositoryException("The repository [name=" + getName() + "] is not writable at present");
        }

        Repositories.check(getName(), jsonObject, Keys.OBJECT_ID);

        return repository.add(jsonObject);
    }
前面说了repository对象是根据runtimeDatabase的值通过反射实例化一个JdbcRepository对象,那么调用的就是JdbcRepository类中的add方法
@Override
    public String add(final JSONObject jsonObject) throws RepositoryException {
        final JdbcTransaction currentTransaction = TX.get();
        if (null == currentTransaction) {
            throw new RepositoryException("Invoking add() outside a transaction");
        }

        final Connection connection = getConnection();
        final List<Object> paramList = new ArrayList<>();
        final StringBuilder sql = new StringBuilder();
        String ret;

        try {
            if (Latkes.RuntimeDatabase.ORACLE == Latkes.getRuntimeDatabase()) {
                toOracleClobEmpty(jsonObject);
            }
            ret = buildAddSql(jsonObject, paramList, sql);
            JdbcUtil.executeSql(sql.toString(), paramList, connection, false);
            JdbcUtil.fromOracleClobEmpty(jsonObject);
        } catch (final Exception e) {
            LOGGER.log(Level.ERROR, "Add failed", e);

            throw new RepositoryException(e);
        }

        return ret;
    }
从方法的命名应该可以很轻易的判断出是什么意思了,就是第一步构造SQL语句,第二步执行SQL语句
private String buildAddSql(final JSONObject jsonObject, final List<Object> paramlist, final StringBuilder sql) throws Exception {
        String ret = null;
        if (!jsonObject.has(Keys.OBJECT_ID)) {
            if (!(KEY_GEN instanceof DBKeyGenerator)) {
                ret = (String) KEY_GEN.gen();
                jsonObject.put(Keys.OBJECT_ID, ret);
            }
        } else {
            ret = jsonObject.getString(Keys.OBJECT_ID);
        }
        setProperties(jsonObject, paramlist, sql);
        return ret;
    }
判断传进来的JSON中设置的默认主键是否为空,如果为空就设置一个,如果不为空就取出来用来返回,solo项目中的主键名称都是oId,值是时间戳,
调用setProperties(jsonObject, paramlist, sql);方法。
private void setProperties(final JSONObject jsonObject, final List<Object> paramlist, final StringBuilder sql) throws Exception {
        final Iterator<String> keys = jsonObject.keys();

        final StringBuilder insertString = new StringBuilder();
        final StringBuilder wildcardString = new StringBuilder();

        boolean isFirst = true;
        String key;
        Object value;

        while (keys.hasNext()) {
            key = keys.next();

            if (isFirst) {
                insertString.append("(").append(key);
                wildcardString.append("(?");
                isFirst = false;
            } else {
                insertString.append(",").append(key);
                wildcardString.append(",?");
            }

            value = jsonObject.get(key);
            paramlist.add(value);

            if (!keys.hasNext()) {
                insertString.append(")");
                wildcardString.append(")");
            }
        }

        sql.append("insert into ").append(getName()).append(insertString).append(" values ").append(wildcardString);
    }
这段代码的意思就是在根据传进来的JSON拼接SQL语句,最后一句中有一个getName()语句,其实拼接在这里就是对应数据库的表名称,什么时候传进来的,其实是在上面的ArticleRepositoryImpl类中调用父类的无参构造方法时传进来的,在父类中通过反射实例化JdbcRepository对象需要传入name属性,在类JdbcRepository中会经常用到,可以说只要跟数据库相关的操作可能都要用到表名称。
拼接好SQL语句后,就是执行SQL了,至此一个新增操作的全部就解读完毕了。

总结

通过看源码,就不难明白,为什么他说不需要像Hibernate、MyBatis那类ORM框架非得从JSON转到实体类对象,再跟关系型数据库对应了,为什么能前端拼接的JSON能直接调用add方法就能保存到数据库了。

目录
相关文章
ECharts 饼图数据放在饼图内部显示
ECharts 饼图数据放在饼图内部显示
730 0
|
jenkins Java 持续交付
Jenkins基础入门与安装配置详解
Jenkins基础入门与安装配置详解
398 1
|
SQL 开发框架 关系型数据库
不会使用 EF Core 的 Code First 模式?来看看这篇文章,手把手地教你
不会使用 EF Core 的 Code First 模式?来看看这篇文章,手把手地教你
646 0
|
8月前
|
人工智能 分布式计算 大数据
MaxFrame 产品评测:大数据与AI融合的Python分布式计算框架
MaxFrame是阿里云MaxCompute推出的自研Python分布式计算框架,支持大规模数据处理与AI应用。它提供类似Pandas的API,简化开发流程,并兼容多种机器学习库,加速模型训练前的数据准备。MaxFrame融合大数据和AI,提升效率、促进协作、增强创新能力。尽管初次配置稍显复杂,但其强大的功能集、性能优化及开放性使其成为现代企业与研究机构的理想选择。未来有望进一步简化使用门槛并加强社区建设。
374 8
|
10月前
|
安全 Java 开发者
Lock锁和AQS之间的关系与区别:基于第一原理的深入分析
【11月更文挑战第26天】在Java并发编程中,锁(Lock)和队列同步器(AbstractQueuedSynchronizer,简称AQS)是两个核心概念。它们为多线程环境下的同步和互斥提供了强大的支持。本文将从第一原理出发,深入探讨Lock锁和AQS之间的关系与区别,同时分析它们的相关概念、业务场景、历史背景、功能点以及底层原理,并使用Java代码进行模拟实现。
145 1
|
存储 API Swift
Ceph Reef(18.2.X)之Swift操作对象存储网关
这篇文章详细介绍了Ceph Reef(18.2.X)中通过Swift API操作对象存储网关的方法,包括创建用户、子用户、配置环境变量、以及使用swift命令行工具进行存储桶和对象的管理。
139 7
Ceph Reef(18.2.X)之Swift操作对象存储网关
|
9月前
|
存储 自然语言处理 Unix
【C语言】C语言 4 个编译过程详解
编译是将源代码转换为目标代码的过程。它是在编译器的帮助下完成的。编译器检查源代码是否存在语法或结构错误,如果源代码没有错误,则生成目标代码。
325 1
|
编译器 C语言
C语言宏定义(#define定义常量​、#define定义宏​、 带有副作用的宏参数、 宏替换的规则、 宏函数的对比)
C语言宏定义(#define定义常量​、#define定义宏​、 带有副作用的宏参数、 宏替换的规则、 宏函数的对比)
|
JSON 开发框架 API
【推荐100个unity插件之20】一个强大的JSON处理库——Newtonsoft.Json(也称为Json.NET)
【推荐100个unity插件之20】一个强大的JSON处理库——Newtonsoft.Json(也称为Json.NET)
1127 0
|
JSON 数据格式 C++
使用NlohmannJson写JSON保留插入顺序
使用NlohmannJson写JSON保留插入顺序
324 0