BeetlSQL 3.3.0 发布,新年快乐

Stella981
• 阅读 830
  • BeetlSQL3 集成 ActFramework ,Act框架是一个非常强大的Web框架

  • 修复了自从3.x以来可能导致内置SQL找不到的BUG,建议升级

  • 增强了Clickhouse集成,Clickhouse 不支持“主键”,BeetlSQL为CH提供自定义的MetaDataManager以支持@AssignId。

    com.ibeetl beetlsql 3.3.0-RELEASE

BeetlSQL 的目标是提供开发高效,维护高效,运行高效的数据库访问框架,以我20年在电信,金融以及互联网天天CRUD的经验总结得来的框架,适用范围广。目前支持的数据库如下

  • 传统数据库:MySQL,MariaDB,Oralce,Postgres,DB2,SQL Server,H2,SQLite,Derby,神通,达梦,华为高斯,人大金仓,PolarDB 等
  • 大数据:HBase,ClickHouse,Cassandar,Hive
  • 物联网时序数据库:Machbase,TD-Engine,IotDB
  • SQL查询引擎:Drill,Presto,Druid
  • 内存数据库:ignite,CouchBase

编译源码

git clone https://gitee.com/xiandafu/beetlsql mvn clean package mvn clean install #如果想修改源码

注意:BeetlSQL3 集成了Spring,以及支持大数据等,就算配置了国内镜像,也可能需要很长时间下载大数据依赖包,为了让编译快速通过,你需要进入pom.xml ,屏蔽sql-integration,sql-db-support,sql-jmh三个模块

sql-core sql-mapper sql-util sql-fetech beetlsql sql-gen sql-test sql-samples

阅读源码例子

可以从模块sql-samples中找得到所有例子,或者从sql-test 中运行单元测试例子,或者在sql-integration 中的各个框架单元测试中找到相关例子。所有例子都是基于H2内存数据库,可以反复运行

sql-samples为例子

sql-samples 又包含了三个模块大约100个例子

  • quickstart: BeetlSQL基础使用例子,可以快速了解BeetlSQL3
  • usuage: BeetlSQL所有API和功能
  • plugin:BeetlSQL高级扩展实例

以usuage模块为例子,包含如下代码

  • S01MapperSelectSample 15个例子, mapper中的查询演示
  • S02MapperUpdateSample 11个例子, mapper中更新操作
  • S03MapperPageSample 3个例子,mapper中的翻页查询
  • S04QuerySample 9个例子,Query查询
  • S05QueryUpdateSample 3个例子,Query完成update操作
  • S06SelectSample 14个例子,SQLManager 查询API
  • S07InsertSample 8个例子,SQLManager 插入新数据API,主键生成
  • S08UpdateSample 6个例子,更新数据
  • S09JsonMappingSample 5个例子, json配置映射
  • S10FetchSample 2个例子,关系映射
  • S11BeetlFunctionSample 2个例子,自定义sql脚本的方法

代码示例

例子1,内置方法,无需写SQL完成常用操作

UserEntity user = sqlManager.unique(UserEntity.class,1);

user.setName("ok123"); sqlManager.updateById(user);

UserEntity newUser = new UserEntity(); newUser.setName("newUser"); newUser.setDepartmentId(1); sqlManager.insert(newUser);

输出日志友好,可反向定位到调用的代码

┏━━━━━ Debug [user.selectUserAndDepartment] ━━━ ┣ SQL: select * from user where 1 = 1 and id=? ┣ 参数: [1] ┣ 位置: org.beetl.sql.test.QuickTest.main(QuickTest.java:47) ┣ 时间: 23ms ┣ 结果: [1] ┗━━━━━ Debug [user.selectUserAndDepartment] ━━━

例子2 使用SQL

String sql = "select * from user where id=?"; Integer id = 1; SQLReady sqlReady = new SQLReady(sql,new Object[id]); List<**UserEntity**> userEntities = sqlManager.execute(sqlReady,UserEntity.class); //Map 也可以作为输入输出参数 List<**Map**> listMap = sqlManager.execute(sqlReady,Map.class);

例子3 使用模板SQL

String sql = "select * from user where department_id=#{id} and name=#{name}"; UserEntity paras = new UserEntity(); paras.setDepartmentId(1); paras.setName("lijz"); List<**UserEntity**> list = sqlManager.execute(sql,UserEntity.class,paras);

String sql = "select * from user where id in ( #{join(ids)} )"; List list = Arrays.asList(1,2,3,4,5); Map paras = new HashMap(); paras.put("ids", list); List<**UserEntity**> users = sqlManager.execute(sql, UserEntity.class, paras);

例子4 使用Query类

支持重构

LambdaQuery<**UserEntity**> query = sqlManager.lambdaQuery(UserEntity.class); List<**UserEntity**> entities = query.andEq(UserEntity::getDepartmentId,1) .andIsNotNull(UserEntity::getName).select();

