Xbatis:SpringBoot 数据管理框架

138 阅读10分钟

Xbatis 是一个 SpringBoot 应用环境中使用的数据管理框架,它基于 MyBatis 实现,支持 MySQL,可以使用更加 Java 的方式实现业务逻辑中的 CRUD 操作。

安装

下载源码


git clone https://github.com/njdi/durian.git

编译源码


cd durian/

切换至最新版本(Tag),如:0.4,


git checkout 0.4

编译安装至本地 Maven 仓库:


mvn clean package

添加依赖

SpringBoot 项目使用 Xbatis 时,需要在 Maven pom.xml 中添加:


<dependency>

<groupId>io.njdi</groupId>

<artifactId>durian-xbatis</artifactId>

<version>${version}</version>

</dependency>

${version} 替换为具体的版本号,如:0.4。

数据表

数据表 mytable 包含 3 个字段:id、col1 和 col2,


CREATE TABLE `mytable` (

`id` int NOT NULL AUTO_INCREMENT,

`col1` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,

`col2` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

其中,字段 id 是主键,且支持自增。

Xbatis 中每一张数据表都要求必须包含一个整数类型的主键字段,名称为 id,且支持自增。

数据源

Xbatis 运行时需要使用 SpringBoot 提供的数据源(DataSource),需要在 application.yml 中添加:


spring:

application:

name: sample

datasource:

url: jdbc:mysql://${host}:${port}/${db}?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true

username: ${user}

password: ${passwd}

hikari:

keepaliveTime: 30000

maxLifetime: 600000

maximumPoolSize: 30

${...} 替换为具体的数据库信息:

  • ${host}:MySQL 实例地址;

  • ${port}:MySQL 实例端口;

  • ${db}:数据库名称;

  • ${user}:用户名;

  • ${passwd}:密码;

Xbatis

Xbatis 是基于 MyBatis 实现的,运行时相关实例需要以 Bean 的形式注入到 Spring 容器,为保证 Spring 可以正常扫描且实例化这些 Bean,需要作如下配置:

Main


import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication

@ComponentScan({"io.njdi.durian.sample.xbatis", "io.njdi.durian.xbatis"})

@MapperScan(basePackages = {"io.njdi.durian.xbatis.core"})

public class Main {

public static void main(String[] args) {

SpringApplication.run(Main.class, args);

}

}

Main 是 SpringBoot 应用的入口类,@ComponentScan 用于指定“去哪里”扫描 Xbatis 的 Bean,“io.njdi.durian.sample.xbatis” 是示例项目的 Bean,“io.njdi.durian.xbatis” 是 Xbatis 项目的 Bean;@MapperScan 用于指定“去哪里”扫描 MyBatis 的 Mapper。

application.xml


mybatis:

mapper-locations: classpath:xbatis.xml

mybatis.mapper-locations 用于指定“去哪里”加载 MyBatis 的配置文件。

XbatisManager

XbatisManager 是数据管理器实例,用以完成 CRUD 相关的具体操作,可以在 ServiceDao 层注入此实例:


@Autowired

private XbatisManager xbatisManager;

Database/Table/Column

Xbatis 中也有数据库(Database)、数据表(Table)和数据列(Column)的概念,它们是 业务逻辑 中的库/表/列,相较于 MySQL 中的 物理 库/表/列,它们拥有更多的业务属性。

Column

Column 用于定义 ,它对应着 MySQL 中的一列,包含以下属性:

name

列名称,指 业务逻辑 中列的名称;如果 业务逻辑 中列的名称与 MySQL 中对应列的名称不一致,则需要使用列别名(alias)指定。

alias

列别名,指 MySQL 中列的名称;仅列名称与列别名不一致时需要指定。

type

列类型,指 业务逻辑 中列的数据类型,支持数值和字符串类型,默认为字符串类型(String)。

implicit

列默认查询标识,查询数据表时,如果没有指定需要查询具体哪些列,会使用默认查询列替代,默认为 true。

create

列允许插入标识,指列是否支持插入,默认为 true。

select

列允许查询标识,指列是否支持查询,默认为 true。

update

列允许更新标识,指列是否支持更新,默认为 false。

Column 实例支持通过 Builder 模式进行创建:


Column id = Column.builder().name(COLUMN_MYTABLE_ID).type(Integer.class).create(false).build();

Column colOne = Column.builder().name("colOne").alias("col1").build();

Column colTwo = Column.builder().name("colTwo").alias("col2").build();

其中,id 可使用 type() 指定类型为整数,使用 create() 指定不允许插入;colOne 和 colTwo 可使用 alias() 分别指定别名 col1 和 col2。

Table

Table 用于定义 ,它对应着 MySQL 中的一张表,包含以下属性:

name

表名称,指 业务逻辑 中表的名称;如果 业务逻辑 中表的名称与 MySQL 中对应表的名称不一致,则需要使用表别名(alias)指定。

alias

表别名,指 MySQL 中表的名称;仅表名称与表别名不一致时需要指定。

columns

列集合,指 某表的多个列(Column)。

limit

查询某表时,最多可以返回的记录数目,默认为整数最大值(Integer.MAX_VALUE),即不限制。

create

表允许插入标识,默认为 true。

delete

表允许删除标识,默认为 true。

page

表允许(分页)查询标识,默认为 true。

update

表允许更新标识,默认为 true。

deleteMustHaveWhere

表删除时,必须指定过滤条件标识,默认为 true。

pageMustHaveWhere

表查询时,必须指定过滤条件标识,默认为 true。

updateMustHaveWhere

表更新时,必须指定过滤条件标识,默认为 true。

Table 实例支持通过 Builder 模式进行创建:


Table myTable = Table.builder()

.name("myTable")

.alias("mytable")

.column(id)

.column(colOne)

.column(colTwo)

.build();

使用 alias() 可指定表别名(注意 myTablemytable 的区别);使用 column() 可添加多个列。

Database

Database 用于定义 ,它对应着 MySQL 中的一个数据库,仅包含一个属性:

tables

表集合,指库的多张表(Table)。

Database 实例支持通过 Builder 模式进行创建:


Database database = Database.builder()

.table(myTable)

.build();

使用 table() 可添加多张表。

Database(库) 实例可以包含多个 Table(表) 实例;Table 实例可以包含多个 Column(列)实例。其中,Database 实例创建完成以后,需要以 Bean 的形式注入 Spring 容器:


import io.njdi.durian.xbatis.model.schema.Column;

import io.njdi.durian.xbatis.model.schema.Database;

import io.njdi.durian.xbatis.model.schema.Table;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class DbConfigurer {

public static final String TABLE_MYTABLE = "myTable";

public static final String COLUMN_MYTABLE_ID = "id";

public static final String COLUMN_MYTABLE_COL_ONE = "colOne";

public static final String COLUMN_MYTABLE_COL_TWO = "colTwo";

public Table createTable() {

Column id = Column.builder().name(COLUMN_MYTABLE_ID).type(Integer.class).create(false).build();

Column colOne = Column.builder().name(COLUMN_MYTABLE_COL_ONE).alias("col1").build();

Column colTwo = Column.builder().name(COLUMN_MYTABLE_COL_TWO).alias("col2").build();

return Table.builder()

.name(TABLE_MYTABLE)

.alias("mytable")

.column(id)

.column(colOne)

.column(colTwo)

.build();

}

@Bean

public Database createDatabase() {

Table myTable = createTable();

return Database.builder()

.table(myTable)

.build();

}

}

注意 @Configuration@Bean 两个注解的使用。其中,业务逻辑 中的表名和列名 均使用 公共静态常量(public/static/final) 进行声明,方便业务代码引用。

Create/Creates

Create 用于描述 插入 操作,每一个 Create 实例对应着一条 Insert 语句,如下:


INSERT INTO mytable (col1, col2) VALUES('a', 'b')

Insert 语句包含两部分重要内容:键值对和表名。键值对即列名和列值,如:


col1 -> a

col2 -> b

Pair

在 Xbatis 中键值对是使用 Pair 表示的,每一个 Pair 实例表示一组列名和列值;其中,列名使用属性 name 表示,列值使用属性 value 表示,如:


Pair<String> colOne = Pair.<String>builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.value("a")

.build();

Pair<String> colTwo = Pair.<String>builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_TWO)

