je-ibatis
- 支持平台元数据:提供MetaDataParse接口,默认使用JdbcTemplate查询数据库获取资源表和功能信息,支持自定义元数据解析器(详见配置)
- 主键策略拓展:在mybatis现有自增主键、查询主键的基础上,增加路由主键,插入数据时动态路由到资源表对应的主键策略。
- 元数据标签:提供<meta/>标签,可在mybatis的xml中使用,通过方法参数中的TableCode与标签type属性(update|insert|select|columns|pk-col|load|load-columns),动态生成sql。
- 条件解析器:增加ConditionsWrapper实现条件封装,支持自定义sql与复杂条件语句嵌套。
- 元数据CRUD:基于上述拓展,封装MetaBaseMapper,使用MetaStatementBuilder解析。继承MetaBaseMapper即可使用基础方法
- 分页插件:拓展MyBatis插件,支持多数据库方言,支持拓展。
Spring-MVC 配置
<!-- Mybatis SessionFactory-->
<bean id="sqlSessionFactory" class="com.je.ibatis.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configurationProperties" >
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<!-- mybatis配置文件 -->
<property name="locations" value="classpath*:mybatis.properties"/>
</bean>
</property>
<!-- 元数据解析器 -->
<property name="metaDataParse">
<bean class="com.je.ibatis.extension.parse.DefaultMetaDataParse"/>
</property>
<property name="plugins">
<array>
<!-- 分页插件配置 -->
<bean id="paginationInterceptor" class="com.je.ibatis.extension.plugins.PaginationInterceptor"/>
</array>
</property>
</bean>
<!-- MapperScanner 扫描Mapper 创建代理 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.je.**.mapper" />
<property name="annotationClass" value="org.apache.ibatis.annotations.Mapper" />
</bean>
MetaBaseMapper 平台基础CRUD封装
说明:
- 以下出现的变量
mapper
均为MetaBaseMapper
子接口的Mapper实例 - 以下出现的变量
tableCode
均为平台资源表名称 - 以下出现的变量
pkValue
均为主键 - 以下出现的变量
wrapper
均为ConditionsWrapper
实例
继承 MetaBaseMapper
@Mapper
public interface MetaMapper extends MetaBaseMapper {
}
注入 MetaBaseMapper 子接口实例
@Autowired
private MetaMapper baseDataMapper;
insertMap
- 平台资源表基础插入方法,须指定 tableCode
int insertMap(Map<String, Object> beanMap)
- 示例
Map<String, Object> beanMap = new HashMap<>(); beanMap.put(Constants.KEY_TABLE_CODE, tableCode); // 如果插入前未指定主键值,则会使用自动生成主键策略 // beanMap.put("ID", "1"); beanMap.put("CODE", "编号"); beanMap.put("NAME", "名称"); // 插入数据 mapper.insertMap(beanMap); // 主键为自动生成时,插入后会将主键值放入Map Object pkValue = beanMap.get("ID");
updateMap
- 平台资源表基础修改方法,须指定 tableCode
int updateMap(@Param(Constants.MAP_ALIAS) Map<String, Object> beanMap, @Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper)
- 示例
Map<String, Object> beanMap = new HashMap<>(); beanMap.put(Constants.KEY_TABLE_CODE, tableCode); beanMap.put("CODE", "编号01"); beanMap.put("NAME", "名称01"); // 如果wrapper为空时,使用主键作为where条件 beanMap.put(Constants.KEY_PK_VALUE, pkValue); //update语句的where条件 ConditionsWrapper wrapper = ConditionsWrapper.builder().apply("CODE = '编号' and NAME = '名称'"); mapper.updateMap(beanMap, wrapper);
select
- 平台资源表基础查询方法,查询资源表全部字段,须指定 tableCode
List<Map<String, Object>> select(@Param(Constants.PAGE_ALIAS) Page page, @Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//页编码 current = -1 时不分页 int current = 1; //每页数量 int size = 30; Page page = new Page(current, size); //select语句的where条件 ConditionsWrapper wrapper = ConditionsWrapper.builder() //指定tableCode .table(tableCode) //添加where条件 .apply("CODE = '编号' and NAME = '名称'") .apply(" ORDER BY CODE "); //wrapper作为where条件语句时不能以AND开头, 可调用 wrapper.getSql()进行调试。 List<Map<String, Object>> list = mapper.select(page, wrapper);
load
- 平台功能基础查询方法,只查询功能列表加载字段,须指定 funcCode 功能编码
List<Map<String, Object>> load(@Param(Constants.PAGE_ALIAS) Page page, @Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//页编码 current = -1 时不分页 int current = 1; //每页数量 int size = 30; Page page = new Page(current, size); //select语句的where条件 ConditionsWrapper wrapper = ConditionsWrapper.builder() //指定funcCode .function(funcCode) //添加where条件 .apply("CODE = '编号' and NAME = '名称'") .apply(" ORDER BY CODE "); //wrapper作为where条件语句时不能以AND开头, 可调用 wrapper.getSql()进行调试。 List<Map<String, Object>> list = mapper.load(page, wrapper);
selectOneByPk
- 根据主键查询,须指定 tableCode pkValue
Map<String, Object> selectOneByPk(@Param(Constants.KEY_TABLE_CODE) String tableCode, @Param(Constants.KEY_PK_VALUE) String pkValue);
- 示例
//使用表名与主键查询,查询资源表所有列 Map<String, Object> map = mapper.selectOneByPk(tableCode, pkValue);
delete
- 平台资源表基础删除方法
int delete(@Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//delete语句的where条件 ConditionsWrapper wrapper = ConditionsWrapper.builder() //指定tableCode .table(tableCode) //添加where条件 .apply("CODE = '编号' and NAME = '名称'") .apply(" ORDER BY CODE "); //wrapper作为where条件语句时不能以AND开头, 可调用 wrapper.getSql()进行调试。 int num = mapper.delete(wrapper);
selectSql
- 自定义Sql查询
List<Map<String, Object>> selectSql(@Param(Constants.PAGE_ALIAS) Page page, @Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//页编码 current = -1 时不分页 int current = 1; //每页数量 int size = 30; Page page = new Page(current, size); //wrapper为完整select语句 ConditionsWrapper wrapper = ConditionsWrapper.builder() .apply("select * from USER where ") //添加where条件 .eq("CODE", "编号") .apply(" and NAME = {0}", "名称") .apply(" ORDER BY CODE "); List<Map<String, Object>> list = mapper.selectSql(page, wrapper);
insertSql
- 执行新增sql
int insertSql(@Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//wrapper为完整语句 ConditionsWrapper wrapper = ConditionsWrapper.builder() .apply("INSERT INTO USER (CODE, NAME) VALUES ({0}, {1})", "编号", "名称"); int num = mapper.insertSql(wrapper);
updateSql
- 执行更新sql
int updateSql(@Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//wrapper为完整语句 ConditionsWrapper wrapper = ConditionsWrapper.builder() .apply("UPDATE USER SET AGE = {0} WHERE CODE = {1} ", 18, "Wang"); int num = mapper.updateSql(wrapper);
deleteSql
- 执行删除sql
int deleteSql(@Param(Constants.WRAPPER_ALIAS) ConditionsWrapper wrapper);
- 示例
//wrapper为完整语句 ConditionsWrapper wrapper = ConditionsWrapper.builder() .apply("DELETE FROM USER WHERE CODE = {0} ", "Wilson"); int num = mapper.deleteSql(wrapper);
基于 MetaBaseMapper 的 MetaService 封装
Mybatis数据持久层为接口,无实现代码,所以持久业务逻辑交由 MetaService 实现。其中方法有多个重载,参数含义在方法注释中。其中 load 方法为特殊实现,除核心代码之外应该不会用到,可以使用 select、selectSql 方法来实现查询。核心参数 ConditionsWrapper 使用详见下面说明。
ConditionsWrapper 是一个条件构造器,本质是 sql + 预处理参数Map,使用预处理来避免sql注入风险。配合 MetaService 中的方法使用。
在 update、delete、select、selectOne
方法中,ConditionsWrapper 中的 sql 内容应该是 where 关键字之后
的内容,如 id = ? and name like ? order by code
。where 关键字及 select 的字段内容会通过参数 tableCode
自动生成补全。上述方法中 tableCode 参数的添加有两种方式,第一种为参数添加 select(tableCode,wrapper)
,
第二种为 ConditionsWrapper 添加 wrapper.table(tableCode)
然后再将 wrapper 作为入参调用方法。
在 selectSql、executeSql、countBySql
方法中,ConditionsWrapper 或 sql 参数中的 sql 内容应该是一段完整的sql
,如 select * from user where id = ? and name like ? order by code
、delete from user where id = ?
。
ConditionsWrapper 条件构造器
说明:
- 以下方法须按照正常sql语法顺序调用!,列如 orderBy 一定要最后调用
- 以下出现的第一个入参
boolean condition
表示该条件是否加入sql中 - 以下代码块内的多个方法均为从上往下补全个别
boolean
类型的入参,默认为true
- 以下方法入参中的
String column
均表示数据库字段 - 以下举例均为使用普通wrapper,入参为
Map
和List
的均以json
形式表现!
builder
builder()
builder(String applySql, Object... params)
- 静态方法,构建一个 ConditionsWrapper 对象
- 例:
ConditionsWrapper.builder().apply("select count(*) from JE_CORE_DEPARTMENT where DEPTCODE = {0}", deptCode)
- 例:
ConditionsWrapper.builder("select count(*) from JE_CORE_DEPARTMENT where DEPTCODE = {0}", deptCode)
allEq
allEq(Map<String, Object> params)
allEq(Map<String, Object> params, boolean null2IsNull)
allEq(boolean condition, Map<String, Object> params, boolean null2IsNull)
全部eq(或个别isNull)
个别参数说明:params
:key
为数据库字段名,value
为字段值null2IsNull
: 为true
则在map
的value
为null
时调用 isNull 方法,为false
时则忽略value
为null
的例1:
allEq({id:1,name:"小明",age:null})
—>id = 1 and name = '小明' and age is null
例2:
allEq({id:1,name:"小明",age:null}, false)
—>id = 1 and name = '小明'
allEq(BiPredicate<String, Object> filter, Map<String, Object> params)
allEq(BiPredicate<String, Object> filter, Map<String, Object> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<String, Object> filter, Map<String, Object> params, boolean null2IsNull)
个别参数说明:filter
: 过滤函数,是否允许字段传入比对条件中params
与 null2IsNull
: 同上
- 例1:
allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"小明",age:null})
—>name = '小明' and age is null
- 例2:
allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"小明",age:null}, false)
—>name = '小明'
eq
eq(String column, Object val)
eq(boolean condition, String column, Object val)
- 等于 =
- 例:
eq("name", "小明")
—>name = '小明'
ne
ne(String column, Object val)
ne(boolean condition, String column, Object val)
- 不等于 <>
- 例:
ne("name", "小明")
—>name <> '小明'
gt
gt(String column, Object val)
gt(boolean condition, String column, Object val)
- 大于 >
- 例:
gt("age", 18)
—>age > 18
ge
ge(String column, Object val)
ge(boolean condition, String column, Object val)
- 大于等于 >=
- 例:
ge("age", 18)
—>age >= 18
lt
lt(String column, Object val)
lt(boolean condition, String column, Object val)
- 小于 <
- 例:
lt("age", 18)
—>age < 18
le
le(String column, Object val)
le(boolean condition, String column, Object val)
- 小于等于 <=
- 例:
le("age", 18)
—>age <= 18
between
between(String column, Object val1, Object val2)
between(boolean condition, String column, Object val1, Object val2)
- BETWEEN 值1 AND 值2
- 例:
between("age", 18, 30)
—>age between 18 and 30
notBetween
notBetween(String column, Object val1, Object val2)
notBetween(boolean condition, String column, Object val1, Object val2)
- NOT BETWEEN 值1 AND 值2
- 例:
notBetween("age", 18, 30)
—>age not between 18 and 30
like
like(String column, Object val)
like(boolean condition, String column, Object val)
- LIKE ‘%值%’
- 例:
like("name", "强")
—>name like '%强%'
notLike
notLike(String column, Object val)
notLike(boolean condition, String column, Object val)
- NOT LIKE ‘%值%’
- 例:
notLike("name", "强")
—>name not like '%强%'
likeLeft
likeLeft(String column, Object val)
likeLeft(boolean condition, String column, Object val)
- LIKE ‘%值’
- 例:
likeLeft("name", "强")
—>name like '%强'
likeRight
likeRight(String column, Object val)
likeRight(boolean condition, String column, Object val)
- LIKE ‘值%’
- 例:
likeRight("name", "强")
—>name like '强%'
isNull
isNull(String column)
isNull(boolean condition, String column)
- 字段 IS NULL
- 例:
isNull("name")
—>name is null
isNotNull
isNotNull(String column)
isNotNull(boolean condition, String column)
- 字段 IS NOT NULL
- 例:
isNotNull("name")
—>name is not null
in
in(String column, Collection<?> value)
in(boolean condition, String column, Collection<?> value)
- 字段 IN (value.get(0), value.get(1), …)
- 例:
in("age",[1,2,3])
—>age in (1,2,3)
- 例:
in("age",[])
—>age in (null)
in(String column, Object... values)
in(boolean condition, String column, Object... values)
- 字段 IN (v0, v1, …)
- 例:
in("age", 1, 2, 3)
—>age in (1,2,3)
notIn
notIn(String column, Collection<?> value)
notIn(boolean condition, String column, Collection<?> value)
- 字段 IN (value.get(0), value.get(1), …)
- 例:
notIn("age",[1,2,3])
—>age not in (1,2,3)
notIn(String column, Object... values)
notIn(boolean condition, String column, Object... values)
- 字段 NOT IN (v0, v1, …)
- 例:
notIn("age", 1, 2, 3)
—>age not in (1,2,3)
inSql
inSql(String column, String inValue)
inSql(boolean condition, String column, String inValue)
- 字段 IN ( sql语句 )
- 例:
inSql("age", "1,2,3,4,5,6")
—>age in (1,2,3,4,5,6)
- 例:
inSql("id", "select id from table where id < 3")
—>id in (select id from table where id < 3)
notInSql
notInSql(String column, String inValue)
notInSql(boolean condition, String column, String inValue)
- 字段 NOT IN ( sql语句 )
- 例:
notInSql("age", "1,2,3,4,5,6")
—>age not in (1,2,3,4,5,6)
- 例:
notInSql("id", "select id from table where id < 3")
—>age not in (select id from table where id < 3)
groupBy
groupBy(String columns)
groupBy(boolean condition, String columns)
- 分组:GROUP BY 字段, …
- 例:
groupBy("id", "name")
—>group by id,name
orderByAsc
orderByAsc(String columns)
orderByAsc(boolean condition, String columns)
- 排序:ORDER BY 字段, … ASC
- 例:
orderByAsc("id", "name")
—>order by id ASC,name ASC
orderByDesc
orderByDesc(String columns)
orderByDesc(boolean condition, String columns)
- 排序:ORDER BY 字段, … DESC
- 例:
orderByDesc("id", "name")
—>order by id DESC,name DESC
orderBy
orderBy(boolean condition, boolean isAsc, String columns)
- 排序:ORDER BY 字段, …
- 例:
orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
or
or()
or(boolean condition)
拼接 OR
注意事项:
主动调用or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接)例:
eq("id",1).or().eq("name","小明")
—>id = 1 or name = '小明'
or(Consumer<ConditionsWrapper> consumer)
or(boolean condition, Consumer<ConditionsWrapper> consumer)
- OR 嵌套
- 例:
or(i -> i.eq("name", "小明").ne("status", "1"))
—>or (name = '小明' and status <> '1')
and
and(Consumer<ConditionsWrapper> consumer)
and(boolean condition, Consumer<ConditionsWrapper> consumer)
- AND 嵌套
- 例:
and(i -> i.eq("name", "小明").ne("status", "1"))
—>and (name = '小明' and status <> '1')
nested
nested(Consumer<ConditionsWrapper> consumer)
nested(boolean condition, Consumer<ConditionsWrapper> consumer)
- 正常嵌套 不带 AND 或者 OR
- 例:
nested(i -> i.eq("name", "小明").ne("status", "1"))
—>(name = '小明' and status <> '1')
exists
exists(String existsSql)
exists(boolean condition, String existsSql)
- 拼接 EXISTS ( sql语句 )
- 例:
exists("select id from table where age = 1")
—>exists (select id from table where age = 1)
notExists
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
- 拼接 NOT EXISTS ( sql语句 )
- 例:
notExists("select id from table where age = 1")
—>not exists (select id from table where age = 1)
apply
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
- 拼接 sql
注意事项: - 动态入参
params
对应前面applySql
内部的{index}
部分.使用预处理方式,不会有sql注入风险的! - 此方法会保留原有sql,不会对sql字符串进行增删操作。
- 可变参中不可包含数组,如有in语句预处理参数应使用List或Set(见示例)。
示例
- 例:
apply("id = 1")
—>id = 1
- 例:
apply("id in ({0})", Lists.newArrayList(1,2,3))
—>and id in(1,2,3)
- 例:
apply("name like {0}", "jay%")
—>name like 'jay%'
- 例:
apply("name like {0}", "%jay%")
—>name like '%jay%'
- 例:
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
- 例:
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
table
table(String table)
- 指定操作的表名
- 例:
table('JE_CORE_ENDUSER')
getTable
String getTable()
- 获取指定操作的表名
getSql
String getSql()
- 查看最终拼接完成的 sql 语句
- 例:
getSql()
getParameterSql
String getParameterSql()
- 获取 ConditionsWrapper 的非预处理sql
- 例:
USER_ID in (#{wrapper.parameter.wrapper1},#{wrapper.parameter.wrapper1}) and USER_NAME = #{wrapper.parameter.wrapper3}
- =>
USER_ID in ('wrapper1Value','wrapper2Value') and USER_NAME = 'wrapper3Value'
put
String put(Object val)
- 添加参数,返回自动生成的参数key
putAll
ConditionsWrapper putAll(Map map)
- 添加参数集
getParameter
ParameterWrapper getParameter()
- 获取所有参数集
isEmpty
boolean isEmpty()
- 检测 ConditionsWrapper 是否为空
clone
ConditionsWrapper clone()
- 克隆一个新的 ConditionsWrapper 对象
replace
ConditionsWrapper replace(char oldChar, char newChar)
- 替换 ConditionsWrapper sql语句中的内容