SkillAgentSearch skills...

JSqlBox

jSqlBox is a Java ORM Tool

Install / Use

/learn @drinkjava2/JSqlBox
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="left"> <a href="README_ENG.md"> English instructions please see "README_ENG.md" </a> </p> <p align="center"> <a href="https://github.com/drinkjava2/jsqlbox"> <img alt="jsqlbox-logo" src="jsqlbox-logo.png"> </a> </p> <p align="center"> Java全功能数据库持久层工具 </p> <p align="center"> <a href="http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.drinkjava2%22%20AND%20a%3A%22jsqlbox%22"> <img alt="maven" src="https://img.shields.io/maven-central/v/com.github.drinkjava2/jsqlbox.svg?style=flat-square"> </a> <a href="https://www.apache.org/licenses/LICENSE-2.0"> <img alt="code style" src="https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square"> </a> </p>

简介 | Intro

jSqlBox是一个全功能开源Java数据库持久层工具,在架构、功能、易用性等方面都不输于其它持久层工具,可以说,只要是与数据库操作相关的功能,jSqlBox都已具备,如DDL操作、分页、分库分表、声明式事务、分布式事务、关联映射查询等,所有这些功能都包含在一个1M大小的jar包中,不依赖任何第三方库。

文档 | Documentation

中文 | English | JavaDoc | PDF

jSqlBox与其它持久层工具对比

请见与其它DAO工具对比, 可以对jSqlBox的功能与特点有一个大概的了解。

主要特点 | Advantages

配置简单,没有依赖任何第三方库

在pom.xml中加入以下依赖即可使用。 如果需要查看或修改源码,甚至可以直接将jSqlBox的源码拷到项目目录里就可以直接使用了。

<dependency>
   <groupId>com.github.drinkjava2</groupId>
   <artifactId>jsqlbox</artifactId>  
   <version>5.0.15.jre8</version> <!-- 或最新版 -->
</dependency> 

直接在Java里写SQL

jSqlBox的最大特点是直接在Java里SQL,它的整个架构都是建立在这个基础上,这是它与其它DAO工具最大的区别。在Java里直接写SQL的最大优点是学习成本低,只要会SQL就可以立即上手使用,降低了学习难度。使用jSqlBox并不表示要使用它的全部功能,很多时候使用SQL就能处理业务,没必要引入复杂的ORM。

DbContext db= new DbContext(dataSource);
db.exe("insert into users (", //
          " name ,", par("Sam"), //一个参数写一行,方便维护
          notNull("address,", user.getAddress()), //空值判断
	  when(age>10, "age,", par(user.getAge())), //根据条件动态添加SQL片段
          " address ", par("Canada"), //
          ") ", valuesQuestions()); //自动根据参数个数补上 values(?,?...?)片段

在Java里写SQL谁都会,但是象jSqlBox这样做到极致的DAO工具并不多,个人认为jSqlBox这种方式是最能发挥原生SQL的威力,同时也是易学性、灵活性、可护展性最好的SQL写法。

借助字符串常量或Q类,可以写出支持重构的SQL

 QTbPriceSetting p=QTbPriceSetting.instance;
 DB.exe("insert into ",p," (", //
	p.id, ",", par(1200), //
	p.price, ",", par(80), //
	p.currency, ",", par("USD"), //
	p.created_at, par("2019-09-17 04:07:55"), //
	")", valuesQuestions());

Q类或包含字符串常量的实体类源码,可以使用jSqlBox的源码生成功能,从数据库直接生成,另外jSqlBox也支持从实体类或数据库中导出Excel格式的数据库表结构,详见jDialect的实体结构或数据库结构导出到Excel

架构合理,模块式架构,各个子模块(jBeanBox,jDbPro,jDialects,jTransaction)都可以脱离jSqlBox单独存在。

jSqlBox是源码包含模块式架构,目的是隔离功能点,并分享给其它工具重用。例如只想使用jDialcet数据库方言模块,可以在项目的pom.xml中加入:

<dependency>
    <groupId>com.github.drinkjava2</groupId>
    <artifactId>jdialects</artifactId>
    <version>5.0.13.jre8</version> <!--或最新版-->
</dependency>

基于jDialects模块,支持80多种数据库的分页、DDl脚本生成、从数据库生成实体源码、函数变换、主键生成等功能。

