BeetlSQL 企业插件库
About 1731 wordsAbout 6 min
1984-01-24
BeetlSQL插件库提供了适合企业应用的多种扩展库,目前包括
sql-accelerator加速包,通过各种优化能让BeetlSQL性能提高50%,尽量接近手写JDBC的性能sql-bean-encrypt字段加密包,用于企业数据脱敏sql-firewallSQL防火墙,用于犯法不合法数据操作sql-xmlMyBatis语法支持,支持90%的mybatis XML 语法sql-dynamic-table像操作静态表一样操作动态表,适合有动态创建表的需求sql-jooq使用jooq生成的sql语句,用beetlsql执行- sql-template-sugar sql模版语法糖,提供了and or,asc,desc,set
所有源码 在 https://gitee.com/xiandafu/beetlsql/tree/master/sql-ext
sql-accelerator
BeetlSQL通过此包,对BeetlSQL的性能进行提速,优化手段主要是使用字节码生成来加速属性的访问,实现类在BeanPropertyWriteFactory
public class BeanPropertyWriteFactory {
public static BeanPropertyAsm getBeanProperty(Class c){
//生成一个POJO的类属性访问类
}
}开发者使用时候无需关心细节,只需要引入sql-accelerator,并对SQLManager进行定制
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>sql-accelerator</artifactId>
<version>${version}</version>
</dependency>通过PerformanceConfig类定制SQLManager
PerformanceConfig performanceConfig = new PerformanceConfig();
performanceConfig.config(sqlManager);通过性能评测 https://gitee.com/xiandafu/beetlsql/tree/master/sql-jmh, 比较BeetlSQL和加速 BeetlSQL,以及JDBC,大多数情况下,BeetlSQL的TPS为手写JDBC的一半
Benchmark Mode Cnt Score Error Units
Beetl.addEntity thrpt 5 107.421 ± 63.432 ops/ms
Beetl.complexMapping thrpt 5 199.583 ± 291.175 ops/ms
Beetl.executeJdbcSql thrpt 5 294.096 ± 72.272 ops/ms
Beetl.executeTemplateSql thrpt 5 248.226 ± 88.204 ops/ms
Beetl.getAll thrpt 5 13.582 ± 0.590 ops/ms
Beetl.getEntity thrpt 5 324.596 ± 79.393 ops/ms
Beetl.lambdaQuery thrpt 5 172.802 ± 63.639 ops/ms
Beetl.one2Many thrpt 5 159.437 ± 130.334 ops/ms
Beetl.pageQuery thrpt 5 136.335 ± 45.815 ops/ms
Beetl.sqlFile thrpt 5 219.103 ± 46.331 ops/ms
GeneralBeetl.addEntity thrpt 5 101.724 ± 51.099 ops/ms
GeneralBeetl.complexMapping thrpt 5 195.298 ± 278.922 ops/ms
GeneralBeetl.executeJdbcSql thrpt 5 141.368 ± 41.212 ops/ms
GeneralBeetl.executeTemplateSql thrpt 5 129.438 ± 30.630 ops/ms
GeneralBeetl.getAll thrpt 5 7.889 ± 1.251 ops/ms
GeneralBeetl.getEntity thrpt 5 127.539 ± 41.250 ops/ms
GeneralBeetl.lambdaQuery thrpt 5 106.850 ± 42.768 ops/ms
GeneralBeetl.one2Many thrpt 5 128.468 ± 86.845 ops/ms
GeneralBeetl.pageQuery thrpt 5 93.118 ± 32.210 ops/ms
GeneralBeetl.sqlFile thrpt 5 134.057 ± 27.093 ops/ms
Jdbc.addEntity thrpt 5 212.159 ± 239.850 ops/ms
Jdbc.executeJdbcSql thrpt 5 661.997 ± 127.704 ops/ms
Jdbc.getAll thrpt 5 34.760 ± 9.619 ops/ms
Jdbc.getEntity thrpt 5 686.916 ± 136.272 ops/mssql-bean-encrypt
对字段的值在入库的时候,自动加密,出库的时候自动解密,保证数据库的值是脱敏的,参考《数据模型》安全扩展注解
BeetlSQL提供了安全扩展包支持,MD5,AES,DES等字段加密和解密
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>sql-bean-encrypt</artifactId>
<version>${version}</version>
</dependency>使用方式举例如下,比如@MD5,或者@AES,@DES,@SM4
String code;
@MD5(saltProperty = "code")
String content;
@AES // 或者 @DES
String phone;安全扩展包是加密注解实现了AttributeConvert接口,可以参考ql-bean-encrypt的源码或者的AttributeConvert文档编写自己的加密注解
以@AES为例子
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD})
@Builder(AESConvert.class)
public @interface AES {
}@Builder说明了使用哪一个类实现此注解,类AESConvert负责,代码如下
public class AESConvert implements AttributeConvert {
@Override
public Object toDb(ExecuteContext ctx, Class cls, String name, Object pojo) {
String value= (String) BeanKit.getBeanProperty(pojo,name);
if(value==null){
return null;
}
SymmetricCrypto aes =getAes(cls,name);
String encryptData = aes.encryptHex(value);
return encryptData;
}
@Override
public Object toAttr(ExecuteContext ctx, Class cls, String name, ResultSet rs, int index) throws SQLException {
String value = rs.getString(index);
if(value==null){
return null;
}
SymmetricCrypto aes =getAes(cls,name);
String decryptData = aes.decryptStr(value);
return decryptData;
}
protected SymmetricCrypto getAes(Class cls, String name){
String key = EncryptConfig.get(EncryptType.AES);;
byte[] byteKey = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), key.getBytes(StandardCharsets.ISO_8859_1)).getEncoded();
SymmetricCrypto aes = SecureUtil.aes(byteKey);
return aes;
}
}sql-firewall
SQL防火墙,用于防止一些意外操作,如删除全表
//配置
FireWall fireWall = new FireWall().setDmlCreateEnable(false).setSqlMaxLength(50);
FireWallConfig fireWallConfig = new FireWallConfig(fireWall);
fireWallConfig.config(sqlManager);
//测试
try{
String sql = "delete from order_log";
sqlManager.executeUpdate(new SQLReady(sql));
Assert.fail();
}catch (Exception exception){
Assert.assertTrue(exception instanceof BeetlSQLException);
}注意,因为涉及到解析sql,因此有性能损耗
免责,sql-firewall并不完善,使用上造成的数据损失不应该怪BeetlSQL
sql-xml
如果你喜欢XML语法,准确的说,喜欢Mybatis语法,此扩展包提供了MyBatis的xml语法支持,具体源码和测试例子 在 https://gitee.com/xiandafu/beetlsql/tree/master/sql-ext/sql-xml/src/test
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>sql-xml</artifactId>
<version>${version}</version>
</dependency>初始化方式如下,因此sql文件变成支持.xml方式
XMLBeetlSQL xmlBeetlSQL = new XMLBeetlSQL();
xmlBeetlSQL.config(sqlManager);如下是一个xml文件例子,根节点是beetlsql
<?xml version="1.0" encoding="UTF-8" ?>
<beetlsql>
<sql id="testAll">
<!-- 测试sql -->
select * from sys_user
<where>
<if test="has(user) and user.name=='a'">
and name='3'
</if>
<bind value="1+99" export="newValue"/>
<isNotEmpty value="'1'">
and name='5'
</isNotEmpty>
and name = #{newValue}
and name in
<foreach items="[1,2,3]" var="id,status" open="(" close=")" separator=",">
#{id+status.index}
</foreach>
<include refid="commonWhere"/>
</where>
</sql>
<sql id="commonWhere">
and name='bdfdsf'
</sql>
<resultMap id="complexListMap">
<result property="id" column="id"/>
<collection property="listInfo" >
<result property="name" column="name"/>
<result property="age" column="age"/>
</collection>
</resultMap>
</beetlsql>需要注意的是,beetlsql模拟了所有的myabtis xml节点(如果我遗忘了,请邮件告诉我),mybatis的表示是语法是ognl语法上,beetlsql这里采用了beetl语法,因此还是有差异,比如上面的if,用来判断变user的name变量是否为'a',与mbatis不一样
<if test="has(user) and user.name=='a'">mybatis是这样的
<if test=" user != null and user.name=='a'">Beetlsql 采用了has方式,表达了user可能不存在的可能。mybatis的ongl语法则内置了了这一点
如果高度定制Beetl语法,这可以满足,比如beetl支持默认模板采用安全输出,因此也可以类似myabtsi 这样。
sql-jooq
未完成
sql-template-sugar
SQL模版的语法糖,简化那种需要判断后再输出的情况,可以运行ShortHolderTest了解其使用
SugarTemplateConfig sugarBeetlSQL = new SugarTemplateConfig();
sugarBeetlSQL.config(sqlManager);需要引入
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>sql-template-sugar</artifactId>
<version>3.38.0-RELEASE</version>
</dependency>简化 and 和 or
提供了and和or格式化函数
select * from user where 1=1
#{name,and}
#{userAge,and}类似sql模版
select * from user where 1=1
@--: if(notEmpty(name)){
and name=#{name}
@--:}
@--: if(notEmpty(userAge)){
and user_age=#{userAge}
@--:}也允许使用较为复杂的表达式
select * from user u where 1=1
#{"u.age">age+1,and}beetlsql插件将解析#{}中的第一个变量age,生成类似sql模版
select * from user u where 1=1
@--: if(notEmpty(age)){
and u.age>#{age+1}
@--:}简化 update
提供了set格式化函数
update user set #{name,set} #{userAge,set} where id =#{id}类似sql模版
update user set
@--: if(notEmpty(name)){
name=#{name}
@--:}
@--: if(notEmpty(userAge)){
,user_age=#{userAge}
@--:}注意,此扩展包会检测是否sql语句是否以set结尾。 如果没有,则增加,. 占位符里可以用任意表达式,beetlsql会找到第一个变量来判断非空 这个同and or 一致
简化order by
允许使用格式化函数asc desc 来自动增加一个order by语句。
select * from user ${orderCol,asc}类似sql模版
select * from user
@--: if(notEmpty(orderCol)){
${orderCol} asc
@--:}注意,目前此插件不支持多个列排序,只支持使用asc 或者 desc 一次。另外,只能使用${} 而非 #{}
如果你想实现语法糖
- 参考SugarSQLGrammarCreator,对beetl的sql模版语法树进行调整,比如使用
ShortHolderFactory.create返回新的PlaceholderST
public class SugarSQLGrammarCreator extends SQLGrammarCreator {
public SugarSQLGrammarCreator(SQLManager sqlManager) {
super(sqlManager);
}
@Override
public PlaceholderST createTextOutputSt(Expression exp, FormatExpression format) {
disableSyntaxCheck("TextOutputSt");
if(format!=null&& ShortHolderFactory.isSupport(format.getName())){
PlaceholderST st =ShortHolderFactory.create(sqlManager.getNc(),format.getName(),exp);
if(st==null){
throw new IllegalArgumentException("错误的用法 "+format.getName()+" "+exp.token.toString());
}
return st;
}
return new SQLPlaceholderST(exp, format, null);
}