Beetlsql扩展之自定义Mapper

Stella981
• 阅读 656

BeetlSql 有一个BaseMapper,提供了很多内置的Dao操作,如增删改查等10几个方法,用户只需要些一个类继承此接口便能很快的完成一个Dao,比如

public interface UserDao extends BaseMapper<User>{
}

UserDao没有包含任何方法,但集成了BaseMapper,因此具备了很多内置的Dao操作。如果这些操作不满足要求,可以使用BeetlSql的MD文档来维护复杂的SQL,比如,一个根据角色查询所有用户的操作

@SqlResource("common.user")
public interface UserDao extends BaseMapper<User>{
    List<User>  getUsersByRole(String role);
}

注解@SqlResource注明了sql文件的位置,位于common/user.md 文件,其中user.md 包含了sql查询

getUsersByRole
===

select u.* from user,user_role,role where ....

这是一个BeetlSql的基本概念,在这里不详细说明,有兴趣的可以参考官网文档 现在有这样一个需求,BaseMapper包含了太多的内置方法,有时候程序员对其中的某些方法并不敢兴趣,有的内置方法则是不允许调用的,比如deleteById 方法,总是有一次物理删除操作。应该在企业应用系统里禁止这种调用,那么,BeetlSql能否定制BaseMapper呢?

答案是能定制。只需要新写一个接口,通过SQLManager注册为BaseMapper即可,然后,为接口的每个方法添加实现即可,实际上,自带的BaseMapper就是这样实现的。

如下是一个自定义的BaseMapper,为了简单起见,Dao只提供三个接口

public interface MyBaseMapper<T>  {
    public T single(Object key);
    public T single(Object key,List attrs);
    public List<T> allData();
}

第一个方法single和第三个all方法都是原有的BaseMapper自带,第二个方法是根据主键获取对象,但只获取部分属性。我们将在博客最后讲述如何实现,现在,我们要实现allData方法。

allData方法返回目标对象对应的数据所有值,这在原有的BaseMapper已经实现,是通过org.beetl.sql.core.mapper.internal.AllAmi 实现,因此我们只需要复用这个实现。

首先,告诉SQLManager,我要添加一个新的BaseMapper

SQLManager     sql =   ....
MapperConfig config = sql.setBaseMapper(MyBaseMapper.class);

其次,添加allData实现

config.getBuilder().addAmi("allData", new AllAmi());

这样,就非常容易的定义了一个allData内置的Dao方法,使用了已经实现的AllAmi方法,AllAmi方法也非常简单,直接使用SQLManager.all方法实现,其源码如下

public class AllAmi implements MapperInvoke {

    @Override
    public Object call(SQLManager sm, Class entityClass, String sqlId, Method m, Object[] args) {
        if (args == null || args.length == 0) {
            return sm.all(entityClass);
        }

        return sm.all(entityClass, (Integer) args[0], (Integer) args[1]);
    }

}

这样,你就具备初步自定义Dao的功能,比如如下代码

MyUserMapper dao = sql.getMapper(MyUserMapper.class);
List<User> users =dao.allData();

现在,面临新的挑战,实现一个根据主键查询,但仅仅返回需要的字段,也就是实现如下方法

public T single(Object key,List attrs);

这比较复杂,因为在BeetlSQL里并未内置,需要我们自己完成后注册,完成思路很简单,生成类似如下sql语句,并交给SQLManager 执行即可,比如

select id,name where id = #id#

我们写一个新的扩展,叫做SingleAmiExt(参考了自带的SingleAmi),其脚手架如下

@Override
public Object call(SQLManager sm, Class entityClass, String sqlId, Method m, Object[] args) {
    if (args.length == 1) {
        return sm.single(entityClass, args[0]);
    }
    List attrs = (List) args[1];
    String sql  = getSingleSelect(entityClass,sm,attrs);

    Map paras = this.setIdsParas(sm, args[0], entityClass);
    List list = sm.execute(sql, entityClass, paras);
    if(list.size()==0) {
        return null;
    }else {
        return list.get(0);
    }

}

这段代码前俩行先判断参数,如果只有一个参数,则认为没有属性过滤,直接调用内置的single方法即可。否则,需要自己拼写一个Sql模板,通过SQLManager.execute 来获取

