mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
commit
2562505139
@ -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 = [普通用户, 贵族用户, 超级贵族用户, 管理员用户]
|
||||
}]
|
||||
```
|
||||
|
||||
## 父子关系查询
|
||||
|
||||
比如在一些系统中,比如菜单会有一些父子关系,例如菜单表如下:
|
||||
|
||||
@ -67,6 +67,14 @@ public @interface RelationManyToMany {
|
||||
*/
|
||||
String targetField() default "";
|
||||
|
||||
/**
|
||||
* 目标对象的关系实体类的属性绑定
|
||||
* <p>
|
||||
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
|
||||
* @return 属性名称
|
||||
*/
|
||||
String targetFieldBind() default "";
|
||||
|
||||
/**
|
||||
* 当映射是一个 map 时,使用哪个内容来当做 map 的 Key
|
||||
* @return 指定的列
|
||||
|
||||
@ -67,6 +67,14 @@ public @interface RelationManyToOne {
|
||||
*/
|
||||
String targetField() default "";
|
||||
|
||||
/**
|
||||
* 目标对象的关系实体类的属性绑定
|
||||
* <p>
|
||||
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
|
||||
* @return 属性名称
|
||||
*/
|
||||
String targetFieldBind() default "";
|
||||
|
||||
/**
|
||||
* 中间表名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。
|
||||
*
|
||||
|
||||
@ -67,6 +67,14 @@ public @interface RelationOneToMany {
|
||||
*/
|
||||
String targetField();
|
||||
|
||||
/**
|
||||
* 目标对象的关系实体类的属性绑定
|
||||
* <p>
|
||||
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
|
||||
* @return 属性名称
|
||||
*/
|
||||
String targetFieldBind() default "";
|
||||
|
||||
/**
|
||||
* 当映射是一个 map 时,使用哪个内容来当做 map 的 Key
|
||||
* @return 指定的列
|
||||
|
||||
@ -67,6 +67,14 @@ public @interface RelationOneToOne {
|
||||
*/
|
||||
String targetField();
|
||||
|
||||
/**
|
||||
* 目标对象的关系实体类的属性绑定
|
||||
* <p>
|
||||
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
|
||||
* @return 属性名称
|
||||
*/
|
||||
String targetFieldBind() default "";
|
||||
|
||||
/**
|
||||
* 中间表名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。
|
||||
*
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -27,6 +27,7 @@ class OneToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
|
||||
, annotation.targetSchema()
|
||||
, annotation.targetTable()
|
||||
, annotation.targetField()
|
||||
, annotation.targetFieldBind()
|
||||
, annotation.joinTable()
|
||||
, annotation.joinSelfColumn()
|
||||
, annotation.joinTargetColumn()
|
||||
|
||||
@ -27,6 +27,7 @@ class OneToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
|
||||
, annotation.targetSchema()
|
||||
, annotation.targetTable()
|
||||
, annotation.targetField()
|
||||
, annotation.targetFieldBind()
|
||||
, annotation.joinTable()
|
||||
, annotation.joinSelfColumn()
|
||||
, annotation.joinTargetColumn()
|
||||
|
||||
@ -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)) {
|
||||
|
||||
//递归查询
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user