指南
介绍
Unabo 是函数式编程风格的 ORM框架。
- 你可以将 POJO 与数据库表建立映射关系,或SQL与代码混用(如基础查询用sql,条件拼接用代码)。
- 你可以只使用Unabo的一部分功能,按需深入,最终使得随心所欲的操作数据库。
- Unabo 既可以在Java客户端程序使用,也可以在Web应用中使用。
快速上手
访问 maven 中央仓库获取最新版本
<dependency>
<groupId>online.sanen</groupId>
<artifactId>unabo</artifactId>
<version>${The Laster Version}</version>
</dependency>
依赖环境
Java 8+
数据库驱动
连接池 (可选,默认为 dbcp)
Spring-data-jdbc (可选,开启spring事务)
支持的数据库
数据库名称 | 已添加依赖 | 驱动版本号 | 默认数据库 |
---|---|---|---|
Sqlite | true | 3.39.2.0 | true |
Mysql | false | 8.0.26 | |
Oracle | false | 11.2.0.4 | |
SqlServer | false | jtds-1.3.1 | |
PostgreSQL | false | 42.2.5 | |
dameng | false | DmJdbcDriver18-8.1.2.79 | |
Mongodb | false | 4.7.0 |
- 支持的连接池
连接池名称 | 已添加依赖 | 版本号 | 默认连接池 |
---|---|---|---|
Dbcp | true | 2.8.0 | true |
C3p0 | false | 0.9.5.5 | |
Druid | false | 1.2.6 | |
HikariCP | false | 4.0.3 |
区分包
除了关系型数据库以外,Unabo还用相似接口做了NoSQL适配,因无法让SQL与NoSQL两者接口完全兼容,顾以包名区分。 如:使用Bootstrap
时会出现2个import
提示
这种选择会很多,不过也无需困扰,只需根据当前数据库类型来判断,如使用的 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不会缓存该实例。
- 参考:
url
- 类型:
String
- 默认值:
""
- 详情
数据库的地址,SQL型数据库必填项。
- 参考
driver
- 类型:
String | Enum
- 默认值:
""
- 详情
- 参考
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语句
- 参考
DataBase | validationQuery |
---|---|
Oracle | select 1 from dual |
MySql | select 1 |
Microsoft SqlServer | select1 |
postgresql | select version() |
datasourceType
- 类型:
datasourceType
- 默认值:
Dbcp
- 详情
数据库连接池
-参考
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
- 属性
name | type | default | explain |
---|---|---|---|
name | String | - | 对应表名 |
schema | String | - | 库/命名空间/用户域, 如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
- 作用 对应字段名
- 属性
name | type | default | explain |
---|---|---|---|
name | String | - | 对应字段名 |
jsonSerialization | boolean | false | 修改/插入 操作是否对字段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 生命周期,从创建到销毁都在容器中操作,又分为 sql
和 nosql
online.sanen.unabo.sql.Container sqlContainer = Unabo.sql;
online.sanen.unabo.nosql.Container nosqlContainer = Unabo.nosql;
创建
id为sys的实例
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));
});
}