getSingleSelect方法用于生成目标sql模板,代码如下

    private String getSingleSelect(Class cls, SQLManager sm, List attrs) {
        NameConversion nameConversion = sm.getNc();
        String condition = appendIdCondition(sm, cls);
        StringBuilder sb = new StringBuilder("select ");
    
    for (Object o : attrs) {
        String attr = (String) o;
        String col = nameConversion.getColName(cls, attr);
        sb.append(col).append(" ,");
    
    }
    // 去掉最后一逗号
    sb.setLength(sb.length() - 1);
    sb.append(" from ").append(nameConversion.getTableName(cls)).append(condition);
        return sb.toString();
    }
    
    /* 参考了AbstractDBStyle的内置代码生成办法 */
    protected String appendIdCondition(SQLManager sm, Class<?> cls) {
    
        AbstractDBStyle style = (AbstractDBStyle) sm.getDbStyle();
        MetadataManager metadataManager = sm.getMetaDataManager();
        NameConversion nameConversion = sm.getNc();
        String tableName = nameConversion.getTableName(cls);
        StringBuilder condition = new StringBuilder(" where ");
    TableDesc table = metadataManager.getTable(tableName);
    ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
    
    List<String> colIds = classDesc.getIdCols();
    List<String> propertieIds = classDesc.getIdAttrs();
    Iterator<String> colIt = colIds.iterator();
    Iterator<String> propertieIt = propertieIds.iterator();
    if (colIt.hasNext() && propertieIt.hasNext()) {
        String colId = colIt.next();
        String properId = propertieIt.next();
        condition.append(style.getKeyWordHandler().getCol(colId)).append(" = ").append(style.HOLDER_START)
                .append(properId).append(style.HOLDER_END);
        while (colIt.hasNext() && propertieIt.hasNext()) {
            colId = colIt.next();
            properId = propertieIt.next();
            condition.append(" and ").append(style.getKeyWordHandler().getCol(colId)).append(" = ")
                        .append(style.HOLDER_START).append(properId).append(style.HOLDER_END);
            }
        }
    
        return condition.toString();
    }

代码复杂是因为考虑到通用性,尤其是符合主键这里,如果你的表都是单一主键,或者主键都叫ID,那代码就只有几行

同样的setIdsParas也因为复合主键原因,也较为复杂,内容如下

    private Map setIdsParas(SQLManager sm, Object key, Class entityClass) {
        AbstractDBStyle style = (AbstractDBStyle) sm.getDbStyle();
        MetadataManager metadataManager = sm.getMetaDataManager();
        NameConversion nameConversion = sm.getNc();
        String tableName = nameConversion.getTableName(entityClass);

        TableDesc table = metadataManager.getTable(tableName);
        ClassDesc desc = table.getClassDesc(entityClass, nameConversion);
        Map paras = new HashMap();
        List<String> idAttrs = desc.getIdAttrs();
        if (idAttrs.size() == 1) {
            paras.put(idAttrs.get(0), key);
        } else {
            // 来自对象id的属性.复合主键
            Map<String, Object> map = desc.getIdMethods();
            for (int i = 0; i < idAttrs.size(); i++) {
                String idCol = idAttrs.get(i);
                String idAttr = idAttrs.get(i);
                Method m = (Method) map.get(idAttr);
                try {
                    Object os = m.invoke(key, new Object[0]);
                    paras.put(idAttr, os);
                } catch (Exception ex) {
                    throw new BeetlSQLException(BeetlSQLException.ID_VALUE_ERROR, "无法设置复合主键:" + idCol, ex);
                }
            }

        }
        return paras;
    }

完成上诉代码后,可以添加这个实现

config.getBuilder().addAmi("single", new SingleAmiExt());

然后,可以使用了,比如按照主键查询,只关心id和name

dao.single(1,Arrays.asList("id","name"));

结束语: BaseMapper大部分扩展都较为简单,这里的SingleAmiExt复杂是考虑到复合主键,且现在的BeetlSQL版本还无法重用内置的代码生成片段导致的,叫来BeetlSQL版本会把内置代码生成独立出来,可以供第三方工具使用,这样,开发者就能轻易扩展BeetlSQL了。

关于BeetlSQL,请参考官网ibeetl.com

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
MybatisPlus的BaseMapper和Wrapper使用
一、简介  在MybatisPlus中,BaseMapper中定义了一些常用的CRUD方法,当我们自定义的Mapper接口继承BaseMapper后即可拥有了这些方法。二、BaseMapper中的CRUD方法通用CRUD封装BaseMapper(https://gitee.com/baomidou/mybatisplus/blo
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这