指南

介绍

Unabo 是函数式编程风格的 ORM框架。

  • 你可以将 POJOopen in new window 与数据库表建立映射关系,或SQL与代码混用(如基础查询用sql,条件拼接用代码)。
  • 你可以只使用Unabo的一部分功能,按需深入,最终使得随心所欲的操作数据库。
  • Unabo 既可以在Java客户端程序使用,也可以在Web应用中使用。

快速上手

访问 mavenopen in new window 中央仓库获取最新版本

<dependency>
    <groupId>online.sanen</groupId>
    <artifactId>unabo</artifactId>
    <version>${The Laster Version}</version>
</dependency>

依赖环境

  • Java 8+

  • 数据库驱动

  • 连接池 (可选,默认为 dbcpopen in new window

  • Spring-data-jdbc (可选,开启spring事务)

  • 支持的数据库

数据库名称已添加依赖驱动版本号默认数据库
Sqlitetrue3.39.2.0true
Mysqlfalse8.0.26
Oraclefalse11.2.0.4
SqlServerfalsejtds-1.3.1
PostgreSQLfalse42.2.5
damengfalseDmJdbcDriver18-8.1.2.79
Mongodbfalse4.7.0
  • 支持的连接池
连接池名称已添加依赖版本号默认连接池
Dbcptrue2.8.0true
C3p0false0.9.5.5
Druidfalse1.2.6
HikariCPfalse4.0.3

区分包

除了关系型数据库以外,Unabo还用相似接口做了NoSQL适配,因无法让SQL与NoSQL两者接口完全兼容,顾以包名区分。 如:使用Bootstrap时会出现2个import提示 img.png

这种选择会很多,不过也无需困扰,只需根据当前数据库类型来判断,如使用的 Mongodb时,尽量选择 nosql 的包即可。

示例

创建Bootstrap 实例

TIP

Bootstrap为Unabo中所有操作(增删改查)的引导类

//创建引导器
Bootstrap bootstrap = Unabo.load("test",configuration -> {
   configuration.setUrl("jdbc:mysql://127.0.0.1:3306/test");
   configuration.setDriverOption(DriverOption.MYSQL_CJ);
   configuration.setUsername("test");
   configuration.setPassword("123456");
});

//控制台打印当前连接下所有表名
  Print.info(bootstrap.dataInformation().getTablesAndViews());
import
import online.sanen.unabo.api.Bootstrap;
import online.sanen.unabo.api.structure.enums.DriverOption;
import online.sanen.unabo.sql.factory.Unabo;

创建user表

Map<String,Object> map = new HashedMap<>();
map.put("id",1);
map.put("name","张三");
map.put("age",18);

bootstrap.queryMap("user",map).setPrimary("id").create();
  • queryMap - 操作Map类型数据(包括子类)
  • setPrimary - 设置主键,数据库支持的情况下默认自增长
控制台
[09:10:53.1053][MYSQL :sys][main][MYSQL]
CREATE TABLE `user` (`age` integer,`name` text ,`id` integer NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`))
----------------------------------------------------------------------------------------------
Time: 0.085s 

插入数据

bootstrap.queryMap("user",map).insert();
控制台
[09:28:53.2853][MYSQL :sys][main][MYSQL]
INSERT INTO `user` (`age`,`name`,`id`) values (18,'张三',1)
----------------------------------------------------------------------------------------------
Time: 0.128s 

批量插入

准备数据

        Map<String,Object> map1 = new HashedMap<>();
        map1.put("name","李四");
        map1.put("age",18);

        Map<String,Object> map2 = new HashedMap<>();
        map2.put("name","王五");
        map2.put("age",20);
        
         List<User> users = Arrays.asList(new User(), new User(), new User()...);

批量插入接收一个Map集合

 bootstrap.queryMap("user", Arrays.asList(map1,map2)).insert();

批量插入接收一个实体类集合

TIP

Unabo初衷是一个ORM框架,本是queryEntry的方法名(早期版本),也简化为query。 参数可以是 集合 也可以是 一个对象

bootstrap.query(users).insert();

控制台
[09:36:28.3628][MYSQL :sys][main][MYSQL]
INSERT INTO `user` (`age`,`name`) values (?,?)
----------------------------------------------------------------------------------------------
Time: 0.125s 

批量操作控制台不会打印可执行的sql

列表查询

Map集合接收

List<Map<String, Object>> users = bootstrap.queryTable("user").maps();

实体类接收

//import lombok.Data;
//import online.sanen.unabo.template.jpa.Id;
//import online.sanen.unabo.template.jpa.Table;

    @Data
    @Table(name="user")
    public static class User {
        @Id
        Integer id;
        String name;
        Integer age;
    }
List<User> users = bootstrap.queryTable("user").list(User.class);
控制台
[09:48:32.4832][MYSQL :sys][main][MYSQL]
SELECT `id`,`name`,`age` FROM `user`
----------------------------------------------------------------------------------------------
Time: 0.12s 

条件查询

主键查询

//import online.sanen.unabo.api.condition.C;

Optional<Map<String,Object>> optional = bootstrap.queryTable("user")
           .addCondition(C.eq("id", 1))
           .unique();
    
System.out.println(optional.get());



 



控制台
[10:21:20.2120][MYSQL :sys][main][MYSQL]
SELECT `age`,`name`,`id` FROM `user` WHERE (`id`=1)
----------------------------------------------------------------------------------------------
Time: 0.119s

{age=18, name=张三, id=1}

多个条件链式调用

        bootstrap.queryTable("user")
                .addCondition(C.gt("id", 1))
                .addCondition(C.between("age",18,20))
                .list();

 
 

控制台
[10:44:18.4418][MYSQL :sys][main][MYSQL]
SELECT `age`,`name`,`id` FROM `user` WHERE (`id`>1) AND (`age` between 18 AND 20)
----------------------------------------------------------------------------------------------
Time: 0.107s

更多使用 参考>Condition

修改数据

Optional<User> optional = bootstrap.queryTable("user")
    .addCondition(C.eq("id", 1))
    .unique(User.class);
    
if (optional.isPresent()) {
    User user = optional.get();
    user.setName("张三丰");
    bootstrap.query(user).setFields("name").update();
}







 

控制台
[10:51:14.5114][MYSQL :sys][main][MYSQL]
SELECT `id`,`name`,`age` FROM `user` WHERE (`id`=1)
----------------------------------------------------------------------------------------------
Time: 0.12s


[10:51:14.5114][MYSQL :sys][main][MYSQL]
UPDATE `user` SET `name`='张三丰' WHERE (`id`=1)
----------------------------------------------------------------------------------------------
Time: 0.012s
删除数据
        bootstrap.queryTable("user")
                .addCondition(C.eq("id",1))
                .delete();


 
控制台
[10:57:24.5724][MYSQL :sys][main][MYSQL]
DELETE FROM `user` WHERE (`id`=1)
----------------------------------------------------------------------------------------------
Time: 0.073s

删除表

bootstrap.queryTable("user").drop();
控制台
[10:59:29.5929][MYSQL :sys][main][MYSQL]
DROP TABLE `user`
----------------------------------------------------------------------------------------------
Time: 0.078s 

现在,你应该掌握了 Unabo 的基本使用。接下来,了解一下 Unabo 配置 相关的内容。

配置

Bootstrap

id

  • 类型: Object
  • 默认值: null
  • 详情

实例唯一值,如果id为null则Unabo不会缓存该实例。

  • 参考:

容器>通过ID获取

url

  • 类型: String
  • 默认值: ""
  • 详情

数据库的地址,SQL型数据库必填项。

  • 参考

创建 Bootstrap 实例

driver

  • 类型: String | Enum
  • 默认值: ""
  • 详情
  • 参考

DriverOption

username

  • 类型: String
  • 默认值: ""
  • 详情

用户名,非必填,如SQLite | MongoDB 数据库

password

  • 类型: String
  • 默认值: ""
  • 详情

用户校验密码,非必填,如SQLite | MongoDB 数据库

isShowSql

  • 类型: Boolean
  • 默认值: true
  • 详情

控制台是否打印SQL,可选值 true | false

isShowSqlSupplier

  • 类型: Supplier<Boolean>
  • 默认值: null
  • 详情

控制台是否打印SQL,优先级比 isShowSql

format

  • 类型: Boolean
  • 默认值: false
  • 详情

控制台打印SQL是否格式化

validationQuery

  • 类型: String
  • 默认值: 依据数据库类型动态生成
  • 详情

用来验证数据库连接的查询语句,这个查询语句必须是至少返回一条数据的SELECT语句

  • 参考
DataBasevalidationQuery
Oracleselect 1 from dual
MySqlselect 1
Microsoft SqlServerselect1
postgresqlselect version()

datasourceType

  • 类型: datasourceType
  • 默认值: Dbcp
  • 详情

数据库连接池

-参考

DatasourceType

isCache

  • 类型: Boolean
  • 默认值: false
  • 详情

Unabo内置缓存,建议除只读模式下不要开启。

maxActive

  • 类型: Integer
  • 默认值: 5
  • 详情

数据库连接池最大连接数

isRemoveAbandoned

  • 类型: Boolean
  • 默认值: true
  • 详情

移除被遗弃的connection,需要连接池支持

mapper_locations

  • 类型: String
  • 默认值: null
  • 详情

用于SQL分离模式,指定mapper路径 (类似于 Mybatis的 XML mapper)

transactionFactory

  • 类型: TransactionFactoryEnum
  • 默认值: null
  • 详情

指定事务工厂。默认不会开启事务

连接池

通常Bootstrap的连接池设置项已经足够,如需深度配置,可通过 Manager 接口获取连接池实例

 Bootstrap bootstrap = Unabo.load(configuration -> {
    configuration.setDatasourceType(Configuration.DatasourceType.HikariCP);
 });


HikariDataSource hikariDataSource = (HikariDataSource) bootstrap.manager()
    .getTemplate()
    .getDataSource();







 

WARNING

注意强转获取的连接池实例要跟配置对应 如 DataSouseType.HikariCP -> HikariDataSource

Spring 扩展

Unabo 在 SpringBoot 项目中可以在 application.yml 文件配置。

unabo:
  enable: true
  nosql-instances:
  - id: bootstrap
    ip: 127.0.0.1
    port: 27017
    schema: etl
    show-log: false

项目启动之后,unabo首先将实例注入自己的容器当中,同时也会注入到 SpringBeanFactory

TIP

实例 id 同时也是 SpringBeanFactory 的 name, 这在多数据源下很有用。

如:- id: bsp1 - id: bsp2 的 Bean 获取为:

@Resource //@Autowired
Bootstrap bsp1;

@Resource //@Autowired
Bootstrap bsp2;

对象映射

建立实体类与库表之间的关系,通过online.sanen.unabo.template.jpa包下注解绑定。

@BootstrapId 注解

  • 作用 指明对象操作由哪个 Bootstrap 实例执行,多数据源下搭配 Behaviror 接口使用
  • 属性 value
  • 示例
@BootstrapId("bootstrap")
public class Connect implements Behavior<Connect> {
    ...
}
 



@Table 注解

  • 作用 指定表名与schema
  • 属性
nametypedefaultexplain
nameString-对应表名
schemaString-库/命名空间/用户域, 如schema不为空,执行的最终表名为:
schema.name
  • 示例
//SELECT * FROM connect
@Table(name = "connect")
@Data
public class Connect implements Behavior<Connect> {


//SELECT * FROM  test.connect
@Table(name = "connect",schema="test")
@Data
public class Connect implements Behavior<Connect> {
 
 




 
 


@Column

  • 作用 对应字段名
  • 属性
nametypedefaultexplain
nameString-对应字段名
jsonSerializationbooleanfalse修改/插入 操作是否对字段json序列化
  • 示例
@Column(name="driver_option",jsonSerialization = false)
String driverOption; 
 

@Id

  • 作用 指定主键,每个类只可以存在一个被Id修饰的字段
  • 示例
public class Connect implements Behavior<Connect> {
    @Id
	String id;
	...
}

 
 


@NoDB

  • 作用 字段修饰,让该字段不参与任何操作

@NoInsert

  • 作用 字段修饰,让该字段不参与插入

@NoUpdate

  • 作用 字段修饰,让该字段不参与修改

@Priority

  • 作用 类修饰,当库表字段与实体类不一致时,字段结果以实体类为准 (默认:表的优先级更高)。 如实体类有 name,age 字段,表里有 id、name、age、sex 字段,被 @Priority 修饰的类接收查询结果不会因为字段不一致而报错。

多数据源

创建多数据源只需要保证 Bootstrap Id 不同即可。

  • 手动创建
Unabo.load("instance1",configuration -> {
			...
});

Unabo.load("instance2",configuration -> {
			...
});

Unabo.load("instance3",configuration -> {
			...
});
  • application.yml 配置文件
unabo:
  enable: true
  nosql-instances:
  - id: bootstrap
    ip: 127.0.0.1
    port: 27017
    schema: etl
    show-log: false
  - id: csv
    ip: 127.0.0.1
    port: 27017
    schema: csv
    show-log: false
  - id: fileData
    ip: 127.0.0.1
    port: 27017
    schema: fileData
    show-log: false
  - id: middleware
    ip: 127.0.0.1
    port: 27017
    schema: middleware
    show-log: false



 




 




 




 




TIP

Unabo.load 会缓存每个带Id的实例,同样的 configuration 只会加载一次,重复加载只会返回已缓存的,因此不用担心重复创建

  • Behavior 接口
@BootstrapId("bootstrap")
@Table(name = "connect")
@Data
public class Connect implements Behavior<Connect> {
    ...
}
 





多数据源下使用 Behavior 接口必须为实体类指定实例,否则会抛出 RuntimeException

参考:

生命周期

Unabo 容器作用为管理 Bootstrap 生命周期,从创建到销毁都在容器中操作,又分为 sqlnosql

online.sanen.unabo.sql.Container sqlContainer = Unabo.sql;
online.sanen.unabo.nosql.Container nosqlContainer = Unabo.nosql;

创建

id为sys的实例

TIP

虽然 sql/nosql 容器中的id互不冲突,但尽量不要取一样的id

完整的创建示例 参考 > 创建实例

Unabo.sql.load("sys",configuration -> {
    ...
});

//或

//Unabo.nosql.load("sys",configuration -> {
//    ...
//});

通过Id获取

Bootstrap bootstrap = Unabo.sql.get("sys");
//或
//Bootstrap bootstrap = Unabo.nosql.get("sys");

释放资源

弃用的Bootstrap实例会占用数据库服务资源,即使从容器中删除了实例,因为连接池没有释放还是会造成资源占用。因此在恰当的时机资源释放是有必要的,特别是一次性使用。

  • try-with-resources 方式自动释放
try(Bootstrap bootstrap = 创建实例代码){
    //Todo something...
} 

TIP

有些业务场景下数据连接是动态获取或只会使用一次,这时建议创建的时候不要给Id (没有 id 就不会缓存实例,用完只需释放资源即可)

  • 手动释放
bootstrap.close() 

销毁

Unabo.sql.remove("sys");
//Unabo.sql.remove("sys");

事务

Spring事务工厂

TIP

Spring事务只支持单数据源回滚,多数据下请使用 JDBC事务手动控制

  • 步骤1: 引入依赖 spring-data-jdbc
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jdbc</artifactId>
</dependency> 
  • 步骤2: 在入口类使用注解 @EnableTransactionManagement 开启事务

  • 步骤3: 启动注解属性忽略spring默认连接池配置

    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

  • 步骤4: application.yml 中配置事务工厂 transaction

unabo:
  enable: true
  sql-instances:
  - id: sys
    driver-option: mysql-cj
    datasource-type: hikaricp
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: test
    password: 123456
    transaction: SpringManagedTransactionFactory









 
  • 步骤5: 需要事务的方法添加 @Transactional 注解

JDBC事务工厂

  • 步骤1: 设置事务工厂
Bootstrap bootstrap = Unabo.load("sys", configuration -> {
     ...
    configuration.setTransactionFactory(Configuration.TransactionFactoryEnum.JdbcTransactionFactory);
 });


 

unabo:
 enable: true
 sql-instances:
 - id: sqlInstance1
   ...
   transaction: JdbcTransactionFactory





 
  • 步骤2: 代码中开启会话

使用Unabo封装的openSession方法 1.1.7.2以上版本

 Unabo.openSession(()->{
    //此处任意数据库操作,发生异常将自动回滚
    bootstrap.query(user).insert();
    bootstrap2.query(user2).insert();
    
},bootstrap,bootstrap2);

手动开启会话及回滚

try{
        bootstrap.openSession();
           
        bootstrap.query(user).insert();
        bootstrap.commit();
         
    }catch (Exception e){
       try{
           bootstrap.rollback();
       }catch (SQLException e1){
             
       }
    }

 


 



 




MongoDB事务工厂

MongoDB事务需集群配置,开发环境至少支持单副本节点

配置事务工厂

unabo:
  enable: true
  nosql-instances:
    - id: bootstrap
      ip: 127.0.0.1
      port: 27017
      username: root
      password: 123456
      schema: nocode
      transaction-factory: MongoDBTransactionFactory
      show-log: false









 

在代码中使用openSession方法开启事务,异常自动回滚

 bootstrap.openSession(() -> {
    ...
 });

如想捕获异常可在外部try catch,并不会影响到事务回退

try{
   bootstrap.openSession(() -> {
      ...
   });
}catch(Exception e){
   e.printStackTrace();
}

分页

Unabo 对不同数据库抽象封装了 limit 方法,搭配 Page 对象实现分页,示例只是提供思路,仅供参考:

//import com.mhdt.net.RequestResult;
//import com.mhdt.structure.Page;

@GetMapping("list")
public RequestResult list(Integer current_page,Integer page_size) {

    return RequestResult.perform(response -> {
        //初始化Page对象
	    Page page = new Page(current_page, page_size);
	    QueryTable queryTable = bootstrap.queryTable(ProjectMerchants.class);
	    
	    //查询数量,设置数量后 Page对象会自动计算 limit 起始位置
	    response.put("page", page.setCount(queryTable.count()));
    	response.put("list",queryTable
    	    .limit(page.getStartIndex(), page_size).sort(Sorts.DESC, "id")
    	    .entities(ProjectMerchants.class));
	});
}