例如下面的SQL语句,就可以在多种数据库上使用而不需要更改源码,jSqlBox的jDialects模块自动处理与方言相关的DDL生成、分页、函数翻译。

DB.exe(DB.gctx().toCreateDDL(HelloWorld.class)); //根据实体生成DDL,创建数据库表
String sql=DB.trans("select concat('a','b','c'), current_time() from user_tb where age>0"); //根据方言对SQL函数翻译
DB.qryString(sql, " and price>100", pagin(1, 10));  //任意数据库分页只需要传入一个pagin对象

简洁的ActiveRecord模式

继承ActiveRRecord类,或只需要声明实现ActiveEntity接口,就可以实现ActiveRecord模式了:

public class User implements ActiveEntity{
 @UUID
 private String id;
 private String name;
 //getter &setter .....
}

//ActiveRecord模式
new User().loadById("张三").setUserAge(13).update(); 

强大的参数式设计。 拦截器、分页、模板、缓存、实体映射器等都可以当作参数拼接到SQL方法里去

例如SQL模板引擎可以当作参数传到SQL里,这样jSqlBox就具备了支持并以扩充各种SQL模板的功能:

SqlTemplateEngine TEMPLATE = BasicSqlTemplate.instance()
ctx2.exe(TEMPLATE, "update users set name=#{user.name}, address=:user.address", bind("user", tom));

再例如下面的SQL,只会执行一次,因为参数中有一个缓存拦截器,重复的SQL不再执行而是直接从缓存中取结果:

SimpleCacheHandler cache = new SimpleCacheHandler();
for (int i = 0; i < 10; i++)
   DB.qry(cache, new EntityListHandler(), DemoUser.class, "select u.* from DemoUser u where u.age>?", par(10));

为Java8开发环境提供多行文本支持,方便利用IDE快速定位到多行SQL文本上

public static class InsertDemoSQL extends Text {
/*-  
insert into demo
      (id, name) 
values( ?,  ?)
*/
}
//使用:
DB.exe(InsertDemoSQL.class, par("1", "Foo"));

实体关联查询

利用SQL的多表关联查询可以用一条SQL返回关联的对象结构。例如下例可以一次无递归查询树节点并装配成内存中对象树,其中的EntityNetHandler、alias, give等方法都是与实体关联映射相关的SQL参数:

Object[] targets = new Object[] { new EntityNetHandler(), TreeNode.class, TreeNode.class,
		alias("t", "p"), give("p", "t", "parent"), give("t", "p", "childs") };
EntityNet net = ctx.qry(targets, //深度树的海底捞算法
  "select t.**, t.pid as p_id from treenodetb t where t.line>=? and ",
      "t.line< (select min(line) from treenodetb where line>? and lvl<=?) ", par(line, line, lvl));
TreeNode node = net.pickOneEntity("t", d.getId());

不同于Hibernate和MyBatis复杂的配置,在jSqlBox中,实体关联查询只不过是一种参数略微复杂的SQL而已,随用随拼,不需要配置。

jSqlBox的主从表查询

jSqlBox的主从表查询是5.0.15版起新增的功能,将参数内嵌式SQL查询写成树状,即可实现类似GraphQL的结构化查询,输入和输出的树状结构一致,所见即所得。其优点有:
1.只需要编写针对单表查询的SQL,会自动按主从关联列名生成类似“id in (?, ?...?)”的SQL片段,并将最终查询结果组装成主从表树状结构。
2.采用纯Java和原生SQL,功能强,学习成本低。
3.没有直接输出为JSON,而是输出Map/List对象或Java实体对象,查询结果可以在后端继续修改。
4.可以直接利用Java的IDE格式化和语法检查功能,不需要第三方工具。
5.jSqlBox的内嵌式SQL参数、分页、分库分表、拦截器、事务等依然可以直接使用。
6.没有涉及安全、权限功能,无学习成本。安全、权限这些不属于ORM工具的职能,而应由后端的SpringSecurity/Shiro工具包或独立的Serverless/JsonAPI服务来提供。
7.如果结合我写的MyServerless项目使用,可以实现前端直接在html里书写Java、定制主从表多级查询并返回json, 实现类似GraphQL的功能,将业务逻辑前移到前端。
8.性能好,用"in"的方式进行数据库表的关联查询,不存在1+N问题。
9.源码简洁(实现这个功能仅用了300行源码,见GraphQuery.java)
使用示例:

        GraphQuery q1 = //
                $("addresstb as addresses", "where id>", que("a1"), " and id<", que("a5"), pagin(1, 10), //
                        $1("usertb", key("user"), ms("userId", "id"), $("userroletb as userRoleList", ms("id", "userId"), //
                                $("roletb as roleList", ms("rid", "id"), // ms方法也可以写成DB.masterSlave(),它的参数是主、从表的键名
                                        $("roleprivilegetb as rolePrivilegeList", ms("id", "rid"), //
                                                $1("privilegetb as privilege", ms("pid", "id")) //                                                 
                                        )//
                                )//
                        ), //
                                $1("select * from emailtb as email", ms("id", "userId"), one), //
                                $("addresstb as addressList", ms("id", "userId"), "and addressName like ?", par("addr%"))//
                        )//
                );
        GraphQuery q2 = //
                $("usertb as u", "where id>", que("u2"), pagin(1, 10), entity(User.class), //
                        $1("emailtb as emailMap", ms("id", "userId"), one), //
                        $("addresstb as addressList", ms("id", "userId"))//
                );
        Object result = DB.graphQuery(q1, q2); //result是查询结果
        String json = JsonUtil.toJSONFormatted(result); //输出为JSON文本