.value("b")

.build();

创建一个 Create 实例表示一条 Insert 语句:


String table = DbConfigurer.TABLE_MYTABLE;

Pair<String> colOne = Pair.<String>builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.value("a")

.build();

Pair<String> colTwo = Pair.<String>builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_TWO)

.value("b")

.build();

Create create = Create.builder()

.pair(colOne)

.pair(colTwo)

.table(table)

.build();

创建 Create 实例时,使用了 DbConfigurer 中表名和列名的公共静态常量。

id

id 是 Create 中一个很重要的属性,它要求 MySQL 数据表中的主键字段名称为 id,且必须是自增的;使用 XbatisManager 执行插入操作后,可以获取已插入记录的主键值:


int id = xbatisManager.create(create);

log.info("id: {}", id);

也可以通过 Create 实例获取主键值:


log.info("id: {}", create.getId());

Creates

Creates 实例内部可以添加多个 Create 实例,用于表示多条记录的插入操作。


Creates creates = Creates.builder()

.create(create)

.create(create2)

.build();

List<Integer> ids = xbatisManager.creates(creates);

log.info("ids: {}", ids);

Delete/Deletes

Delete 用于描述 删除 操作,每一个 Delete 实例对应着一条 Delete 语句,如下:


DELETE FROM mytable WHERE col1 = 'a'

