mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 09:38:26 +08:00
rename @As to @ColumnAlias and optimize TableInfo.doBuildResultMap
This commit is contained in:
parent
6ad26ff398
commit
5f14bd96a7
@ -185,7 +185,7 @@ System.out.println(results);
|
|||||||
- 1、在 `ArticleDTO` 和 `Account` 这两个类中,如果他们有相同的字段,`Account` 中的字段将和 `ArticleDTO` 中的字段拥有一样的值。
|
- 1、在 `ArticleDTO` 和 `Account` 这两个类中,如果他们有相同的字段,`Account` 中的字段将和 `ArticleDTO` 中的字段拥有一样的值。
|
||||||
- 2、假设在 `ArticleDTO` 中有多个类似 `Account` 的对象,且他们有相同的字段(字段和 `ArticleDTO`
|
- 2、假设在 `ArticleDTO` 中有多个类似 `Account` 的对象,且他们有相同的字段(字段和 `ArticleDTO`
|
||||||
中的不相同),所有相同属性名的值与优先定义的属性的值相同。
|
中的不相同),所有相同属性名的值与优先定义的属性的值相同。
|
||||||
- 3、解决这种情况需要在重名的属性上添加 `@As` 注解指定别名(如果是继承的属性,需要把 `@As` 注解放到对应的 `getter`
|
- 3、解决这种情况需要在重名的属性上添加 `@ColumnAlias` 注解指定别名(如果是继承的属性,需要把 `@ColumnAlias` 注解放到对应的 `getter`
|
||||||
方法上),这样在查询的时候就会为重名字段自动添加 `AS` 别名。
|
方法上),这样在查询的时候就会为重名字段自动添加 `AS` 别名。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@ -27,10 +27,10 @@ import java.lang.annotation.*;
|
|||||||
@Inherited
|
@Inherited
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||||
public @interface As {
|
public @interface ColumnAlias {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列的别名。
|
* 列的别名,在查询的时候,查询 sql 会自动添加 as ...
|
||||||
*
|
*
|
||||||
* @return 别名
|
* @return 别名
|
||||||
*/
|
*/
|
||||||
@ -788,19 +788,20 @@ public class TableInfo {
|
|||||||
|
|
||||||
|
|
||||||
public ResultMap buildResultMap(Configuration configuration) {
|
public ResultMap buildResultMap(Configuration configuration) {
|
||||||
return doBuildResultMap(configuration, new HashSet<>());
|
return doBuildResultMap(configuration, new HashSet<>(), new HashSet<>(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultMap doBuildResultMap(Configuration configuration, Set<String> context) {
|
private ResultMap doBuildResultMap(Configuration configuration, Set<String> resultMapIds, Set<String> existMappingColumns, boolean isNested) {
|
||||||
|
|
||||||
|
String resultMapId = isNested ? "nested:" + entityClass.getName() : entityClass.getName();
|
||||||
|
|
||||||
//是否有循环引用
|
//是否有循环引用
|
||||||
boolean withCircularReference = context.contains(entityClass.getName());
|
boolean withCircularReference = resultMapIds.contains(resultMapId);
|
||||||
if (withCircularReference) {
|
if (withCircularReference) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultMapId = entityClass.getName();
|
resultMapIds.add(resultMapId);
|
||||||
context.add(resultMapId);
|
|
||||||
|
|
||||||
if (configuration.hasResultMap(resultMapId)) {
|
if (configuration.hasResultMap(resultMapId)) {
|
||||||
return configuration.getResultMap(resultMapId);
|
return configuration.getResultMap(resultMapId);
|
||||||
@ -810,70 +811,88 @@ public class TableInfo {
|
|||||||
// <resultMap> 标签下的 <result> 标签映射
|
// <resultMap> 标签下的 <result> 标签映射
|
||||||
for (ColumnInfo columnInfo : columnInfoList) {
|
for (ColumnInfo columnInfo : columnInfoList) {
|
||||||
// add column mapping
|
// add column mapping
|
||||||
ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property,
|
ResultMapping mapping = new ResultMapping.Builder(configuration
|
||||||
columnInfo.column, columnInfo.propertyType)
|
, columnInfo.property
|
||||||
|
, isNested && existMappingColumns.contains(columnInfo.column) ? tableName + "$" + columnInfo.column : columnInfo.column
|
||||||
|
, columnInfo.propertyType)
|
||||||
.jdbcType(columnInfo.getJdbcType())
|
.jdbcType(columnInfo.getJdbcType())
|
||||||
.typeHandler(columnInfo.buildTypeHandler())
|
.typeHandler(columnInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
resultMappings.add(mapping);
|
resultMappings.add(mapping);
|
||||||
|
existMappingColumns.add(mapping.getColumn());
|
||||||
|
|
||||||
if (ArrayUtil.isNotEmpty(columnInfo.alias)) {
|
if (ArrayUtil.isNotEmpty(columnInfo.alias)) {
|
||||||
// add alias mapping
|
// add alias mapping
|
||||||
for (String alias : columnInfo.alias) {
|
for (String alias : columnInfo.alias) {
|
||||||
ResultMapping aliasMapping = new ResultMapping.Builder(configuration, columnInfo.property,
|
ResultMapping aliasMapping = new ResultMapping.Builder(configuration
|
||||||
alias, columnInfo.propertyType)
|
, columnInfo.property
|
||||||
|
, isNested && existMappingColumns.contains(alias) ? tableName + "$" + alias : alias
|
||||||
|
, columnInfo.propertyType)
|
||||||
.jdbcType(columnInfo.getJdbcType())
|
.jdbcType(columnInfo.getJdbcType())
|
||||||
.typeHandler(columnInfo.buildTypeHandler())
|
.typeHandler(columnInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(aliasMapping);
|
resultMappings.add(aliasMapping);
|
||||||
|
existMappingColumns.add(aliasMapping.getColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add property mapper for sql: select xxx as property ...
|
// add property mapper for sql: select xxx as property ...
|
||||||
if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
|
if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
|
||||||
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property,
|
ResultMapping propertyMapping = new ResultMapping.Builder(configuration
|
||||||
columnInfo.property, columnInfo.propertyType)
|
, columnInfo.property
|
||||||
|
, isNested && existMappingColumns.contains(columnInfo.property) ? tableName + "$" + columnInfo.property : columnInfo.property
|
||||||
|
, columnInfo.propertyType)
|
||||||
.jdbcType(columnInfo.getJdbcType())
|
.jdbcType(columnInfo.getJdbcType())
|
||||||
.typeHandler(columnInfo.buildTypeHandler())
|
.typeHandler(columnInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(propertyMapping);
|
resultMappings.add(propertyMapping);
|
||||||
|
existMappingColumns.add(propertyMapping.getColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <resultMap> 标签下的 <id> 标签映射
|
// <resultMap> 标签下的 <id> 标签映射
|
||||||
for (IdInfo idInfo : primaryKeyList) {
|
for (IdInfo idInfo : primaryKeyList) {
|
||||||
|
ResultMapping mapping = new ResultMapping.Builder(configuration
|
||||||
ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property,
|
, idInfo.property
|
||||||
idInfo.column, idInfo.propertyType)
|
, isNested && existMappingColumns.contains(idInfo.column) ? tableName + "$" + idInfo.column : idInfo.column
|
||||||
|
, idInfo.propertyType)
|
||||||
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
||||||
.jdbcType(idInfo.getJdbcType())
|
.jdbcType(idInfo.getJdbcType())
|
||||||
.typeHandler(idInfo.buildTypeHandler())
|
.typeHandler(idInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(mapping);
|
resultMappings.add(mapping);
|
||||||
|
existMappingColumns.add(mapping.getColumn());
|
||||||
|
|
||||||
|
|
||||||
if (ArrayUtil.isNotEmpty(idInfo.alias)) {
|
if (ArrayUtil.isNotEmpty(idInfo.alias)) {
|
||||||
// add alias mapping
|
// add alias mapping
|
||||||
for (String alias : idInfo.alias) {
|
for (String alias : idInfo.alias) {
|
||||||
ResultMapping aliasMapping = new ResultMapping.Builder(configuration, idInfo.property,
|
ResultMapping aliasMapping = new ResultMapping.Builder(configuration
|
||||||
alias, idInfo.propertyType)
|
, idInfo.property
|
||||||
|
, isNested && existMappingColumns.contains(alias) ? tableName + "$" + alias : alias
|
||||||
|
, idInfo.propertyType)
|
||||||
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
||||||
.jdbcType(idInfo.getJdbcType())
|
.jdbcType(idInfo.getJdbcType())
|
||||||
.typeHandler(idInfo.buildTypeHandler())
|
.typeHandler(idInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(aliasMapping);
|
resultMappings.add(aliasMapping);
|
||||||
|
existMappingColumns.add(aliasMapping.getColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add property mapper for sql: select xxx as property ...
|
// add property mapper for sql: select xxx as property ...
|
||||||
if (!Objects.equals(idInfo.getColumn(), idInfo.getProperty())) {
|
if (!Objects.equals(idInfo.getColumn(), idInfo.getProperty())) {
|
||||||
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, idInfo.property,
|
ResultMapping propertyMapping = new ResultMapping.Builder(configuration
|
||||||
idInfo.property, idInfo.propertyType)
|
, idInfo.property
|
||||||
|
, isNested && existMappingColumns.contains(idInfo.property) ? tableName + "$" + idInfo.property : idInfo.property
|
||||||
|
, idInfo.propertyType)
|
||||||
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
||||||
.jdbcType(idInfo.getJdbcType())
|
.jdbcType(idInfo.getJdbcType())
|
||||||
.typeHandler(idInfo.buildTypeHandler())
|
.typeHandler(idInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(propertyMapping);
|
resultMappings.add(propertyMapping);
|
||||||
|
existMappingColumns.add(propertyMapping.getColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,7 +902,7 @@ public class TableInfo {
|
|||||||
// 获取嵌套类型的信息,也就是 javaType 属性
|
// 获取嵌套类型的信息,也就是 javaType 属性
|
||||||
TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
|
TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
|
||||||
// 构建嵌套类型的 ResultMap 对象,也就是 <association> 标签下的内容
|
// 构建嵌套类型的 ResultMap 对象,也就是 <association> 标签下的内容
|
||||||
ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, context);
|
ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, resultMapIds, existMappingColumns, true);
|
||||||
if (nestedResultMap != null) {
|
if (nestedResultMap != null) {
|
||||||
resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
|
resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
|
||||||
.javaType(fieldType)
|
.javaType(fieldType)
|
||||||
@ -916,7 +935,7 @@ public class TableInfo {
|
|||||||
// 获取集合泛型类型的信息,也就是 ofType 属性
|
// 获取集合泛型类型的信息,也就是 ofType 属性
|
||||||
TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
|
TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
|
||||||
// 构建嵌套类型的 ResultMap 对象,也就是 <collection> 标签下的内容
|
// 构建嵌套类型的 ResultMap 对象,也就是 <collection> 标签下的内容
|
||||||
ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, context);
|
ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, resultMapIds, existMappingColumns, true);
|
||||||
if (nestedResultMap != null) {
|
if (nestedResultMap != null) {
|
||||||
resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
|
resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
|
||||||
.javaType(field.getType())
|
.javaType(field.getType())
|
||||||
@ -929,7 +948,7 @@ public class TableInfo {
|
|||||||
|
|
||||||
ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
|
ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
|
||||||
configuration.addResultMap(resultMap);
|
configuration.addResultMap(resultMap);
|
||||||
context.add(resultMapId);
|
resultMapIds.add(resultMapId);
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -287,19 +287,19 @@ public class TableInfoFactory {
|
|||||||
columnInfoList.add(columnInfo);
|
columnInfoList.add(columnInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
As asType = null;
|
ColumnAlias columnAlias = null;
|
||||||
// 属性上没有别名,查找 getter 方法上有没有别名
|
// 属性上没有别名,查找 getter 方法上有没有别名
|
||||||
Method getterMethod = ClassUtil.getFirstMethod(entityClass, m -> ClassUtil.isGetterMethod(m, field.getName()));
|
Method getterMethod = ClassUtil.getFirstMethod(entityClass, m -> ClassUtil.isGetterMethod(m, field.getName()));
|
||||||
if (getterMethod != null) {
|
if (getterMethod != null) {
|
||||||
asType = getterMethod.getAnnotation(As.class);
|
columnAlias = getterMethod.getAnnotation(ColumnAlias.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asType == null) {
|
if (columnAlias == null) {
|
||||||
asType = field.getAnnotation(As.class);
|
columnAlias = field.getAnnotation(ColumnAlias.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asType != null) {
|
if (columnAlias != null) {
|
||||||
columnInfo.setAlias(asType.value());
|
columnInfo.setAlias(columnAlias.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
columnInfo.setColumn(columnName);
|
columnInfo.setColumn(columnName);
|
||||||
@ -362,6 +362,7 @@ public class TableInfoFactory {
|
|||||||
if (!largeColumns.isEmpty()) {
|
if (!largeColumns.isEmpty()) {
|
||||||
tableInfo.setLargeColumns(largeColumns.toArray(new String[0]));
|
tableInfo.setLargeColumns(largeColumns.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defaultColumns.isEmpty()) {
|
if (!defaultColumns.isEmpty()) {
|
||||||
tableInfo.setDefaultColumns(defaultColumns.toArray(new String[0]));
|
tableInfo.setDefaultColumns(defaultColumns.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.mybatisflex.processor;
|
package com.mybatisflex.processor;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.As;
|
import com.mybatisflex.annotation.ColumnAlias;
|
||||||
import com.mybatisflex.annotation.Column;
|
import com.mybatisflex.annotation.Column;
|
||||||
import com.mybatisflex.annotation.Table;
|
import com.mybatisflex.annotation.Table;
|
||||||
import com.mybatisflex.processor.builder.ContentBuilder;
|
import com.mybatisflex.processor.builder.ContentBuilder;
|
||||||
@ -293,9 +293,9 @@ public class MybatisFlexProcessor extends AbstractProcessor {
|
|||||||
|
|
||||||
String[] alias = getColumnAliasByGetterMethod(enclosedElements, property);
|
String[] alias = getColumnAliasByGetterMethod(enclosedElements, property);
|
||||||
if (alias == null || alias.length == 0) {
|
if (alias == null || alias.length == 0) {
|
||||||
As asType = fieldElement.getAnnotation(As.class);
|
ColumnAlias columnAlias = fieldElement.getAnnotation(ColumnAlias.class);
|
||||||
if (asType != null) {
|
if (columnAlias != null) {
|
||||||
alias = asType.value();
|
alias = columnAlias.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ public class MybatisFlexProcessor extends AbstractProcessor {
|
|||||||
if (ElementKind.METHOD == enclosedElement.getKind()) {
|
if (ElementKind.METHOD == enclosedElement.getKind()) {
|
||||||
String methodName = enclosedElement.toString();
|
String methodName = enclosedElement.toString();
|
||||||
if (StrUtil.isGetterMethod(methodName, property)) {
|
if (StrUtil.isGetterMethod(methodName, property)) {
|
||||||
As asType = enclosedElement.getAnnotation(As.class);
|
ColumnAlias asType = enclosedElement.getAnnotation(ColumnAlias.class);
|
||||||
if (asType != null) {
|
if (asType != null) {
|
||||||
return asType.value();
|
return asType.value();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
|
|||||||
@ColumnMask(Masks.CHINESE_NAME)
|
@ColumnMask(Masks.CHINESE_NAME)
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
@As("my_age")
|
@ColumnAlias("my_age")
|
||||||
private int age;
|
private int age;
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.mybatisflex.test.entity;
|
package com.mybatisflex.test.entity;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.As;
|
import com.mybatisflex.annotation.ColumnAlias;
|
||||||
import com.mybatisflex.annotation.Table;
|
import com.mybatisflex.annotation.Table;
|
||||||
import com.mybatisflex.test.model.IdEntity;
|
import com.mybatisflex.test.model.IdEntity;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ public class Outer extends IdEntity<Integer> {
|
|||||||
private Inner inner;
|
private Inner inner;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@As("test_id")
|
@ColumnAlias("test_id")
|
||||||
public void setId(Integer id) {
|
public void setId(Integer id) {
|
||||||
super.setId(id);
|
super.setId(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.mybatisflex.test.model;
|
package com.mybatisflex.test.model;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.As;
|
import com.mybatisflex.annotation.ColumnAlias;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 王帅
|
* @author 王帅
|
||||||
@ -26,13 +26,15 @@ public class AccountVO2 extends IdEntity<Long> {
|
|||||||
|
|
||||||
private Integer age;
|
private Integer age;
|
||||||
|
|
||||||
@As("account_name")
|
@ColumnAlias("account_name")
|
||||||
private String userName;
|
private String userName;
|
||||||
private UserVO4 user;
|
|
||||||
|
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@As("account_id")
|
@ColumnAlias("account_id")
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return super.getId();
|
return super.getId();
|
||||||
}
|
}
|
||||||
@ -53,11 +55,11 @@ public class AccountVO2 extends IdEntity<Long> {
|
|||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserVO4 getUser() {
|
public User getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUser(UserVO4 user) {
|
public void setUser(User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ spring:
|
|||||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://localhost:3306/flex_test
|
url: jdbc:mysql://localhost:3306/flex_test
|
||||||
username: root
|
username: root
|
||||||
password: 12345678
|
password: 123456
|
||||||
# driver-class-name:
|
# driver-class-name:
|
||||||
# datasource:
|
# datasource:
|
||||||
# driver-class-name: org.h2.Driver
|
# driver-class-name: org.h2.Driver
|
||||||
|
|||||||
@ -117,6 +117,20 @@ class AccountMapperTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testAs() {
|
void testAs() {
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ID,
|
||||||
|
ACCOUNT.AGE,
|
||||||
|
USER.USER_ID,
|
||||||
|
USER.USER_NAME)
|
||||||
|
.from(ACCOUNT.as("a"), USER.as("u"))
|
||||||
|
.where(ACCOUNT.ID.eq(1))
|
||||||
|
.limit(1);
|
||||||
|
AccountVO2 account = accountMapper.selectOneByQueryAs(queryWrapper, AccountVO2.class);
|
||||||
|
System.out.println(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAs0() {
|
||||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
.select(ACCOUNT.ID.as("account_id"),
|
.select(ACCOUNT.ID.as("account_id"),
|
||||||
ACCOUNT.AGE,
|
ACCOUNT.AGE,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user