使用Enjoy SQL简化数据访问

ActiveRecord提供了基础的方法,实际开发项目中扩展一下用起来就更方便了,幸好官方提供的有Enjoy Template Engine,可以很方便的实现扩展,今天就分享一下我在一个项目中的扩展方案。

首先我们编写Enjoy SQL模板文件,resources/all.sql

#define in(field,values)
    #if(values && values.size()>0)
    #(field) in
    (
    #for(value:values)
        #para(value)#(for.last?'':',')
    #end
    )
    #else
    false
    #end
#end


#define where(where)
#for(x : where)
    #(for.first ? " where": " and")
    #if(x.key=="_in")
        #for(inx : x.value)
            #@in( inx.key,inx.value)
        #end
    #elseif(x.key=="")
        #(x.value)
    #else
        #(x.key) #para(x.value)
    #end
#end
#end


###  count查询,table、[field]、[where]
#sql("count")
select
    #if(field)
        count(#(field))
    #else
        count(*)
    #end
    from #(table) #@where(where)
#end


###  sum查询,table、field、[where]
#sql("sum")
    select sum(#(field)) from #(table) #@where(where)
#end


###  column查询,table、field、[where]
#sql("column")
    select #(field) from #(table) #@where(where)
#end


### 修改操作 table、where、update
#sql("update")
    update #(table) set
    #for(x : update)
        #(x.key) = #para(x.value)#(for.last?'':',')
    #end
    #@where(where)
#end

### 查询数据 table、[fields]、where、[orderby]
#sql("query")
    select
    #if(fields)
        #(fields)
    #else
        *
    #end
    from #(table) #@where(where)
    #if(orderby)
        order by #(orderby)
    #end
#end


### 删除数据 table、[fields]、where、[orderby]
#sql("delete")
    delete from #(table) #@where(where)
#end

文件中提供了增删改查的一些模板,主要是实现动态条件,上面的参数需要定义一个参数类,使用字典形式,大体如下 :

package com.aaa.xapi.helpers;

import com.jfinal.kit.Kv;

/**
 * 结合 jfinal 的 Enjoy SQL 模板,快速生成参数
 */
public class Dict extends Kv {

    /* 总体结构预览
    {
        table:'table',
        field:'id',
        fields:'id,name',
        update:{
            name:'新的名称',
            age:'新的年龄'
        },
        where:{
            id:1,
            age:2
            _in:{
                id:[2,3,4]
            },
            _or:{
                id:2,
                name like:23
            }
        }
        orderby:'id desc',
    }

    * */
    
    public static Dict create() {
        return new Dict();
    }


    public static Dict table(String table) {
        return new Dict().tbl(table);
    }

    public Dict() {
    }

    /**
     * 指定查询的table
     *
     * @param table
     * @return
     */
    public Dict tbl(String table) {
        String newTable = table;
        if (table.equals("Order") || table.equals("User") ) {
            newTable = "`" + newTable + "`";
        }
        this.set("table", newTable);
        return this;
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict where(Object key, Object value) {
        Kv where = (Kv) this.getOrDefault("where", Kv.create());
        where.set(key, value);
        this.set("where", where);
        return this;
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict eq(Object key, Object value) {
        return where(key + " = ", value);
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict neq(Object key, Object value) {
        return where(key + " != ", value);
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict gt(Object key, Object value) {
        return where(key + " > ", value);
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict gte(Object key, Object value) {
        return where(key + " >= ", value);
    }
    
    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict lt(Object key, Object value) {
        return where(key + " < ", value);
    }

    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict lte(Object key, Object value) {
        return where(key + " <= ", value);
    }
    
    /**
     * 添加where条件
     *
     * @param key
     * @param value
     * @return
     */
    public Dict like(Object key, Object value) {
        return where(key + " like ", "%" + value + "%");
    }
    
    /**
     * 添加where条件
     *
     * @param key
     * @param values
     * @return
     */
    public Dict in(String key, Object values) {
        Kv where = (Kv) this.getOrDefault("where", Kv.create());

        Kv _in = (Kv) where.getOrDefault("_in", Kv.create());
        _in.set(key, values);
        where.set("_in", _in);
        this.set("where", where);
        return this;
    }
    
    /**
     * 添加where条件
     *
     * @param values
     * @return
     */
    public Dict idIn(Object values) {
        Kv where = (Kv) this.getOrDefault("where", Kv.create());

        Kv _in = (Kv) where.getOrDefault("_in", Kv.create());
        _in.set("Id", values);
        where.set("_in", _in);
        this.set("where", where);
        return this;
    }

    /**
     * sql
     *
     * @param sql
     * @return
     */
    public Dict whereSql(String sql) {

        return where("", sql);
    }

    public Dict whereSql(String sql, Object... args) {
        return where("", String.format(sql, args));
    }


    /**
     * 添加要修改的字段和值
     *
     * @param key
     * @param value
     * @return
     */
    public Dict update(Object key, Object value) {

        Kv update = (Kv) this.getOrDefault("update", Kv.create());
        update.set(key, value);

        this.set("update", update);
        return this;
    }

    /**
     * 要查询的字段
     *
     * @param selectFields
     * @return
     */
    public Dict fields(String selectFields) {
        this.set("fields", selectFields);
        return this;
    }

    /**
     * 要查询的字段
     *
     * @param field
     * @return
     */
    public Dict field(String field) {
        this.set("field", field);
        return this;
    }

    /**
     * 排序条件
     *
     * @param orderby
     * @return
     */
    public Dict orderby(String orderby) {
        this.set("orderby", orderby);
        return this;
    }

    public Dict orderbyIdDesc() {
        this.set("orderby", "Id desc");
        return this;
    }
}

该类的目的就是方便生成sqltemplate里的目标,调用的template方法不同,使用的参数也不同。

我们配置ActiveRecordPlugin的时候需要添加一下all.sql

image.png

这样我们基本就可以使用了,如查询分页:

var dict = Dict.create();
dict.tbl("User");
if (StringHelper.hasValue(ps.getName())) {
    dict.like("Name", ps.getName());
}

if (NumberHelper.hasValue(ps.getAge())) {
    dict.eq("Age", ps.getAge());
}

User.dao.template("query",dict).paginate(1,20);


为了更方便使用,需要更进一步的优化,我们需要替换掉实体类的默认基类Model<M>,然后我们就可以随时扩展自己的基类。

首先定义自己的基类,让它集成Model<M>:

package com.aaa.xapi.basedto;

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.DbTemplate;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import com.mpyf.xapi.helpers.Dict;
import com.mpyf.xapi.services.SQL;
import org.apache.calcite.linq4j.Linq4j;

import java.util.List;

/**
 * 自定义数据库模型基类
 *
 * @param <M>
 */
public abstract class DbModel<M extends DbModel<M>> extends Model<M> {

    private String tableName;

    @Override
    public M dao() {
        this.tableName = _getTable().getName();
        return super.dao();
    }

    public int queryMaxId(){
        return Db.queryInt("select IfNULL( max(Id),0) from "+tableName);
    }

    public List<M> queryByIds(List<Integer> ids) {
        return template(SQL.query, Dict.table(tableName).idIn(ids)).find();
    }

    public List<M> queryByIds(Integer[] ids) {

        return template(SQL.query, Dict.table(tableName).idIn(Linq4j.asEnumerable(ids).toList())).find();
    }

    public List<M> queryByFields(String field, Object values) {
        return template(SQL.query, Dict.table(tableName).in(field, values)).find();
    }

    public List<M> query(Dict dict) {
        dict.tbl(tableName);
        return template(SQL.query, dict).find();
    }

    public M queryFirst(Dict dict) {
        dict.tbl(tableName);
        return template(SQL.query, dict).findFirst();
    }


    public Page<M> paginate(Dict dict, int pageIndex, int pageSize) {
        dict.tbl(tableName);
        return template(SQL.query, dict).paginate(pageIndex, pageSize);
    }

    public Page<M> paginate(Dict dict, PageParams ps) {
        dict.tbl(tableName);
        System.out.println( getSqlPara(SQL.query,dict).getSql());
        return template(SQL.query, dict).paginate(ps.getPageIndex(), ps.getPageSize());
    }


    public int count(Dict dict) {
        dict.tbl(tableName);
        return Db.template(SQL.count, dict).queryInt();
    }

    public <T> List<T> column(Dict dict) {
        dict.tbl(tableName);
        if (dict.get("field") == null) {
            dict.field("Id");
        }
        return Db.template(SQL.column, dict).query();
    }

    public <T> List<T> column(String field, Dict dict) {
        dict.tbl(tableName).field(field);
        return column(dict);
    }

    public DbTemplate sumTemplate(Dict dict) {
        dict.tbl(tableName);
        return Db.template(SQL.sum, dict);
    }

    public int update(Dict dict) {
        dict.tbl(tableName);
        return Db.template(SQL.update, dict).update();
    }

    public int delete(Dict dict) {
        dict.tbl(tableName);
        return Db.template(SQL.delete, dict).delete();
    }
}

DbModel里主要实现了对自定义template方法的调用,不过可以随时扩展其他的公共方法。

上面用到了一个SQL类是这样的,只是方便写template里key的名称:

public class SQL {
    public static final String query = "query";
    public static final String update = "update";
    public static final String count = "count";
    public static final String column = "column";
    public static final String sum = "sum";
    public static final String delete = "delete";
}


生成模型的时候需要一个基类模板,也放在resources中,base_model_template.jf:

package #(baseModelPackageName);

import com.jfinal.plugin.activerecord.IBean;
import com.aaa.xapi.basedto.DbModel;

/**
 * Generated by JFinal, do not modify this file.
 */
#if (generateChainSetter)
@SuppressWarnings({"serial", "unchecked"})
#else
@SuppressWarnings("serial")
#end
public abstract class #(tableMeta.baseModelName)<M extends #(tableMeta.baseModelName)<M>> extends DbModel<M> implements IBean {

#set(b = generateChainSetter)
#for(cm : tableMeta.columnMetas)
   #if (cm.remarks)
   /**
    * #(cm.remarks)
    */
   #end
   #set(argName = javaKeyword.contains(cm.attrName) ? '_' + cm.attrName : cm.attrName)
   public #(b ? 'M' : 'void') set#(firstCharToUpperCase(cm.attrName))(#(cm.javaType) #(argName)) {
      set("#(cm.name)", #(argName));
      #if (b)
      return (M)this;
      #end
   }

   #if (cm.remarks)
   /**
    * #(cm.remarks)
    */
   #end
   #set(getterOfModel = getterTypeMap.get(cm.javaType))
   #if (isBlank(getterOfModel))
      #set(getterOfModel = 'get')
   #end
   public #(cm.javaType) get#(firstCharToUpperCase(cm.attrName))() {
      return #(getterOfModel)("#(cm.name)");
   }

#end
}

在生成配置中需要指定模型基类:

generator.setBaseModelTemplate("base_model_template.jf");

到此便更改完成,我们增强了dao层,并且有一个自定义的dao层基类,可以随时新增一些公共方法。

举例几个用法:

///修改密码:
User.dao.update(Dict.create().eq("Id", 1).update("Password", "123456"));


 //根据ids查询
 user.dao.queryByIds(new []{1,2,3,4,5})
 
//根据条件查询
User.dao.queryFirst(
        Dict.create().eq("Account", ps.getAccount()).eq("Password", pwd).eq("UserType", ps.getUserType())
);



评论区

山东小木

2022-07-08 15:08

跟JBolt平台思路差不多

laofa

2022-07-11 22:01

简单的业务逻辑用这个可以快速实现

北流家园网

2022-07-12 14:30

不错,收藏了