Delete 语句包含两部分重要内容:表名和过滤条件(可选)。

Where

Xbatis 中过滤条件使用 Where 表示,每一个 Where 实例表示一个过滤条件。Where 有四种实现:

  • Filter(基本过滤器)

  • OrFilter(逻辑或过滤器)

  • AndFilter(逻辑与过滤器)

  • NotFilter(逻辑非过滤器)

Filter

Filter 是基本过滤器的实现,它含有三个属性:

name

列名称。

operator

操作符,支持:EQ(=), NE(!=), GT(>), LT(<), GE(>=), LE(<=), BETWEEN(between ... and ...), LIKE(like), IN(in), NOT_IN(not in), IS_NULL(is null), IS_NOT_NULL(is not null)。

values

值列表,根据操作符的不同,可能是零个、一个或多个值。

创建 Filter 实例:


// col1 = 'a'

Filter eq = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.operator(Filter.Operator.EQ)

.value("a")

.build();

// id between 1 and 10

Filter between = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_ID)

.operator(Filter.Operator.BETWEEN)

.value(1)

.value(10)

.build();

// col2 is not null

Filter isNotNull = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_TWO)

.operator(Filter.Operator.IS_NOT_NULL)

.build();

Delete、Page 和 Update 实例均可以添加多个 Filter(Where) 实例,多个 Filter 之间的逻辑关系是 And(与)。

OrFilter

OrFilter 逻辑非过滤器,用于表示多个 Filter 之间的逻辑或(Or)关系。

假设需要表示过滤:


col1 = 'a' || col2 is not null

使用 OrFilter:


// col1 = 'a'

Filter eq = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.operator(Filter.Operator.EQ)

.value("a")

.build();

// col2 is not null

Filter isNotNull = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_TWO)

.operator(Filter.Operator.IS_NOT_NULL)

.build();

// col1 = 'a' or col2 is not null

OrFilter orFilter = OrFilter.builder()

.filter(eq)

.filter(isNotNull)

.build();

AndFilter

AndFilter 逻辑与过滤器,用于表示多个 Filter 之间的逻辑与(And)关系,使用场景较少。

NotFilter

NotFilter 逻辑非过滤器,用于表示多个 Filter 之间的逻辑非(Not)关系,使用场景较少。

使用 Where 创建 Delete 实例:


String table = DbConfigurer.TABLE_MYTABLE;

Filter colOne = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.operator(Filter.Operator.EQ)

.value("a")

.build();

Delete delete = Delete.builder()

.table(table)

.where(colOne)

.build();

可以使用 Delete where() 添加多个 Where 实例,它们之间的逻辑关系是与(And)。

执行删除操作:


int rows = xbatisManager.delete(delete);

log.info("rows: {}", rows);

XbatisManager delete() 会返回执行删除操作后所影响的记录行数,也可以使用 Deletes 执行多个删除操作。

Page

Page 用于描述 查询 操作,每一个 Page 实例对应着一条 Select 语句,它是 Xbatis 中最复杂的结构,包含若干组成部分:

  • Field(查询字段)

  • Where(过滤)

  • Group(分组)

  • Having(分组过滤)

  • Order(排序)

  • limit/offset(分页)

Field

Field 用于描述 查询字段 部分:


SELECT

select_expr [, select_expr] ...

FROM

table

select_expr 就是查询字段,每一个 Field 实例对应着一个 查询字段,它包含两个属性:

name

列名称。

alias

列别名,如果需要为查询的列起一个其它的名称,可以使用列别名指定。

创建 Field 实例:


Field field = Field.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.alias("mycol")

.build();

翻译成 SQL 片段:


SELECT

col1 AS mycol

FROM

table

Where


SELECT

select_expr [, select_expr] ...

FROM

table

WHERE

where_condition

详细内容参考 Delete Where。

Group

列名称集合,每一个列名称表示一个分组字段,直接使用字符串表示。


SELECT

select_expr [, select_expr] ...

FROM

table

WHERE

where_condition

GROUP BY

col_name [, col_name] ...

Having


SELECT

select_expr [, select_expr] ...

