Latke持久层-新增add方法解读
发布日期:2022-03-12 04:49:27 浏览次数:36 分类:技术文章

本文共 6240 字,大约阅读时间需要 20 分钟。

100

说明:这篇文章不探讨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
repositoryClass; final Latkes.RuntimeDatabase runtimeDatabase = Latkes.getRuntimeDatabase(); switch (runtimeDatabase) { case MYSQL: case H2: case MSSQL: case ORACLE: repositoryClass = (Class
) Class.forName("org.b3log.latke.repository.jdbc.JdbcRepository"); break; case NONE: repositoryClass = (Class
) Class.forName("org.b3log.latke.repository.NoneRepository"); break; default: throw new RuntimeException("The runtime database [" + runtimeDatabase + "] is not support NOW!"); } final Constructor
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)方法
@Overridepublic 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 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 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 paramlist, final StringBuilder sql) throws Exception {        final Iterator
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方法就能保存到数据库了。

转载于:https://www.cnblogs.com/nobt/p/9704260.html

转载地址:https://blog.csdn.net/weixin_30348519/article/details/98182216 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:jquery.masonry + jquery.infinitescroll 实现瀑布流布局
下一篇:Linux FTP服务器配置文件vsftpd.conf 配置

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年03月30日 07时42分35秒