文档章节

Beetlsql扩展之自定义Mapper

闲大赋
 闲大赋
发布于 2017/09/26 21:59
字数 1485
阅读 222
收藏 0

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

© 著作权归作者所有

共有 人打赏支持
闲大赋

闲大赋

粉丝 1160
博文 94
码字总数 87660
作品 10
西城
架构师
私信 提问
BeetlSQL 2.10.29 发布,Java Dao 工具

BeetlSQL 2.10.29 发布,更新如下: #IKPBA mapper 接口 支持default method,采用了liuzou1991 对mapper的优化,liuzhou也是提供BeetlSQL的Mapper功能作者之一 #IKPBB 代码生成优化,采用八...

闲大赋
06/27
735
2
Beetl Starter 1.1.32 发布,增强定制接口

这次发布增强了Starter定制Beetl和BeetlSQL的接口 提供BeetlSqlCustomize类定制BeetlSQL 提供BeetlTemplateCustomize类定制Beetl POM beetl-framework-starter 是 SpringBoot 与 Beetl 和 Be......

闲大赋
01/24
506
2
Beetl Starter 1.1.0发布,SpringBoot自动装配工具

beetl-framework-starter 是 SpringBoot 与 Beetl 和 BeetlSql 集成的自动转配工具,只需要添加如下依赖就可以自动使用 Beetl 和 BeetlSql 的功能 默认情况下,Beetl的模板配置在templates目...

闲大赋
2017/06/07
0
0
hibernate,mybatis,beetlsql 全面比较

这是我的一个综合评分,总共分为12个单项,每个单项最高5分,最低0分。注意,评价只包含这些软件提供的标准功能,不包含第三方提供的功能,如代码生成等。 开发效率 hibernate 能获取数据库m...

闲大赋
2016/02/19
0
22
BeetlSQL,简单和强大数据库访问工具(更新)

beetlsql 特点 BeetSql是一个全功能DAO工具, 同时具有Hibernate 优点 & Mybatis优点功能,适用于承认以SQL为中心,同时又需求工具能自动能生成大量常用的SQL的应用。 无需注解,自动生成大量...

闲大赋
2015/08/17
0
61

没有更多内容

加载失败,请刷新页面

加载更多

Yearning基于Inception的开源SQL审核平台

基础环境: python3 nodejs vuejs mysql python-ldap pymysql mysql 新建数据库 create database Yearning DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; Navicat导入初始化数据 : ......

以谁为师
10分钟前
0
0
flutter Expanded用法

使用的地方:一个分类,类似京东的,左右两边都可以滑动 Widget build(BuildContext context) { return Row(children: [ Column( children: <Widget>[ Ex......

大灰狼wow
15分钟前
2
0
Java8 Map中新增的方法使用总结

前言 得益于 Java 8 的 default 方法特性,Java 8 对 Map 增加了不少实用的默认方法,像 getOrDefault, forEach, replace, replaceAll, putIfAbsent, remove(key, value), computeIfPresent,......

kaixin_code
25分钟前
1
0
@TransactionConfiguration

@TransactionConfiguration过时与替代写法 @TransactionConfiguration 替代写法

miaojiangmin
27分钟前
0
0
浅谈Vue响应式(数组变异方法)

很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法。 看来想让女...

开元中国2015
29分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部