FROM

table

WHERE

where_condition

GROUP BY

col_name [, col_name] ...

HAVING

where_condition

详细内容参考 Delete Where。

Order

Order 用于描述 排序字段 部分:


SELECT

select_expr [, select_expr] ...

FROM

table

WHERE

where_condition

GROUP BY

col_name [, col_name] ...

HAVING

where_condition

ORDER BY order_expr [, order_expr] ...

order_expr 就是排序字段,每一个 Order 实例对应着一个 排序字段,它包含两个属性:

name

列名称。

sort

排序,升序(ASC)或降序(DESC)。

创建 Order 实例:


Order order = Order.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.sort(Order.Sort.DESC)

.build();

翻译成 SQL 片段:


ORDER BY col1 DESC

limit/offset

limit 用于限制查询返回的记录行数,offset 用于指定记录偏移量,两者结合可实现分页。


SELECT

select_expr [, select_expr] ...

FROM

table

WHERE

where_condition

GROUP BY

col_name [, col_name] ...

HAVING

where_condition

ORDER BY order_expr [, order_expr] ...

LIMIT limit

OFFSET offset

创建 Page 实例:


Page page = Page.builder()

.field(...)

.table(table)

.where(...)

.group(...)

.having(...)

.order(...)

.limit(...)

.offset(...)

.build();

其中,field()、where()、group()、having()、order() 均可以使用多次。

执行查询操作:


List<Map<String, Object>> rows = xbatisManager.page(page);

log.info("rows: {}", rows);

XbatisManager page() 会返回一个记录集合,使用 List 表示;每一个记录使用一个 Map 表示,Key(String)代表列名称,Value(Object)代表列值。

XbatisManager page() 有一个重载方法,可以直接以 对象集合 的形式返回查询结果:


public class MyRow {

private Integer id;

private String colOne;

private String colTwo;

}

List<MyRow> rows = xbatisManager.page(page, MyRow.class);

log.info("rows: {}", rows);

每一个 MyRow 实例表示一行记录。注意,MyRow 属性名称及类型必须与列名称及类型一一对应。

id/ids

使用 ID 查询记录是很常见的需求,Xbatis 直接支持这样的查询请求。ID 可以有两种解读:

  • MySQL 数据表记录中的自增 id

  • 业务逻辑中的唯一记录标识

对于第一种情况:


String table = DbConfigurer.TABLE_MYTABLE;

int id = 106;

MyRow myRow = xbatisManager.id(table, id, MyRow.class);

log.info("id: {}", myRow);

直接使用 id 值查询对应的记录。

对于第二种情况:


String table = DbConfigurer.TABLE_MYTABLE;

String name = "id";

Integer value = 106;

MyRow myRow = xbatisManager.id(table, name, value, MyRow.class);

log.info("id: {}", myRow);

需要特别指定唯一记录标识列的名称 name。

此外,id() 也支持返回 Map 类型的记录。

ids() 可以查询 ID 集合(多个ID)的记录列表。如果 ID 集合中存在 重复 的 ID,返回的记录列表也会包含 重复 的记录。

Update/Updates

Update 用于描述 更新 操作,每一个 Update 实例对应着一条 Update 语句,如下:


UPDATE

mytable

SET

col2 = 'b'

WHERE

col1 = 'a'

Update 语句包含三部分重要内容:表名,键值对(Pair)和过滤条件(Where);其中,Pair 请参考 Create Pair,Where 请参考 Delete Where。

创建 Update 实例:


String table = DbConfigurer.TABLE_MYTABLE;

Filter filter = Filter.builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_ONE)

.operator(Filter.Operator.EQ)

.value("a")

.build();

Pair<String> pair = Pair.<String>builder()

.name(DbConfigurer.COLUMN_MYTABLE_COL_TWO)

.value("c")

.build();

Update update = Update.builder()

.table(table)

.pair(pair)

.where(filter)

.build();

执行更新操作:


int rows = xbatisManager.update(update);

log.info("rows: {}", rows);

XbatisManager update() 会返回执行更新操作后所影响的记录行数,也可以使用 Updates 执行多个删除操作。

自定义列名称

一般情况下,Field.nameFilter.nameOrder.name 只能使用已经定义的列名称 Column.name。如果需要使用自定义的列名称,则需要通过 expr 属性额外指定。

以 Field 为例,假设需要查询列 colOne 大写之后的值:


Field field = Field.builder()

.name("UPPER(col1)")

.alias("colOne")

.expr(true)

.build();

注意,使用自定义列名称时,只能引用 MySQL 数据表中的列名,如:col1。

示例