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方法就能保存到数据库了。

目录
相关文章
|
3月前
|
前端开发
Antd中Table列表行默认包含修改及删除功能的封装
Antd中Table列表行默认包含修改及删除功能的封装
48 0
|
9月前
PADS新建封装中焊盘部分修正(一)
在PADS新建封装中,需要修改焊盘参数。因为之前做板,有问题工厂都会帮忙修正,所以忽略了焊盘中阻焊层和助焊层。这里专门做一个修正。
116 0
|
9月前
PADS新建封装中焊盘部分修正(二)
按照同样的方法添加助焊层底层(Paste Mask Bottom),如下图所示:
80 0
|
NoSQL Java MongoDB
Java:SpringBoot项目中MongoTemplate的新增、删除、更新、查询操作
Java:SpringBoot项目中MongoTemplate的新增、删除、更新、查询操作
382 0
|
SQL Oracle 关系型数据库
MyBatis获取新增数据ID的几种方法
MyBatis获取新增数据ID的几种方法
3224 0
如何使用BOL接口set_property修改SAP CRM BO实例的某个属性
如何使用BOL接口set_property修改SAP CRM BO实例的某个属性
125 0
|
Java 数据库连接
Hibernate @OneToOne 自动新增和删除
版权声明:本文首发 http://asing1elife.com ,转载请注明出处。 https://blog.csdn.net/asing1elife/article/details/82811799 ...
1117 0
|
Web App开发 数据库 容器
EntityFramework中常用的数据删除方式速度对比
EntityFramework中常用的数据删除方式速度对比http://www.bieryun.com/3375.html 最近在学EF,目前了解到删除操作有三种方式, 第一,官方推荐的先查询数据,再根据查询的对象,删除对象。
1388 0