!344 注解关联查询支持单字段赋值

Merge pull request !344 from Ice/main
This commit is contained in:
Michael Yang 2023-09-18 00:58:18 +00:00 committed by Gitee
commit 2562505139
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
15 changed files with 311 additions and 18 deletions

View File

@ -352,6 +352,111 @@ public class Account implements Serializable {
}
```
Relation结果集只使用某个字段值-`since v1.6.6`
`RelationOneToOne``RelationOneToMany``RelationManyToOne``RelationManyToMany`新增属性`targetFieldBind`
```java {7-11}
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String targetFieldBind() default "";
```
> 注解其他属性配置使用不变,当配置了`targetFieldBind`值时,只提取目标对象关系实体类的该属性
>
> 注意:因为不是对象接收,所以该配置需要强制配置注解`targetTable`属性(因为是某个字段接收,并不是某个实体对应的表,所以需要增加`targetTable`获取目标表信息)
>
> 示例场景有个业务有个操作日志操作日志中有个createBy(操作人字段),此时在日志详情或者说日志列表中需要显示操作人名称,且只需要这一个字段,此时使用实体接收会导致不必要的字段出现,接口文档也会变得混乱。该场景也可以使用`Field Query``Join Query`实现
>
假设一个账户
- 每个账户有一个唯一对应的`id_number`列在表`tb_id_card`
- 每个账户拥有多个订单`tb_user_order`
- 一个账户可以有多个角色,一个角色也可以分配给多个账户,他们通过中间表`tb_user_role`进行关系映射
```java {7-11}
@Table("tb_user")
public class UserVO5 implements Serializable {
private static final long serialVersionUID = 474700189859144273L;
@Id
private Integer userId;
private String userName;
private String password;
@RelationOneToOne(
selfField = "userId",
targetTable = "tb_id_card",
targetField = "id",
targetFieldBind = "idNumber"
)
//该处可以定义其他属性名,不一定要是目标对象的字段名
private String idNumberCustomFieldName;
@RelationOneToMany(
selfField = "userId",
targetTable = "tb_user_order",
targetField = "userId",
targetFieldBind = "orderId"
)
private List<Integer> orderIdList;
@RelationManyToMany(
selfField = "userId",
targetTable = "tb_role",
targetField = "roleId",
targetFieldBind = "roleName",
joinTable = "tb_user_role",
joinSelfColumn = "user_id",
joinTargetColumn = "role_id"
)
private List<String> roleNameList;
//getter setter toString
}
```
进行查询
```java
List<UserVO5> userVO5List = userMapper.selectListWithRelationsByQueryAs(QueryWrapper.create(), UserVO5.class);
System.out.println(userVO5List);
```
输出结果
```json
[{
userId = 1,
userName = '张三',
password = '12345678',
idNumberCustomFieldName = 'F281C807-C40B-472D-82F5-6130199C6328',
orderIdList = [1],
roleNameList = [普通用户]
},
{
userId = 2,
userName = '李四',
password = '87654321',
idNumberCustomFieldName = '6176E9AD-36EF-4201-A5F7-CCE89B254952',
orderIdList = [3, 2],
roleNameList = [普通用户, 贵族用户]
},
{
userId = 3,
userName = '王五',
password = '09897654',
idNumberCustomFieldName = 'A038E6EA-1FDE-4191-AA41-06F78E91F6C2',
orderIdList = [6, 5, 4],
roleNameList = [普通用户, 贵族用户, 超级贵族用户]
},
{
userId = 4,
userName = '苏六',
password = '45678345',
idNumberCustomFieldName = 'A33E8BAA-93F2-4E28-A161-15CF7D0AE6D0',
orderIdList = [],
roleNameList = [普通用户, 贵族用户, 超级贵族用户, 管理员用户]
}]
```
## 父子关系查询
比如在一些系统中,比如菜单会有一些父子关系,例如菜单表如下:

View File

@ -67,6 +67,14 @@ public @interface RelationManyToMany {
*/
String targetField() default "";
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String targetFieldBind() default "";
/**
* 当映射是一个 map 使用哪个内容来当做 map Key
* @return 指定的列

View File

@ -67,6 +67,14 @@ public @interface RelationManyToOne {
*/
String targetField() default "";
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String targetFieldBind() default "";
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*

View File

@ -67,6 +67,14 @@ public @interface RelationOneToMany {
*/
String targetField();
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String targetFieldBind() default "";
/**
* 当映射是一个 map 使用哪个内容来当做 map Key
* @return 指定的列

View File

@ -67,6 +67,14 @@ public @interface RelationOneToOne {
*/
String targetField();
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String targetFieldBind() default "";
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*

View File

@ -46,6 +46,8 @@ abstract class AbstractRelation<SelfEntity> {
protected String targetSchema;
protected String targetTable;
protected Field targetField;
protected String targetFieldBind;
protected boolean onlyTargetFieldBind;
protected Class<?> targetEntityClass;
protected TableInfo targetTableInfo;
protected FieldWrapper targetFieldWrapper;
@ -62,7 +64,7 @@ abstract class AbstractRelation<SelfEntity> {
protected QueryColumn conditionColumn;
protected String[] selectColumns;
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField,
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, String targetFieldBind,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> entityClass, Field relationField,
String extraCondition, String[] selectColumns
@ -82,25 +84,33 @@ abstract class AbstractRelation<SelfEntity> {
this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField));
this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField);
this.targetEntityClass = relationFieldWrapper.getMappingType();
//以使用者注解配置为主
this.targetTableInfo = StringUtil.isBlank(targetTable) ? TableInfoFactory.ofEntityClass(relationFieldWrapper.getMappingType()) : TableInfoFactory.ofTableName(targetTable);
this.targetEntityClass = targetTableInfo != null ? targetTableInfo.getEntityClass() : relationFieldWrapper.getMappingType();
this.targetSchema = targetSchema;
this.targetTable = targetTable;
this.targetTable = targetTableInfo != null ? targetTableInfo.getTableName() : targetTable;
this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equals(targetField));
this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass);
this.targetFieldBind = targetFieldBind;
this.onlyTargetFieldBind = StringUtil.isNotBlank(targetFieldBind);
this.conditionColumn = column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName()));
if (ArrayUtil.isNotEmpty(selectColumns)) {
if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) {
this.selectColumns = selectColumns;
} else {
//需要追加 conditionColumn因为进行内存 join 的时候需要用到这个内容进行对比
this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()});
if (onlyTargetFieldBind) {
//仅绑定字段时只需要查询关联列和该字段列即可
this.selectColumns = new String[]{conditionColumn.getName(), targetTableInfo != null ? targetTableInfo.getColumnByProperty(this.targetFieldBind) : StringUtil.camelToUnderline(this.targetFieldBind)};
} else {
if (ArrayUtil.isNotEmpty(selectColumns)) {
if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) {
this.selectColumns = selectColumns;
} else {
//需要追加 conditionColumn因为进行内存 join 的时候需要用到这个内容进行对比
this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()});
}
}
}
initExtraCondition(extraCondition);
@ -249,6 +259,22 @@ abstract class AbstractRelation<SelfEntity> {
this.targetTable = targetTable;
}
public String getTargetFieldBind() {
return targetFieldBind;
}
public void setTargetFieldBind(String targetFieldBind) {
this.targetFieldBind = targetFieldBind;
}
public boolean isOnlyTargetFieldBind() {
return onlyTargetFieldBind;
}
public void setOnlyTargetFieldBind(boolean onlyTargetFieldBind) {
this.onlyTargetFieldBind = onlyTargetFieldBind;
}
public String getJoinTable() {
return joinTable;
}

View File

@ -26,6 +26,7 @@ class ManyToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField), "@RelationManyToMany.targetField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.targetFieldBind()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class ManyToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField)
, "@RelationManyToOne.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.targetFieldBind()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class OneToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, annotation.targetField()
, annotation.targetFieldBind()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class OneToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, annotation.targetField()
, annotation.targetFieldBind()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -359,8 +359,9 @@ public class RelationManager {
DataSourceKey.use(configDsKey);
}
//仅绑定字段:As目标实体类 不进行字段绑定:As映射类型
QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues);
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.getMappingType());
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.isOnlyTargetFieldBind() ? relation.getTargetEntityClass() : relation.getMappingType());
if (CollectionUtil.isNotEmpty(targetObjectList)) {
//递归查询

View File

@ -31,11 +31,11 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
protected long limit = 0;
public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField,
public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField, String targetFieldBind,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField,
String extraCondition, String[] selectColumns) {
super(selfField, targetSchema, targetTable, targetField,
super(selfField, targetSchema, targetTable, targetField, targetFieldBind,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
extraCondition, selectColumns
@ -101,7 +101,12 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
collection.add(targetObject);
if (onlyTargetFieldBind) {
//仅绑定某个字段
collection.add(FieldWrapper.of(targetObject.getClass(), targetFieldBind).get(targetObject));
} else {
collection.add(targetObject);
}
}
}
relationFieldWrapper.set(collection, selfEntity);

View File

@ -16,6 +16,7 @@
package com.mybatisflex.core.relation;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.util.FieldWrapper;
import java.lang.reflect.Field;
import java.util.List;
@ -23,10 +24,10 @@ import java.util.List;
class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField,
public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField, String targetFieldBind,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField, String[] selectColumns) {
super(selfField, targetSchema, targetTable, targetField,
super(selfField, targetSchema, targetTable, targetField, targetFieldBind,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
null, selectColumns
@ -53,7 +54,12 @@ class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValue.equals(targetValue.toString())) {
relationFieldWrapper.set(targetObject, selfEntity);
if (onlyTargetFieldBind) {
//仅绑定某个字段
relationFieldWrapper.set(FieldWrapper.of(targetObject.getClass(), targetFieldBind).get(targetObject), selfEntity);
} else {
relationFieldWrapper.set(targetObject, selfEntity);
}
break;
}
}

View File

@ -0,0 +1,108 @@
package com.mybatisflex.test.model;
import com.mybatisflex.annotation.*;
import java.io.Serializable;
import java.util.List;
/**
* 字段绑定测试
* @author Ice 2023/09/16
* @version 1.0
*/
@Table("tb_user")
public class UserVO5 implements Serializable {
private static final long serialVersionUID = 474700189859144273L;
@Id
private Integer userId;
private String userName;
private String password;
@RelationOneToOne(
selfField = "userId",
targetTable = "tb_id_card",
targetField = "id",
targetFieldBind = "idNumber"
)
private String idNumberCustomFieldName;
@RelationOneToMany(
selfField = "userId",
targetTable = "tb_user_order",
targetField = "userId",
targetFieldBind = "orderId"
)
private List<Integer> orderIdList;
@RelationManyToMany(
selfField = "userId",
targetTable = "tb_role",
targetField = "roleId",
targetFieldBind = "roleName",
joinTable = "tb_user_role",
joinSelfColumn = "user_id",
joinTargetColumn = "role_id"
)
private List<String> roleNameList;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getIdNumberCustomFieldName() {
return idNumberCustomFieldName;
}
public void setIdNumberCustomFieldName(String idNumberCustomFieldName) {
this.idNumberCustomFieldName = idNumberCustomFieldName;
}
public List<Integer> getOrderIdList() {
return orderIdList;
}
public void setOrderIdList(List<Integer> orderIdList) {
this.orderIdList = orderIdList;
}
public List<String> getRoleNameList() {
return roleNameList;
}
public void setRoleNameList(List<String> roleNameList) {
this.roleNameList = roleNameList;
}
@Override
public String toString() {
return "UserVO5{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", idNumberCustomFieldName='" + idNumberCustomFieldName + '\'' +
", orderIdList=" + orderIdList +
", roleNameList=" + roleNameList +
'}';
}
}

View File

@ -280,4 +280,10 @@ class UserMapperTest {
System.err.println(user);
}
@Test
public void testFieldBindRelations() {
List<UserVO5> userVO5List = userMapper.selectListWithRelationsByQueryAs(QueryWrapper.create(), UserVO5.class);
System.out.println(userVO5List);
}
}