更多关于实体主从表查询的使用请详见用户手册。以上示例详见单元测试下的GraphQueryTest.java,示例结果输出如下:

{
   "addresses":[
      {
         "addressName":"address2",
         "id":"a2",
         "userId":"u2",
         "user":{
            "id":"u2",
            "userName":"user2",
            "userRoleList":[
               {
                  "id":"3i6yaxy2fusjkgisyfhypkti9",
                  "rid":"r1",
                  "userId":"u2",
                  "roleList":[
                     {
                        "id":"r1",
                        "roleName":"role1",
                        "rolePrivilegeList":[
                           {
                              "id":"b484ze4k44xemtkstehnprhxq",
                              "pid":"p1",
                              "rid":"r1",
                              "privilege":{
                                 "id":"p1",
                                 "privilegeName":"privilege1"
                              }
                           }
                        ]
                     }
                  ]
               },
            ......
      }
   ],
   "u":[
      {
         "id":"u3",
         "userName":"user3",
         "addressList":[
            {
               "addressName":"address3",
               "id":"a3",
               "userId":"u3"
            }
         ],
         "emailMap":{
            "emailName":"email5",
            "id":"e5",
            "userId":"u3"
         }
      },
      {
         "id":"u5",
         "userName":"user5",
         "addressList":[
            {
               "addressName":"address5",
               "id":"a5",
               "userId":"u5"
            }
         ]
      }
   ]
}

兼容主要JPA注解,支持在运行期动态更改配置

为了方便学习,jSqlBox兼容JPA实体类的以下主要注解:

@Entity, @Transient, @UniqueConstraint, @GenerationType, @Id, @Index, @SequenceGenerator, 
@GeneratedValue, @Table, @Column, @TableGenerator, @Version, @Enumerated, @Convert, @Temporal

jSql自带一些特殊实体注解如CreatedBy、LastModifiedBy、ShardTable、ShardDatabase、Snowflake等。 jSqlBox在运行期可动态更改实体关联配置,例如下面在运行期给一个pojo类动态配置UUID32主键,并更改它的name字段映射到address字段上:

TableModel m = TableModelUtils.entity2Model(PojoDemo.class);
m.column("id").pkey().uuid32();
m.column("name").setColumnName("address");
TableModelUtils.bindGlobalModel(PojoDemo.class, m);

jSqlBox自带多租户、主从、分库分表功能

jSqlBox的主从和分库分表功能除了默认的用实体Sharding注解操作外,还支持将分库分表方法作为参数直接传到SQL中使用,精准控制每一条SQL的分库分表:

//实体的分库分表
for (i:=0;i<100;i++)
   new TheUser().put("databaseId", i).insert(); 
   
//SQL中的分库分表   
db[2].exe(User.class, "insert into ", shardTB(tbID), shardDB(dbID)," (id, name, databaseId) 
          values(?,?,?)", par(tbID, "u1", dbID

Related Skills

View on GitHub
GitHub Stars60
CategoryData
Updated10mo ago
Forks15

Languages

Java

Security Score

87/100

Audited on Jun 2, 2025

No findings