# Conflicts:
#	mybatis-flex-codegen/src/main/java/com/mybatisflex/codegen/config/ColumnConfig.java
#	mybatis-flex-codegen/src/main/java/com/mybatisflex/codegen/entity/Column.java
This commit is contained in:
zack 2023-07-25 22:00:39 +08:00
commit 10d2080a94
46 changed files with 1563 additions and 107 deletions

View File

@ -1,6 +1,25 @@
# MyBatis-Flex ChangeLog
## v1.5.3 20230725:
- 新增:添加 UpdateChain 方便用于对数据进行更新
- 新增:添加对 ActiveRecord 设计模式的支持,感谢 @Suomm
- 新增:代码生成器 ColumnConfig 增加 propertyType. 可以用于自定通用属性的类型。感谢 @jerryzhengsz1
- 新增:添加 selectOneWithRelationsById(根据主表主键来查询 1 条数据) 方法,感谢 @barql
- 新增QueryWrapper.groupBy 支持 Lambda 表达式的功能,感谢 @Suomm
- 新增QueryWrapper 添加 `not like` 构建的支持
- 优化:重命名 QueryWrapperChain 为 QueryChain保存和 UpdateChain 统一
- 修复:`@Relation` 关联查询注解,在指定 selectColumns 出错的问题,感谢 @zhy_balck
- 修复:代码生成器配置 `camelToUnderline` 属性时 entity 生成后编译错误的问题,感谢 @genomics_zcg
- 文档:优化 APT 的文档描述有错别字的问题,感谢 @zhangjx1992
- 文档:添加关于 ActiveRecord 的相关文档,感谢 @Suomm
- 文档:修改代码生成器对 EnjoyTemplate 的描述错误的问题
- 文档:添加更多关于链式查询的相关文档
- 文档:重构文档链接,链式操作的相关文档
- 文档:修改代码生成器对 EnjoyTemplate 的描述错误的问题
## v1.5.2 20230723:
- 新增:添加 QueryWrapperChain 用于链式调用查询或者操作数据,感谢 @Suomm
- 新增:添加 DbChain 链式调用 Db + Row 的相关方法和功能,感谢 @Suomm

View File