例子5 把数十行SQL放到sql文件里维护

//访问user.md#select SqlId id = SqlId.of("user","select"); Map map = new HashMap(); map.put("name","n"); List<**UserEntity**> list = sqlManager.select(id,UserEntity.class,map);

BeetlSQL 3.3.0 发布,新年快乐

例子6 复杂映射支持

支持像mybatis那样复杂的映射

  • 自动映射

@Data @ResultProvider(AutoJsonMapper.class) public static class MyUserView { Integer id; String name; DepartmentEntity dept; }

  • 配置映射,比MyBatis更容易理解,报错信息更详细

{ "id": "id", "name": "name", "dept": { "id": "dept_id", "name": "dept_name" }, "roles": { "id": "r_id", "name": "r_name" } }

例子7 最好使用mapper来作为数据库访问类

@SqlResource("user") /*sql文件在user.md里*/ public interface UserMapper extends BaseMapper<**UserEntity**> {

@Sql("select * from user where id = ?") UserEntity queryUserById(Integer id);

@Sql("update user set name=? where id = ?") @Update int updateName(String name,Integer id);

@Template("select * from user where id = #{id}") UserEntity getUserById(Integer id);

@SpringData/*Spring Data风格*/ List<**UserEntity**> queryByNameOrderById(String name);

/** * 可以定义一个default接口 * @return */ default List<**DepartmentEntity**> findAllDepartment(){ Map paras = new HashMap(); paras.put("exlcudeId",1); List<**DepartmentEntity**> list = getSQLManager().execute("select * from department where id != #{exlcudeId}",DepartmentEntity.class,paras); return list; }

/** * 调用sql文件user.md#select,方法名即markdown片段名字 * @param name * @return */ List<**UserEntity**> select(String name);

/** * 翻页查询,调用user.md#pageQuery * @param deptId * @param pageRequest * @return */ PageResult<**UserEntity**> pageQuery(Integer deptId, PageRequest pageRequest);

@SqlProvider(provider= S01MapperSelectSample.SelectUserProvider.class) List<**UserEntity**> queryUserByCondition(String name);

@SqlTemplateProvider(provider= S01MapperSelectSample.SelectUs List<**UserEntity**> queryUserByTemplateCondition(String name);

@Matcher /*自己定义个Matcher注解也很容易*/ List<**UserEntity**> query(Condition condition,String name); }

你看到的这些用在Mapper上注解都是可以自定义,自己扩展的

例子8 使用Fetch 注解

可以在查询后根据Fetch注解再次获取相关对象,实际上@FetchOne和 @FetchMany是自定义的,用户可自行扩展

@Data @Table(name="user") @Fetch public static class UserData { @Auto private Integer id; private String name; private Integer departmentId; @FetchOne("departmentId") private DepartmentData dept; }

/** * 部门数据使用"b" sqlmanager */ @Data @Table(name="department") @Fetch public static class DepartmentData { @Auto private Integer id; private String name; @FetchMany("departmentId") private List<**UserData**> users; }

例子9 不同数据库切换

可以自行扩展ConditionalSQLManager的decide方法,来决定使用哪个SQLManager

SQLManager a = SampleHelper.init(); SQLManager b = SampleHelper.init(); Map<**String**, **SQLManager**> map = new HashMap<>(); map.put("a", a); map.put("b", b); SQLManager sqlManager = new ConditionalSQLManager(a, map);

//不同对象,用不同sqlManager操作,存入不同的数据库 UserData user = new UserData(); user.setName("hello"); user.setDepartmentId(2); sqlManager.insert(user);

DepartmentData dept = new DepartmentData(); dept.setName("dept"); sqlManager.insert(dept);

使用注解 @TargetSQLManager来决定使用哪个SQLManger

@Data @Table(name = "department") @TargetSQLManager("b") public static class DepartmentData { @Auto private Integer id; private String name; }

例子10 如果想给每个sql语句增加一个sqlId标识

这样好处是方便数据库DBA与程序员沟通

public static class SqlIdAppendInterceptor implements Interceptor{ @Override public void before(InterceptorContext ctx) { ExecuteContext context = ctx.getExecuteContext(); String jdbcSql = context.sqlResult.jdbcSql; String info = context.sqlId.toString(); //为发送到数据库的sql增加一个注释说明,方便数据库dba能与开发人员沟通 jdbcSql = "/*"+info+"*/\n"+jdbcSql; context.sqlResult.jdbcSql = jdbcSql; } }

例子11 代码生成框架

可以使用内置的代码生成框架生成代码何文档,也可以自定义的,用户可自行扩展SourceBuilder类

List<**SourceBuilder**> sourceBuilder = new ArrayList<>(); SourceBuilder entityBuilder = new EntitySourceBuilder(); SourceBuilder mapperBuilder = new MapperSourceBuilder(); SourceBuilder mdBuilder = new MDSourceBuilder(); //数据库markdown文档 SourceBuilder docBuilder = new MDDocBuilder();

sourceBuilder.add(entityBuilder); sourceBuilder.add(mapperBuilder); sourceBuilder.add(mdBuilder); sourceBuilder.add(docBuilder); SourceConfig config = new SourceConfig(sqlManager,sourceBuilder); //只输出到控制台 ConsoleOnlyProject project = new ConsoleOnlyProject(); String tableName = "USER"; config.gen(tableName,project);

例子13 定义一个Beetl函数

GroupTemplate groupTemplate = groupTemplate(); groupTemplate.registerFunction("nextDay",new NextDayFunction());

Map map = new HashMap(); map.put("date",new Date()); String sql = "select * from user where create_time is not null and create_time<#{nextDay(date)}"; **List**<**UserEntity**> count = sqlManager.execute(sql,UserEntity.class,map);

nextDay函数是一个Beetl函数,非常容易定义,非常容易在sql模板语句里使用

public static class NextDayFunction implements Function {

@Override public Object call(Object[] paras, Context ctx) { Date date = (Date) paras[0]; Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.DAY_OF_YEAR, 1); // 今天+1天 return c.getTime(); } }

例子14 更多可扩展的例子

根据ID或者上下文自动分表,toTable是定义的一个Beetl函数,

static final String USER_TABLE="${toTable('user',id)}"; @Data @Table(name = USER_TABLE) public static class MyUser { @AssignID private Integer id; private String name; }

定义一个Jackson注解,@Builder是注解的注解,表示用Builder指示的类来解释执行,可以看到BeetlSQL的注解可扩展性就是来源于@Build注解

@Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.FIELD}) @Builder(JacksonConvert.class) public @interface Jackson {

}

定义一个@Tenant 放在POJO上,BeetlSQL执行时候会给SQL添加额外参数,这里同样使用了@Build注解

/** * 组合注解,给相关操作添加额外的租户信息,从而实现根据租户分表或者分库 */ @Retention(RetentionPolicy.RUNTIM@ @Target(value = {ElementType.TYPE}) @Builder(TenantContext.class) public @interface Tenant {

}

使用XML而不是JSON作为映射

@Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE}) @Builder(ProviderConfig.class) public @interface XmlMapping { String path() default ""; }

参考源码例子 PluginAnnotationSample了解如何定义自定的注解,实际上BeetlSQL有一半的注解都是通过核心注解扩展出来的

例子15 微服务事务

BeetlSQL除了集成传统的事务管理器外,也提供Saga事务支持,支持多库事务和微服务事务。 其原理是自动为每个操作提供反向操作,如insert的反向操作是deleteById,并把这些操作作为任务交给Saga—Server调度。实现了通过Kafka作为客户端(各个APP)与SagaServer 交互的媒介保证任务可靠传递并最终被系统执行。

String orderAddUrl = "http://127.0.0.1:8081/order/item/{orderId}/{userId}/{fee}"; String userBalanceUpdateUrl = "http://127.0.0.1:8082/user/fee/{orderId}/{userId}/{fee}"; .......... SagaContext sagaContext = SagaContext.sagaContextFactory.current(); try { sagaContext.start(gid); //模拟调用俩个微服务,订单和用户 rest.postForEntity(orderAddUrl, null,String.class, paras); rest.postForEntity(userBalanceUpdateUrl, null,String.class, paras); if (1 == 1) { throw new RuntimeException("模拟失败,查询saga-server 看效果"); } } catch (Exception e) { log.info("error " + e.getMessage()); log.info("start rollback " + e.getMessage()); sagaContext.rollback(); return e.getMessage(); }

以用户系统为例(源码是DemoController),userBalanceUpdateUrl对应如下扣费逻辑

@Autowired UserMapper userMapper; @Transactional(propagation= Propagation.NEVER) public void update(String orderId,String userId,Integer fee){ SagaContext sagaContext = SagaContext.sagaContextFactory.current(); try{ sagaContext.start(orderId); UserEntity user = userMapper.unique(userId); user.setBalance(user.getBalance()-fee); userMapper.updateById(user); sagaContext.commit(); }catch (Exception e){ sagaContext.rollback(); } }

这里的UserMapper实际上是SagaMapper子类(而不是BaseMapper),会为每个操作提供反向操作

public interface SagaMapper<T> { /** sega 改造的接口**/ @AutoMapper(SagaInsertAMI.class) void insert(T entity);

@AutoMapper(SagaUpdateByIdAMI.class) int updateById(T entity);

@AutoMapper(SagaDeleteByIdAMI.class) int deleteById(Object key); }

BeetlSQL的架构

BeetlSQL 3.3.0 发布,新年快乐

除了SQLManager和ClassAnnotations,任何一部分都可以扩展

点赞
收藏
评论区
推荐文章
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
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年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这