BeetlSQL 3.0.6 released, Java database access framework

This release fixes a few bugs

  • Fix SQLReady page turning bug fix
  • bug that upsertByTemplate should return false instead of int
<dependency>
  <groupId>com.ibeetl</groupId>
  <artifactId>beetlsql</artifactId>
  <version>3.0.6-RELEASE</version>
</dependency>

The goal of BeetlSQL is to provide a database access framework that is efficient in development, efficient in maintenance, and efficient in operation, and provides a consistent way of writing code in the case of multiple libraries in a system. Support the following data platforms

  • Traditional database: MySQL, MariaDB, Oralce, Postgres, DB2, SQL Server, H2, SQLite, Derby, Magic Power, Dream, Huawei Goss, Renda Jincang, PolarDB, etc.
  • Big data: HBase, ClickHouse, Cassandar, Hive
  • IoT time series database: Machbase, TD-Engine, IotDB
  • SQL query engine: Drill, Presto, Druid
  • In-memory database: ignite, CouchBase

Read the documentation  source code and examples

BeetlSQL3 performance test

The test dimension is ops/ms, the number of calls per millisecond

Benchmark                         Mode  Cnt     Score     Error   Units
JMHMain.beetlsqlComplexMapping   thrpt    5   212.378 ±  26.222  ops/ms
JMHMain.beetlsqlExecuteJdbc      thrpt    5   428.713 ±  66.192  ops/ms
JMHMain.beetlsqlExecuteTemplate  thrpt    5   374.943 ±  20.214  ops/ms
JMHMain.beetlsqlFile             thrpt    5   433.001 ±  65.448  ops/ms
JMHMain.beetlsqlInsert           thrpt    5   236.244 ± 112.102  ops/ms
JMHMain.beetlsqlLambdaQuery      thrpt    5   247.289 ±  19.310  ops/ms
JMHMain.beetlsqlOne2Many         thrpt    5   108.132 ±  10.934  ops/ms
JMHMain.beetlsqlPageQuery        thrpt    5   203.751 ±   9.395  ops/ms
JMHMain.beetlsqlSelectById       thrpt    5   393.437 ±  15.685  ops/ms
JMHMain.jdbcExecuteJdbc          thrpt    5  1083.310 ±  80.947  ops/ms
JMHMain.jdbcInsert               thrpt    5   308.341 ± 231.163  ops/ms
JMHMain.jdbcSelectById           thrpt    5  1019.370 ±  92.946  ops/ms
JMHMain.jpaExecuteJdbc           thrpt    5    94.600 ±  15.624  ops/ms
JMHMain.jpaExecuteTemplate       thrpt    5   133.017 ±  12.954  ops/ms
JMHMain.jpaInsert                thrpt    5    81.232 ±  26.971  ops/ms
JMHMain.jpaOne2Many              thrpt    5   101.506 ±  11.301  ops/ms
JMHMain.jpaPageQuery             thrpt    5   117.748 ±   4.512  ops/ms
JMHMain.jpaSelectById            thrpt    5   335.945 ±  27.186  ops/ms
JMHMain.mybatisComplexMapping    thrpt    5   102.402 ±  11.129  ops/ms
JMHMain.mybatisExecuteTemplate   thrpt    5   202.619 ±  16.978  ops/ms
JMHMain.mybatisFile              thrpt    5   151.151 ±   4.251  ops/ms
JMHMain.mybatisInsert            thrpt    5   141.469 ±  43.092  ops/ms
JMHMain.mybatisLambdaQuery       thrpt    5    15.558 ±   1.481  ops/ms
JMHMain.mybatisPageQuery         thrpt    5    63.705 ±   7.592  ops/ms
JMHMain.mybatisSelectById        thrpt    5   197.130 ±  19.461  ops/ms
JMHMain.weedExecuteJdbc          thrpt    5   416.941 ±  22.256  ops/ms
JMHMain.weedExecuteTemplate      thrpt    5   439.266 ±  57.130  ops/ms
JMHMain.weedFile                 thrpt    5   477.561 ±  37.926  ops/ms
JMHMain.weedInsert               thrpt    5   231.444 ±  92.598  ops/ms
JMHMain.weedLambdaQuery          thrpt    5   422.707 ±  64.716  ops/ms
JMHMain.weedPageQuery            thrpt    5   246.018 ±  18.724  ops/ms
JMHMain.weedSelectById           thrpt    5   380.348 ±  20.968  ops/ms

Performance test code

Code example

Example 1, built-in method, no need to write SQL to complete common operations

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);

The output log is friendly, and the called code can be located backward

┏━━━━━ 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] ━━━

Example 2 Using 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);

Example 3 Using template 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);

Example 4 Using the Query class

Support refactoring

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

Example 5 Put dozens of lines of SQL into sql file for maintenance

//访问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);

Example 6 Complex mapping support

Supports complex mapping like mybatis

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

  • Configuration mapping, easier to understand than MyBatis, more detailed error information
{
	"id": "id",
	"name": "name",
	"dept": {
		"id": "dept_id",
		"name": "dept_name"
	},
	"roles": {
		"id": "r_id",
		"name": "r_name"
	}
}

Example 7 It is best to use mapper as a database access class

@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);
}

The annotations you see on Mapper can be customized and extended by yourself

Example 8 Using Fetch annotation

You can get related objects again according to the Fetch annotations after querying. In fact, @FetchOne and @FetchMany are customized and users can extend them by themselves

    @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;
    }

Example 9 Switching between different databases

You can extend the decide method of ConditionalSQLManager to decide which SQLManager to use

        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);

Use the annotation @TargetSQLManager to decide which SQLManger to use

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

Example 10 If you want to add a sqlId identifier to each sql statement

This benefit is to facilitate communication between the database DBA and the programmer

 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;
        }
 }

Example 11 Code generation framework

You can use the built-in code generation framework to generate code and documentation, or you can customize it. Users can extend the SourceBuilder class by themselves

	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);

Example 13 Define a Beetl function

        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);

The nextDay function is a Beetl function, very easy to define, very easy to use in the SQL template statement

   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();
        }
    }

Example 14 More extensible examples

Automatically divide tables according to ID or context, toTable is a Beetl function defined,

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

Define a Jackson annotation, @Builder is the annotation of the annotation, which means that the class indicated by the Builder is used to explain the execution. You can see that the extensibility of BeetlSQL's annotations is derived from the @Build annotation

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

}

Define a @Tenant and place it on the POJO. When BeetlSQL is executed, additional parameters will be added to the SQL. The @Build annotation is also used here.


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

}

Use XML instead of JSON as the mapping

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

Refer to the source code example PluginAnnotationSample to learn how to define custom annotations. In fact, half of BeetlSQL's annotations are extended through core annotations

BeetlSQL architecture

Except SQLManager and ClassAnnoations, any part can be extended

Guess you like

Origin www.oschina.net/news/119368/beetlsql-3-0-6-released