BeetlSql 有一个BaseMapper,提供了不少内置的Dao操做,如增删改查等10几个方法,用户只须要些一个类继承此接口便能很快的完成一个Dao,好比程序员
public interface UserDao extends BaseMapper<User>{ }
UserDao没有包含任何方法,但集成了BaseMapper,所以具有了不少内置的Dao操做。若是这些操做不知足要求,可使用BeetlSql的MD文档来维护复杂的SQL,好比,一个根据角色查询全部用户的操做sql
@SqlResource("common.user") public interface UserDao extends BaseMapper<User>{ List<User> getUsersByRole(String role); }
注解@SqlResource注明了sql文件的位置,位于common/user.md 文件,其中user.md 包含了sql查询app
getUsersByRole === select u.* from user,user_role,role where ....
这是一个BeetlSql的基本概念,在这里不详细说明,有兴趣的能够参考官网文档 如今有这样一个需求,BaseMapper包含了太多的内置方法,有时候程序员对其中的某些方法并不敢兴趣,有的内置方法则是不容许调用的,好比deleteById 方法,老是有一次物理删除操做。应该在企业应用系统里禁止这种调用,那么,BeetlSql可否定制BaseMapper呢?ide
答案是能定制。只须要新写一个接口,经过SQLManager注册为BaseMapper便可,而后,为接口的每一个方法添加实现便可,实际上,自带的BaseMapper就是这样实现的。工具
以下是一个自定义的BaseMapper,为了简单起见,Dao只提供三个接口ui
public interface MyBaseMapper<T> { public T single(Object key); public T single(Object key,List attrs); public List<T> allData(); }
第一个方法single和第三个all方法都是原有的BaseMapper自带,第二个方法是根据主键获取对象,但只获取部分属性。咱们将在博客最后讲述如何实现,如今,咱们要实现allData方法。this
allData方法返回目标对象对应的数据全部值,这在原有的BaseMapper已经实现,是经过org.beetl.sql.core.mapper.internal.AllAmi 实现,所以咱们只须要复用这个实现。code
首先,告诉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