@ -60,9 +60,10 @@ export default defineConfig({
{text: '基础查询', link: '/zh/base/query'},
{text: '关联查询', link: '/zh/base/relations-query'},
{text: '批量操作', link: '/zh/base/batch'},
{text: '链式操作', link: '/zh/base/chain'},
{text: 'QueryWrapper', link: '/zh/base/querywrapper'},
{text: 'QueryWrapperChain', link: '/zh/base/query-wrapper-chain'},
{text: 'Db + Row', link: '/zh/base/db-row'},
{text: 'Active Record', link: '/zh/base/active-record'},
{text: 'IService', link: '/zh/base/service'},
{text: 'SpringBoot 配置文件', link: '/zh/base/configuration'},
{text: 'MyBatisFlexCustomizer', link: '/zh/base/mybatis-flex-customizer'},

View File

@ -0,0 +1,138 @@
# Active Record
[Active Record 模式](http://www.martinfowler.com/eaaCatalog/activeRecord.html)出自 Martin Fowler
写的《[企业应用架构模式](https://book.douban.com/subject/4826290/)》书中。在 Active Record
模式中对象中既有持久存储的数据也有针对数据的操作。Active Record 模式把数据存取逻辑作为对象的一部分,处理对象的用户知道如何把数据写入数据库,还知道如何从数据库中读出数据。
在 MyBatis-Flex 中实现 Active Record
功能十分简单,只需让 Entity 类继承 [Model](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/Model.java)
即可。
::: tip 注意事项
- 使用 Active Record 功能时,项目中必须注入对应实体类的 BaseMapper 对象。
- 如果不想手动创建 Mapper 接口,可以使用 [代码生成器](../others/codegen.md) 或 [APT](../others/apt.md#配置文件和选项) 辅助生成。
:::
## 使用示例
在以下示例当中,使用了 [Lombok](https://www.projectlombok.org/) 对实体类进行了增强,以便我们全链式调用:
1. `@Table` 标记了实体类对应的数据表。
2. `@Data` 为我们生成了 setter/getter、toString、equals、hashCode 等方法,
其中 `staticConstructor = "create"` 为我们创建了一个 `create()` 静态方法用于链式调用。
3. `@Accessors(chain = true)` 为我们开启了 `return this;` 这样既可以被序列化,又可以链式调用。
```java
@Table("tb_account")
@Accessors(chain = true)
@Data(staticConstructor = "create")
public class Account extends Model<Account> {
@Id(keyType = KeyType.Auto)
private Long id;
private String userName;
private Integer age;
private Date birthday;
}
```
这样我们就可以流畅的使用 Active Record 功能了:
```java
@RestController
@RequestMapping("/account")
public class AccountController {
@PostMapping("save")
public boolean save(@RequestBody Account account) {
return account.save();
}
}
```
## 保存数据
`Model` 提供了 `save` 方法来保存数据,调用该方法前需要将保存数据填充:
```java
Account.create()
.setUserName("张三")
.setAge(18)
.setBirthday(new Date())
.save();
```
## 删除数据
`Model` 提供了 `remove` 方法来删除数据:
- 根据主键删除
```java
Account.create()
.setId(1L)
.removeById();
```
- 根据条件删除
```java
Account.create()
.where(Account::getId).eq(1L)
.remove();
```
## 更新数据
`Model` 提供了 `update` 方法来更新数据,调用该方法前需要将更新数据填充:
- 根据主键更新
```java
Account.create()
.setId(1L)
.setAge(100)
.updateById();
```
- 根据条件更新
```java
Account.create()
.setAge(100)
.where(Account::getId).eq(1L)
.update();
```
## 查询数据
### 查询一条数据
`Model` 提供了 `one` 方法来查询一条数据:
```java
Account.create()
.where(Account::getId).eq(1L)
.one();
```
### 查询多条数据
`Model` 提供了 `list` 方法来查询多条数据:
```java
Account.create()
.where(Account::getAge).ge(18)
.list();
```
### 查询分页数据
`Model` 提供了 `page` 方法来查询分页数据:
```java
Account.create()
.where(Account::getAge).ge(18)
.page(Page.of(1,10));
```

View File

@ -170,3 +170,28 @@ update tb_account
set user_name = "michael", age = (select ... from ... )
where id = 100
```
## UpdateChain
UpdateChain 是一个对 `UpdateEntity``UpdateWrapper` 等进行封装的一个工具类,方便用户用于进行链式操作。
假设我们要更新 `Account``userName` 为 "`张三`",更新年龄在之前的基础上加 1更新代码如下
```java
@Test
public void testUpdateChain() {
UpdateChain.of(Account.class)
.set(Account::getUserName, "张三")
.setRaw(Account::getAge, "age + 1")
.where(Account::getId).eq(1)
.update();
}
```
以上方法调用时MyBatis-Flex 内部执行的 SQL 如下:
```sql
UPDATE `tb_account` SET `user_name` = '张三' , `age` = age + 1
WHERE `id` = 1
```
更多关于 **链式操作**,请点击这个 [这里](./chain.html#updatechain-示例)。

View File

@ -1,7 +1,12 @@
# QueryWrapperChain
# 链式操作
`QueryWrapperChain.java` 是一个对 `QueryWrapper` 进行链式调用封装的一个类,在 Service 中,
我们可以调用 `service.queryChain()` 获得该实例。
在 MyBatis-Flex 中,内置了 `QueryChain.java``UpdateChain.java` 用于对数据进行链式查询操作和链式数据操作(修改和删除)。
- **QueryChain**:链式查询
- **UpdateChain**:链式更新
## QueryChain 示例
例如,查询文章列表代码如下:
@ -24,29 +29,76 @@ class ArticleServiceTest {
}
```
若不是在 Service 中,我们也可以通过 `QueryWrapperChain.create` 方法,自己创建一个 `QueryWrapperChain` 实例,代码如下:
若不是在 Service 中,我们也可以通过 `QueryChain.create` 方法,自己创建一个 `QueryChain` 实例,代码如下:
```java
List<Article> articles = QueryWrapperChain.create(mapper)
List<Article> articles = QueryChain.of(mapper)
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.list();
```
## QueryWrapperChain 的方法
## UpdateChain 示例
假设我们要更新 `Account``userName` 为 "`张三`",更新年龄在之前的基础上加 1更新代码如下
```java
@Test
public void testUpdateChain1() {
UpdateChain.of(Account.class)
.set(Account::getUserName, "张三")
.setRaw(Account::getAge, "age + 1")
.where(Account::getId).eq(1)
.update();
}
```
以上方法调用时MyBatis-Flex 内部执行的 SQL 如下:
```sql
UPDATE `tb_account` SET `user_name` = '张三' , `age` = age + 1
WHERE `id` = 1
```
**另一个示例:**
```java
@Test
public void testUpdateChain2() {
//更新数据
UpdateChain.of(Account.class)
.set(Account::getAge, ACCOUNT.AGE.add(1))
.where(Account::getId).ge(100)
.and(Account::getAge).eq(18)
.update();
//查询所有数据并打印
QueryChain.of(accountMapper)
.list()
.forEach(System.out::println);
}
```
通过 `UpdateChain` 进行 `update()`,其执行的 SQL 如下:
```sql
UPDATE `tb_account` SET `age` = `age` + 1
WHERE `id` >= 100 AND `age` = 18
```
## QueryChain 的方法
- one():获取一条数据
- list():获取多条数据
- page():分页查询
- obj():当 SQL 查询只返回 1 列数据的时候,且只有 1 条数据时,可以使用此方法
- objList():当 SQL 查询只返回 1 列数据的时候,可以使用此方法
- remove():删除数据
- update(entity):更新数据
- count():查询数据条数
- exists():是否存在,判断 count 是否大于 0
## 扩展方法
## QueryChain 扩展方法
### `one()` 系列方法

View File

@ -1,6 +1,25 @@
# MyBatis-Flex ChangeLog
## v1.5.3 20230725:
- 新增:添加 UpdateChain 方便用于对数据进行更新
- 新增:添加对 ActiveRecord 设计模式的支持,感谢 @Suomm
- 新增:代码生成器 ColumnConfig 增加 propertyType. 可以用于自定通用属性的类型。感谢 @jerryzhengsz1
- 新增:添加 selectOneWithRelationsById(根据主表主键来查询 1 条数据) 方法,感谢 @barql
- 新增QueryWrapper.groupBy 支持 Lambda 表达式的功能,感谢 @Suomm
- 新增QueryWrapper 添加 `not like` 构建的支持
- 优化:重命名 QueryWrapperChain 为 QueryChain保存和 UpdateChain 统一
- 修复:`@Relation` 关联查询注解,在指定 selectColumns 出错的问题,感谢 @zhy_balck
- 修复:代码生成器配置 `camelToUnderline` 属性时 entity 生成后编译错误的问题,感谢 @genomics_zcg
- 文档:优化 APT 的文档描述有错别字的问题,感谢 @zhangjx1992
- 文档:添加关于 ActiveRecord 的相关文档,感谢 @Suomm
- 文档:修改代码生成器对 EnjoyTemplate 的描述错误的问题
- 文档:添加更多关于链式查询的相关文档
- 文档:重构文档链接,链式操作的相关文档
- 文档:修改代码生成器对 EnjoyTemplate 的描述错误的问题
## v1.5.2 20230723:
- 新增:添加 QueryWrapperChain 用于链式调用查询或者操作数据,感谢 @Suomm
- 新增:添加 DbChain 链式调用 Db + Row 的相关方法和功能,感谢 @Suomm

View File

@ -39,7 +39,7 @@ VALUES (1, '张三', 18, '2020-01-11'),
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>

View File

@ -12,12 +12,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-core</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
<scope>provided</scope>
</dependency>
```
@ -28,12 +28,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
<scope>provided</scope>
</dependency>
``````
@ -44,12 +44,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
<scope>provided</scope>
</dependency>
```
@ -70,7 +70,7 @@
<path>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</path>
</annotationProcessorPaths>
</configuration>

View File

@ -88,7 +88,7 @@ public class AccountTableDef extends TableDef {
#upperCase, lowerCase, upperCamelCase, lowerCamelCase
processor.tableDef.propertiesNameStyle = upperCase
```
风格支持 4 配置,默认(未配置时)为 upperCase支持的配置分别为
风格支持 4 配置,默认(未配置时)为 upperCase支持的配置分别为
- upperCase大写 + 下划线例如USER_NAME
- lowerCase小写 + 下划线例如user_name
@ -173,7 +173,7 @@ processor.allInTables.package=com.example.entity.table
## 和 Lombok、Mapstruct 整合
在很多项目中,用到了 Lombok 帮我们减少代码编写,同时用到 Mapstruct 进行 bean 转换。使用到 Lombok 和 Mapstruct 时,其要求我们
在很多项目中,用到了 Lombok 帮我们减少代码编写,同时用到 Mapstruct 进行 bean 转换。使用到 Lombok 和 Mapstruct 时,其要求我们
pom.xml 添加 `annotationProcessorPaths` 配置,
此时,我们也需要把 MyBatis-Flex 的 annotation 添加到 `annotationProcessorPaths` 配置里去,如下图所示:
@ -219,7 +219,7 @@ pom.xml 添加 `annotationProcessorPaths` 配置,
```
dependencies {
...
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:<version>1.5.2</version>'
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:<version>1.5.3</version>'
}
```

View File

@ -10,7 +10,7 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</dependency>
```
@ -474,40 +474,30 @@ public class EnjoyTemplate implements ITemplate {
private Engine engine;
public EnjoyTemplate() {
engine = Engine.create("mybatis-flex", engine -> {
engine.setToClassPathSourceFactory();
engine.addSharedMethod(StringUtil.class);
Engine engine = Engine.use(engineName);
if (engine == null) {
engine = Engine.create(engineName, e -> {
e.addSharedStaticMethod(StringUtil.class);
e.setSourceFactory(new FileAndClassPathSourceFactory());
});
}
this.engine = engine;
// 以下配置将支持 user.girl 表达式去调用 user 对象的 boolean isGirl() 方法
Engine.addFieldGetterToFirst(new FieldGetters.IsMethodFieldGetter());
}
/**
* 生成 entity 的方法实现
*/
@Override
public void generateEntity(GlobalConfig globalConfig, Table table, File entityJavaFile) throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("globalConfig", globalConfig);
params.put("table", table);
FileOutputStream fileOutputStream = new FileOutputStream(entityJavaFile);
engine.getTemplate("/templates/enjoy/entity.tpl").render(params, fileOutputStream);
public void generate(Map<String, Object> params, String templateFilePath, File generateFile) {
if (!generateFile.getParentFile().exists() && !generateFile.getParentFile().mkdirs()) {
throw new IllegalStateException("Can not mkdirs by dir: " + generateFile.getParentFile());
}
// 开始生成文件
try (FileOutputStream fileOutputStream = new FileOutputStream(generateFile)) {
engine.getTemplate(templateFilePath).render(params, fileOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
/**
* 生成 mapper 的方法实现
*/
@Override
public void generateMapper(GlobalConfig globalConfig, Table table, File mapperJavaFile) throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("globalConfig", globalConfig);
params.put("table", table);
FileOutputStream fileOutputStream = new FileOutputStream(mapperJavaFile);
engine.getTemplate("/templates/enjoy/mapper.tpl").render(params, fileOutputStream);
}
}
```

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -216,6 +216,9 @@ public class ColumnConfig implements Serializable {
public void setTenantId(Boolean tenantId) {
this.tenantId = tenantId;
}
public Class<?> getPropertyType() {
return propertyType;
}
public String getPropertyType() {
return propertyType;

View File

@ -76,6 +76,31 @@ public class StrategyConfig {
*/
private Set<String> unGenerateTables;
/**
* 需要忽略的列 全局配置
*/
private Set<String> ignoreColumns;
/**
* 获取需要忽略的列 全局配置
*/
public Set<String> getIgnoreColumns() {
return ignoreColumns;
}
/**
* 设置需要忽略的列 全局配置
*/
public StrategyConfig setIgnoreColumns(String... columns) {
if (ignoreColumns == null) {
ignoreColumns = new HashSet<>();
}
for (String column : columns) {
if (column != null && column.trim().length() > 0) {
ignoreColumns.add(column.trim().toLowerCase());
}
}
return this;
}
/**
* 设置要生成的模式

View File

@ -131,6 +131,12 @@ public class Table {
public void addColumn(Column column) {
//排除忽略列
if (globalConfig.getStrategyConfig().getIgnoreColumns() != null &&
globalConfig.getStrategyConfig().getIgnoreColumns().contains(column.getName().toLowerCase())) {
return;
}
//主键
if (primaryKeys != null && primaryKeys.contains(column.getName())) {
column.setPrimaryKey(true);
@ -239,7 +245,7 @@ public class Table {
tableAnnotation.append(", schema = \"").append(tableConfig.getSchema()).append("\"");
}
if (tableConfig.getCamelToUnderline() != null) {
tableAnnotation.append(", camelToUnderline = \"").append(tableConfig.getCamelToUnderline()).append("\"");
tableAnnotation.append(", camelToUnderline = ").append(tableConfig.getCamelToUnderline());
}
if (tableConfig.getInsertListenerClass() != null) {
tableAnnotation.append(", onInsert = ").append(tableConfig.getInsertListenerClass().getSimpleName()).append(".class");

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -490,6 +490,16 @@ public interface BaseMapper<T> {
return MapperUtil.queryRelations(this, MapperUtil.getSelectOneResult(selectListByQuery(queryWrapper)));
}
/**
* 根据主表主键来查询 1 条数据
*
* @param id 主表主键
* @return 实体类数据
*/
default T selectOneWithRelationsById(Serializable id) {
return MapperUtil.queryRelations(this, selectOneById(id));
}
/**
* 根据查询条件来查询 1 条数据
*

View File

@ -24,7 +24,7 @@ public class FlexConsts {
}
public static final String NAME = "MyBatis-Flex";
public static final String VERSION = "1.5.2";
public static final String VERSION = "1.5.3";
public static final String DEFAULT_PRIMARY_FIELD = "id";

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.activerecord;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.mybatis.Mappers;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.SqlUtil;
import java.util.Optional;
/**
* <p>使用 {@link BaseMapper} 进行 CRUD 操作的实体类的抽象接口
*
* <p>使用接口是为了方便拓展该接口提供了简单的根据 <b>主键</b> 操作数据的方法
* 实现类可以进行其他方法的扩展
*
* @param <T> 实体类类型
* @author 王帅
* @since 2023-07-23
*/
@SuppressWarnings({"unused", "unchecked"})
public interface MapperModel<T> {
/**
* 获取实体类对应的 {@link BaseMapper} 接口
*
* @return {@link BaseMapper} 接口
*/
default BaseMapper<T> baseMapper() {
return Mappers.ofEntityClass((Class<T>) getClass());
}
/**
* <p>获取实体类主键数据
*
* <p>可以拓展该方法提高效率例如
* <pre>{@code
* return new Object[]{id};
* }</pre>
*
* @return 主键数据数组
*/
default Object[] getPkValues() {
TableInfo tableInfo = TableInfoFactory.ofEntityClass(getClass());
return tableInfo.buildPkSqlArgs(this);
}
/**
* 保存数据自动忽略 {@code null}
*
* @return {@code true} 保存成功{@code false} 保存失败
*/
default boolean save() {
return save(true);
}
/**
* 保存数据并设置是否忽略 {@code null}
*
* @param ignoreNulls 是否忽略 {@code null}
* @return {@code true} 保存成功{@code false} 保存失败
*/
default boolean save(boolean ignoreNulls) {
return SqlUtil.toBool(baseMapper().insert((T) this, ignoreNulls));
}
/**
* 保存或者更新数据如果实体类主键没有值 <b>保存</b> 数据如果实体类主键有值
* <b>更新</b> 数据全部自动忽略 {@code null}
*
* @return {@code true} 保存或更新成功{@code false} 保存或更新失败
*/
default boolean saveOrUpdate() {
return saveOrUpdate(true);
}
/**
* 保存或者更新数据如果实体类主键没有值 <b>保存</b> 数据如果实体类主键有值
* <b>更新</b> 数据并设置是否忽略 {@code null}
*
* @param ignoreNulls 是否忽略 {@code null}
* @return {@code true} 保存或更新成功{@code false} 保存或更新失败
*/
default boolean saveOrUpdate(boolean ignoreNulls) {
return SqlUtil.toBool(baseMapper().insertOrUpdate((T) this, ignoreNulls));
}
/**
* 根据实体类主键删除数据
*
* @return {@code true} 删除成功{@code false} 删除失败
*/
default boolean removeById() {
return SqlUtil.toBool(baseMapper().deleteById(getPkValues()));
}
/**
* 根据实体类主键更新数据自动忽略 {@code null}
*
* @return {@code true} 更新成功{@code false} 更新失败
*/
default boolean updateById() {
return updateById(true);
}
/**
* 根据实体类主键更新数据并设置是否忽略 {@code null}
*
* @param ignoreNulls 是否忽略 {@code null}
* @return {@code true} 更新成功{@code false} 更新失败
*/
default boolean updateById(boolean ignoreNulls) {
return SqlUtil.toBool(baseMapper().update((T) this, ignoreNulls));
}
/**
* 根据实体类主键获取一条数据
*
* @return 数据
*/
default T oneById() {
return baseMapper().selectOneById(getPkValues());
}
/**
* 根据实体类主键获取一条数据并封装为 {@link Optional} 返回
*
* @return 数据
*/
default Optional<T> oneByIdOpt() {
return Optional.ofNullable(oneById());
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.activerecord;
import com.mybatisflex.core.activerecord.query.QueryModel;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.util.SqlUtil;
import java.util.List;
import java.util.Optional;
/**
* Active Record 模型
*
* @param <T> 实体类类型
* @author 王帅
* @since 2023-07-24
*/
@SuppressWarnings({"unused", "unchecked"})
public abstract class Model<T extends Model<T>>
extends QueryModel<T>
implements MapperModel<T> {
/**
* 根据实体类构建的条件删除数据
*
* @return {@code true} 删除成功{@code false} 删除失败
*/
public boolean remove() {
return SqlUtil.toBool(baseMapper().deleteByQuery(getQueryWrapper()));
}
/**
* 根据实体类构建的条件更新数据自动忽略 {@code null}
*
* @return {@code true} 更新成功{@code false} 更新失败
*/
public boolean update() {
return update(true);
}
/**
* 根据实体类构建的条件更新数据并设置是否忽略 {@code null}
*
* @param ignoreNulls 是否忽略 {@code null}
* @return {@code true} 更新成功{@code false} 更新失败
*/
public boolean update(boolean ignoreNulls) {
return SqlUtil.toBool(baseMapper().updateByQuery((T) this, ignoreNulls, getQueryWrapper()));
}
/**
* 根据实体类构建的条件查询数据数量
*
* @return 数据数量
*/
public long count() {
return baseMapper().selectCountByQuery(getQueryWrapper());
}
/**
* 根据实体类构建的条件判断数据是否存在
*
* @return {@code true} 数据存在{@code false} 数据不存在
*/
public boolean exists() {
return SqlUtil.toBool(count());
}
/**
* 根据实体类构建的条件获取一条数据
*
* @return 数据
*/
public T one() {
return baseMapper().selectOneByQuery(getQueryWrapper().limit(1));
}
/**
* 根据实体类构建的条件获取一条数据并封装为 {@link Optional} 返回
*
* @return 数据
*/
public Optional<T> oneOpt() {
return Optional.ofNullable(one());
}
/**
* 根据实体类构建的条件获取多条数据
*
* @return 数据列表
*/
public List<T> list() {
return baseMapper().selectListByQuery(getQueryWrapper());
}
/**
* 根据实体类构建的条件获取分页数据
*
* @param page 分页对象
* @return 分页数据
*/
public Page<T> page(Page<T> page) {
return baseMapper().paginate(page, getQueryWrapper());
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Active Record 功能
*/
package com.mybatisflex.core.activerecord;

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.activerecord.query;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.util.LambdaGetter;
import com.mybatisflex.core.util.LambdaUtil;
/**
* Lambda 排序构建器
*
* @author 王帅
* @since 2023-07-25
*/
public class OrderByBuilder<R extends QueryModel<R>> {
private final R queryModel;
private final QueryColumn queryColumn;
public <T> OrderByBuilder(R queryModel, LambdaGetter<T> getter) {
this.queryModel = queryModel;
this.queryColumn = LambdaUtil.getQueryColumn(getter);
}
public R asc() {
return queryModel.orderBy(queryColumn.asc());
}
public R desc() {
return queryModel.orderBy(queryColumn.desc());
}
}

View File

@ -0,0 +1,178 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.activerecord.query;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.core.query.*;
import com.mybatisflex.core.util.LambdaGetter;
import com.mybatisflex.core.util.LambdaUtil;
/**
* <p>实体类条件查询构建模型
*
* <p>该类内部维护了一个 {@link QueryWrapper} 属性用来构建查询条件
* 通过实体类属性构建的查询条件都是值等于该扩展用于非等于值构建及一些其他方法
* 如果不想通过实体类直接构建查询条件可以不继承该类
*
* @param <T> 实体类类型
* @author 王帅
* @since 2023-07-24
*/
@SuppressWarnings({"unused", "unchecked"})
public abstract class QueryModel<T extends QueryModel<T>> {
@Column(ignore = true)
private QueryWrapper queryWrapper;
protected QueryWrapper getQueryWrapper() {
if (queryWrapper == null) {
queryWrapper = QueryWrapper.create();
}
return queryWrapper;
}
public T select() {
return (T) this;
}
public T select(String... columns) {
getQueryWrapper().select(columns);
return (T) this;
}
public T select(QueryColumn... queryColumns) {
getQueryWrapper().select(queryColumns);
return (T) this;
}
public <E> T select(LambdaGetter<E>... columns) {
getQueryWrapper().select(columns);
return (T) this;
}
public T select(QueryColumn[]... queryColumns) {
getQueryWrapper().select(queryColumns);
return (T) this;
}
public T where(QueryCondition queryCondition) {
getQueryWrapper().where(queryCondition);
return (T) this;
}
public T where(String sql) {
getQueryWrapper().where(sql);
return (T) this;
}
public T where(String sql, Object... params) {
getQueryWrapper().where(sql, params);
return (T) this;
}
public <E> WhereBuilder<T> where(LambdaGetter<E> column) {
return new WhereBuilder<>((T) this, LambdaUtil.getQueryColumn(column), SqlConnector.AND);
}
public T and(QueryCondition queryCondition) {
getQueryWrapper().and(queryCondition);
return (T) this;
}
public T and(String sql) {
getQueryWrapper().and(sql);
return (T) this;
}
public T and(String sql, Object... params) {
getQueryWrapper().and(sql, params);
return (T) this;
}
public <E> WhereBuilder<T> and(LambdaGetter<E> column) {
return new WhereBuilder<>((T) this, LambdaUtil.getQueryColumn(column), SqlConnector.AND);
}
public T or(QueryCondition queryCondition) {
getQueryWrapper().or(queryCondition);
return (T) this;
}
public T or(String sql) {
getQueryWrapper().or(sql);
return (T) this;
}
public T or(String sql, Object... params) {
getQueryWrapper().or(sql, params);
return (T) this;
}
public <E> WhereBuilder<T> or(LambdaGetter<E> column) {
return new WhereBuilder<>((T) this, LambdaUtil.getQueryColumn(column), SqlConnector.OR);
}
public T groupBy(String... names) {
getQueryWrapper().groupBy(names);
return (T) this;
}
public T groupBy(QueryColumn... columns) {
getQueryWrapper().groupBy(columns);
return (T) this;
}
public <E> T groupBy(LambdaGetter<E>... columns) {
getQueryWrapper().groupBy(columns);
return (T) this;
}
public T having(QueryCondition queryCondition) {
getQueryWrapper().having(queryCondition);
return (T) this;
}
public T orderBy(QueryOrderBy... orderBys) {
getQueryWrapper().orderBy(orderBys);
return (T) this;
}
public T orderBy(String... orderBys) {
getQueryWrapper().orderBy(orderBys);
return (T) this;
}
public <E> OrderByBuilder<T> orderBy(LambdaGetter<E> column) {
return new OrderByBuilder<>((T) this, column);
}
public T limit(Integer rows) {
getQueryWrapper().limit(rows);
return (T) this;
}
public T offset(Integer offset) {
getQueryWrapper().offset(offset);
return (T) this;
}
public T limit(Integer offset, Integer rows) {
getQueryWrapper().limit(offset, rows);
return (T) this;
}
}

View File

@ -0,0 +1,352 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.activerecord.query;
import com.mybatisflex.core.query.CPI;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.SqlConnector;
import com.mybatisflex.core.util.LambdaGetter;
import com.mybatisflex.core.util.LambdaUtil;
import java.util.Collection;
import java.util.function.Predicate;
/**
* Lambda 条件构建器
*
* @author 王帅
* @since 2023-07-24
*/
public class WhereBuilder<R extends QueryModel<R>> {
private final R queryModel;
private final QueryColumn queryColumn;
private final SqlConnector connector;
public WhereBuilder(R queryModel, QueryColumn queryColumn, SqlConnector connector) {
this.queryModel = queryModel;
this.queryColumn = queryColumn;
this.connector = connector;
}
public R eq(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.eq(value), connector);
}
return queryModel;
}
public <T> R eq(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.eq(value, when), connector);
}
return queryModel;
}
public <T> R eq(LambdaGetter<T> value) {
return eq(LambdaUtil.getQueryColumn(value));
}
public <T> R eq(LambdaGetter<T> value, Predicate<T> when) {
return eq(LambdaUtil.getQueryColumn(value), when);
}
public R ne(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.ne(value), connector);
}
return queryModel;
}
public <T> R ne(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.ne(value, when), connector);
}
return queryModel;
}
public <T> R ne(LambdaGetter<T> value) {
return ne(LambdaUtil.getQueryColumn(value));
}
public <T> R ne(LambdaGetter<T> value, Predicate<T> when) {
return ne(LambdaUtil.getQueryColumn(value), when);
}
public R like(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.like(value), connector);
}
return queryModel;
}
public <T> R like(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.like(value, when), connector);
}
return queryModel;
}
public R likeLeft(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.likeLeft(value), connector);
}
return queryModel;
}
public <T> R likeLeft(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.likeLeft(value, when), connector);
}
return queryModel;
}
public R likeRight(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.likeRight(value), connector);
}
return queryModel;
}
public <T> R likeRight(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.likeRight(value, when), connector);
}
return queryModel;
}
public R gt(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.gt(value), connector);
}
return queryModel;
}
public <T> R gt(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.gt(value, when), connector);
}
return queryModel;
}
public <T> R gt(LambdaGetter<T> value) {
return gt(LambdaUtil.getQueryColumn(value));
}
public <T> R gt(LambdaGetter<T> value, Predicate<T> when) {
return gt(LambdaUtil.getQueryColumn(value), when);
}
public R ge(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.ge(value), connector);
}
return queryModel;
}
public <T> R ge(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.ge(value, when), connector);
}
return queryModel;
}
public <T> R ge(LambdaGetter<T> value) {
return ge(LambdaUtil.getQueryColumn(value));
}
public <T> R ge(LambdaGetter<T> value, Predicate<T> when) {
return ge(LambdaUtil.getQueryColumn(value), when);
}
public R lt(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.lt(value), connector);
}
return queryModel;
}
public <T> R lt(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.lt(value, when), connector);
}
return queryModel;
}
public <T> R lt(LambdaGetter<T> value) {
return lt(LambdaUtil.getQueryColumn(value));
}
public <T> R lt(LambdaGetter<T> value, Predicate<T> when) {
return lt(LambdaUtil.getQueryColumn(value), when);
}
public R le(Object value) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.le(value), connector);
}
return queryModel;
}
public <T> R le(Object value, Predicate<T> when) {
if (value != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.le(value, when), connector);
}
return queryModel;
}
public <T> R le(LambdaGetter<T> value) {
return le(LambdaUtil.getQueryColumn(value));
}
public <T> R le(LambdaGetter<T> value, Predicate<T> when) {
return le(LambdaUtil.getQueryColumn(value), when);
}
public R isNull() {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.isNull(), connector);
return queryModel;
}
public <T> R isNull(Predicate<T> when) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.isNull(when), connector);
return queryModel;
}
public R isNotNull() {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.isNotNull(), connector);
return queryModel;
}
public <T> R isNotNull(Predicate<T> when) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.isNotNull(when), connector);
return queryModel;
}
public R in(Object... arrays) {
if (arrays != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(arrays), connector);
}
return queryModel;
}
public <T> R in(Object[] arrays, Predicate<T> when) {
//忽略 QueryWrapper.in("name", null) 的情况
if (arrays != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(arrays, when), connector);
}
return queryModel;
}
public R in(R queryModel) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(queryModel), connector);
}
return this.queryModel;
}
public <T> R in(R queryModel, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(queryModel, when), connector);
}
return this.queryModel;
}
public R in(Collection<?> collection) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(collection), connector);
}
return queryModel;
}
public <T> R in(Collection<?> collection, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.in(collection, when), connector);
}
return queryModel;
}
public R notIn(Object... arrays) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(arrays), connector);
}
return queryModel;
}
public <T> R notIn(Object[] arrays, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(arrays, when), connector);
}
return queryModel;
}
public R notIn(Collection<?> collection) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(collection), connector);
}
return queryModel;
}
public <T> R notIn(Collection<?> collection, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(collection, when), connector);
}
return queryModel;
}
public R notIn(R queryModel) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(queryModel), connector);
}
return this.queryModel;
}
public <T> R notIn(R queryModel, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notIn(queryModel, when), connector);
}
return this.queryModel;
}
public R between(Object start, Object end) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.between(start, end), connector);
}
return queryModel;
}
public <T> R between(Object start, Object end, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.between(start, end, when), connector);
}
return queryModel;
}
public R notBetween(Object start, Object end) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notBetween(start, end), connector);
}
return queryModel;
}
public <T> R notBetween(Object start, Object end, Predicate<T> when) {
if (queryModel != null) {
CPI.addWhereQueryCondition(queryModel.getQueryWrapper(), queryColumn.notBetween(start, end, when), connector);
}
return queryModel;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 条件查询功能
*/
package com.mybatisflex.core.activerecord.query;

View File

@ -23,10 +23,14 @@ import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.util.MapUtil;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -77,6 +81,8 @@ public class Mappers {
static class MapperHandler implements InvocationHandler {
private static final Set<String> ignoreMethods = new HashSet<>(Arrays.asList("queryChain","updateChain"));
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private Class<?> mapperClass;
private final SqlSessionFactory sqlSessionFactory = FlexGlobalConfig.getDefaultConfig().getSqlSessionFactory();
private final ExecutorType executorType = FlexGlobalConfig.getDefaultConfig().getConfiguration().getDefaultExecutorType();

View File

@ -125,6 +125,10 @@ public class CPI {
return queryWrapper.getWhereQueryCondition();
}
public static void addWhereQueryCondition(QueryWrapper queryWrapper, QueryCondition queryCondition, SqlConnector connector) {
queryWrapper.addWhereQueryCondition(queryCondition, connector);
}
public static List<QueryColumn> getGroupByColumns(QueryWrapper queryWrapper) {
return queryWrapper.getGroupByColumns();
}

View File

@ -29,28 +29,16 @@ import java.util.Optional;
* @author 王帅
* @since 2023-07-22
*/
public class QueryWrapperChain<T> extends QueryWrapperAdapter<QueryWrapperChain<T>> {
public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> {
private final BaseMapper<T> baseMapper;
public QueryWrapperChain(BaseMapper<T> baseMapper) {
public QueryChain(BaseMapper<T> baseMapper) {
this.baseMapper = baseMapper;
}
public static <E> QueryWrapperChain<E> create(BaseMapper<E> baseMapper) {
return new QueryWrapperChain<>(baseMapper);
}
public boolean remove() {
return SqlUtil.toBool(baseMapper.deleteByQuery(this));
}
public boolean update(T entity) {
return SqlUtil.toBool(baseMapper.updateByQuery(entity, this));
}
public boolean update(T entity, boolean ignoreNulls) {
return SqlUtil.toBool(baseMapper.updateByQuery(entity, ignoreNulls, this));
public static <E> QueryChain<E> of(BaseMapper<E> baseMapper) {
return new QueryChain<>(baseMapper);
}
public long count() {

View File

@ -579,6 +579,18 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
return this;
}
public <T> QueryWrapper groupBy(LambdaGetter<T> column) {
addGroupByColumns(LambdaUtil.getQueryColumn(column));
return this;
}
public <T> QueryWrapper groupBy(LambdaGetter<T>... columns) {
for (LambdaGetter<T> column : columns) {
groupBy(LambdaUtil.getQueryColumn(column));
}
return this;
}
public QueryWrapper having(QueryCondition queryCondition) {
addHavingQueryCondition(queryCondition, SqlConnector.AND);
return this;

View File

@ -536,6 +536,18 @@ public class QueryWrapperAdapter<R extends QueryWrapperAdapter<R>> extends Query
return (R) this;
}
@Override
public <T> R groupBy(LambdaGetter<T> column) {
super.groupBy(column);
return (R) this;
}
@Override
public <T> R groupBy(LambdaGetter<T>... columns) {
super.groupBy(columns);
return (R) this;
}
@Override
public R having(QueryCondition queryCondition) {
super.having(queryCondition);

View File

@ -20,7 +20,7 @@ import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.query.QueryWrapperChain;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.CollectionUtil;
@ -529,8 +529,8 @@ public interface IService<T> {
return QueryWrapper.create();
}
default QueryWrapperChain<T> queryChain() {
return new QueryWrapperChain<>(getMapper());
default QueryChain<T> queryChain() {
return new QueryChain<>(getMapper());
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.update;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.mybatis.Mappers;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapperAdapter;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.LambdaGetter;
import com.mybatisflex.core.util.SqlUtil;
import com.mybatisflex.core.util.UpdateEntity;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* 用于数据更新删除的链式操作
*
* @author michale
* @since 2023-07-25
*/
public class UpdateChain<T> extends QueryWrapperAdapter<UpdateChain<T>> {
private final BaseMapper<T> baseMapper;
private final T entity;
private final UpdateWrapper entityWrapper;
public static <T> UpdateChain<T> of(Class<T> entityClass) {
BaseMapper<T> baseMapper = Mappers.ofEntityClass(entityClass);
return new UpdateChain<>(baseMapper);
}
public UpdateChain(BaseMapper<T> baseMapper) {
this.baseMapper = baseMapper;
this.entity = createEntity(ClassUtil.getUsefulClass(baseMapper.getClass()));
this.entityWrapper = (UpdateWrapper) entity;
}
private T createEntity(Class<?> mapperClass) {
Type type = mapperClass.getGenericInterfaces()[0];
if (type instanceof ParameterizedType) {
Class<T> modelClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
return UpdateEntity.of(modelClass);
}
throw FlexExceptions.wrap("Can not get entity class from mapper: " + mapperClass.getName());
}
public static <E> UpdateChain<E> create(BaseMapper<E> baseMapper) {
return new UpdateChain<>(baseMapper);
}
public UpdateChain<T> set(String property, Object value) {
entityWrapper.set(property, value);
return this;
}
public UpdateChain<T> set(LambdaGetter<T> getter, Object value) {
entityWrapper.set(getter, value);
return this;
}
public UpdateChain<T> set(QueryColumn queryColumn, Object value) {
entityWrapper.set(queryColumn, value);
return this;
}
public UpdateChain<T> setRaw(String property, Object value) {
entityWrapper.setRaw(property, value);
return this;
}
public UpdateChain<T> setRaw(LambdaGetter<T> getter, Object value) {
entityWrapper.setRaw(getter, value);
return this;
}
public UpdateChain<T> setRaw(QueryColumn queryColumn, Object value) {
entityWrapper.set(queryColumn, value);
return this;
}
public boolean remove() {
return SqlUtil.toBool(baseMapper.deleteByQuery(this));
}
public boolean update() {
return SqlUtil.toBool(baseMapper.updateByQuery(entity, this));
}
}

View File

@ -26,6 +26,9 @@ import org.apache.ibatis.javassist.util.proxy.ProxyObject;
import java.io.Serializable;
import java.util.Map;
/**
* @author michael
*/
public interface UpdateWrapper extends Serializable {
default Map<String, Object> getUpdates() {

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,72 @@
package com.mybatisflex.test;
import com.mybatisflex.core.MybatisFlexBootstrap;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.audit.ConsoleMessageCollector;
import com.mybatisflex.core.audit.MessageCollector;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.update.UpdateChain;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import javax.sql.DataSource;
import static com.mybatisflex.test.table.AccountTableDef.ACCOUNT;
public class UpdateChainTest {
static AccountMapper accountMapper;
@BeforeClass
public static void init() {
DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.addScript("data.sql")
.build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
.setDataSource(dataSource)
.setLogImpl(StdOutImpl.class)
.addMapper(AccountMapper.class)
.start();
//开启审计功能
AuditManager.setAuditEnable(true);
//设置 SQL 审计收集器
MessageCollector collector = new ConsoleMessageCollector();
AuditManager.setMessageCollector(collector);
accountMapper = bootstrap.getMapper(AccountMapper.class);
}
@Test
public void testUpdateChain() {
UpdateChain.of(Account.class)
.set(Account::getUserName,"张三")
.setRaw(Account::getAge,"age + 1")
.where(Account::getId).eq(1)
.update();
Account account = accountMapper.selectOneById(1);
System.out.println(account);
}
@Test
public void testUpdateChain1() {
UpdateChain.of(Account.class)
.set(Account::getAge, ACCOUNT.AGE.add(1))
.where(Account::getId).ge(100)
.and(Account::getAge).eq(18)
.update();
QueryChain.of(accountMapper)
.list()
.forEach(System.out::println);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -17,7 +17,9 @@
package com.mybatisflex.test.model;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.activerecord.Model;
import java.util.Objects;
@ -28,35 +30,42 @@ import java.util.Objects;
* @since 2023-06-07
*/
@Table("tb_good")
public class Good {
public class Good extends Model<Good> {
@Id
@Id(keyType = KeyType.Auto)
private Integer goodId;
private String name;
private double price;
private Double price;
public static Good create() {
return new Good();
}
public Integer getGoodId() {
return goodId;
}
public void setGoodId(Integer goodId) {
public Good setGoodId(Integer goodId) {
this.goodId = goodId;
return this;
}
public String getName() {
return name;
}
public void setName(String name) {
public Good setName(String name) {
this.name = name;
return this;
}
public double getPrice() {
public Double getPrice() {
return price;
}
public void setPrice(double price) {
public Good setPrice(Double price) {
this.price = price;
return this;
}
@Override
@ -79,23 +88,20 @@ public class Good {
Good good = (Good) o;
if (Double.compare(good.price, price) != 0) {
return false;
}
if (!Objects.equals(goodId, good.goodId)) {
return false;
}
return Objects.equals(name, good.name);
if (!Objects.equals(name, good.name)) {
return false;
}
return Objects.equals(price, good.price);
}
@Override
public int hashCode() {
int result;
long temp;
result = goodId != null ? goodId.hashCode() : 0;
int result = goodId != null ? goodId.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
temp = Double.doubleToLongBits(price);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + (price != null ? price.hashCode() : 0);
return result;
}

View File

@ -19,9 +19,12 @@ package com.mybatisflex.test.mapper;
import com.mybatisflex.core.mybatis.Mappers;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.test.model.Good;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static com.mybatisflex.test.model.table.GoodTableDef.GOOD;
/**
* @author 王帅
* @since 2023-07-23
@ -31,12 +34,75 @@ class ActiveRecordTest {
@Test
void testMapper() {
Good good = new Good();
good.setPrice(28);
Good good = Good.create();
GoodMapper goodMapper = Mappers.ofMapperClass(GoodMapper.class);
good.setPrice(28.0);
GoodMapper goodMapper = (GoodMapper) Mappers.ofEntityClass(Good.class);
goodMapper.selectListByQuery(QueryWrapper.create(good));
}
@Test
void testInsert() {
boolean saved = Good.create()
.setPrice(28.0)
.setName("摆渡人")
.save();
Assertions.assertTrue(saved);
}
@Test
void testUpdate() {
Good.create()
.setGoodId(11)
.setPrice(38.0)
.updateById();
}
@Test
void testDelete() {
boolean removed = Good.create()
.setGoodId(1)
.removeById();
Assertions.assertTrue(removed);
}
@Test
void testSelectById() {
Good good = Good.create()
.setGoodId(11)
.oneById();
System.out.println(good);
}
@Test
void testSelectOne() {
Good good1 = Good.create()
.setName("摆渡人")
.one();
Good good2 = Good.create()
.where(GOOD.NAME.eq("摆渡人"))
.one();
Good good3 = Good.create()
.where(Good::getName).eq("摆渡人")
.one();
Assertions.assertEquals(good1, good2);
Assertions.assertEquals(good1, good3);
}
@Test
void testSelectList() {
Good.create()
.where(GOOD.PRICE.ge(28.0))
.list()
.forEach(System.out::println);
}
}

View File

@ -4,7 +4,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -7,7 +7,7 @@
<groupId>com.mybatis-flex</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>1.5.2</version>
<version>1.5.3</version>
<name>mybatis-flex</name>
<url>https://mybatis-flex.com</url>
@ -54,7 +54,7 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mybatis-flex.version>1.5.2</mybatis-flex.version>
<mybatis-flex.version>1.5.3</mybatis-flex.version>
<mybatis.version>3.5.13</mybatis.version>
<mybatis-spring.version>2.1.0</mybatis-spring.version>