spring data Bean 继承 父类拥有子类属性?spring data 忽略字段

做个笔记:

功能:创建租户时同时创建此租户的管理员账号,租户表包含管理员账号,同时账号存放在管理员表;

租户表:租户id,租户名称,租户管理员账户,创建时间

管理员账户表:管理员账户,登录密码

思路:为了充分发挥spring data的作用(手动滑稽~_~),所以我决定前台传值(租户名称,管理员账号,pwd,age)将其张换成json传值后台,后台新建一个临时租户bean继承租户表对应的Bean,临时Bean字段为pwd,创建临时bean为的是将前端传值转换成有一个bean。然后我想解释的就是临时bean存在的意义。

然后上栗子:

公司bean包含字段:公司名称,管理员账户名称,管理员age

@Document("company")
public class Company {
    private String companyName;
    private String adminUser;
    private int age;

    public String getAdminUser() {
        return adminUser;
    }

    public void setAdminUser(String adminUser) {
        this.adminUser = adminUser;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Company{" +
                "adminUser='" + adminUser + '\'' +
                ", age=" + age +
                '}';
    }
}

 管理员Bean字段:管理员名称、登录密码、age

@Document("admin_user")
public class AdminUser {

    private String adminName;
    private int age;
    private String pwd;

    public String getAdminName() {
        return adminName;
    }

    public void setAdminName(String adminName) {
        this.adminName = adminName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "AdminUser{" +
                "adminName='" + adminName + '\'' +
                ", age=" + age +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

 临时Bean为了转换前台数据而存在的bean,字段:pwd

public class CompanyParam extends Company {
    private String pwd;

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "CompanyParam{" +
                "pwd='" + pwd + '\'' +
                '}';
    }
}

controller:ennn,没错就一个接口

@RestController
@RequestMapping("/regodter")
public class RegisterRestController {

    private RegisterService registerService;

    @Autowired
    public RegisterRestController(RegisterService registerService) {
        this.registerService = registerService;
    }

    @PostMapping("")
    public Company create(@Validated @RequestBody CompanyParam companyParam){
        return registerService.createCompany(companyParam, companyParam.getPwd());
    }
}

service: 这儿有俩方法,创建company的和生成user的

 然后这里需要说明一下,我controller里面调用时传递的事临时bean(子类) CompanyParam  service方法里面是公司的实体类(父类) Company 当时想的是,直接将Company  进行储存(这里没有描述清楚,希望大神指点)。enn,因为这个想法所以在这里出了问题;

@Service
public class RegisterService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RegisterService.class);

    public Company createCompany(Company company, String pwd){
        AdminUser user = createUser(company.getAdminUser(), company.getAge(), pwd);
        //假装储存了user 用户表.save(user);
        //假装储存了company 公司表.save(company);
        return company;
    }

    private AdminUser createUser(String name,int age,String pwd){
        AdminUser adminUser = new AdminUser();
        adminUser.setAdminName(name);
        adminUser.setAge(age);
        adminUser.setPwd(DigestUtils.md5Hex(pwd)) ;
        return  adminUser;
    }
}

都准备好了,那就调用一下接口测试一下吧;工具postman

请求:

 返回:

 

WHAT?  wtf!! 希望看到这里各位看官还是清醒的,在这里我强行解释一波吧,我的理解是,service 方法中需传的参的是父类,然后我将子类传递进去,以为会进行剔除,然鹅没有,将pwd也存入了company表了,然后我找到了俩解决方式:

方式一:将两个bean进行一个copy

Company company= new Company();
BeanUtils.copyProperties(companyParam,company);

方式二:在CompanyParam 中加注解@Transient(import org.springframework.data.annotation.Transient;)

 @Transient
 private String pwd;

(enn哈哈 如果有这个需求的朋友可以联系我,咱们详谈,有错误也请各位放肆的☞出,thank you)

<think>我们之前讨论过通过反射获取Repository管理的实体类型,这里详细解释如何动态获取SpringDataJPA中Repository的实体类型。在SpringDataJPA中,每个Repository接口通常继承自`JpaRepository<T,ID>`,其中`T`就是该Repository管理的实体类型。我们可以通过Java的反射机制来获取这个泛型参数。###解决方案:我们可以通过以下步骤获取Repository的实体类型:1.获取Repository接口的泛型接口(因为`JpaRepository`是一个泛型接口)。2.从泛型接口中提取实际类型参数(即实体类型)。###代码示例:以下是一个工具方法,用于从Repository接口获取实体类型:```javaimportorg.springframework.data.jpa.repository.JpaRepository;importjava.lang.reflect.ParameterizedType;importjava.lang.reflect.Type;importjava.lang.reflect.TypeVariable;importjava.util.Arrays;importjava.util.Optional;publicclassRepositoryEntityTypeUtil{/***获取Repository接口管理的实体类型*@paramrepositoryInterfaceRepository接口的Class对象*@return实体类型的Class*@throwsIllegalArgumentException如果无法确定实体类型*/publicstaticClass<?>getEntityType(Class<?>repositoryInterface){//检查是否继承了JpaRepositoryreturngetEntityTypeFromInterfaces(repositoryInterface).orElseThrow(()->newIllegalArgumentException("无法确定Repository的实体类型"));}privatestaticOptional<Class<?>>getEntityTypeFromInterfaces(Class<?>repositoryInterface){//遍历接口for(TypegenericInterface:repositoryInterface.getGenericInterfaces()){if(genericInterfaceinstanceofParameterizedType){ParameterizedTypeparamType=(ParameterizedType)genericInterface;TyperawType=paramType.getRawType();//检查原始类型是否是JpaRepositoryif(rawTypeinstanceofClass&&JpaRepository.class.isAssignableFrom((Class<?>)rawType)){//获取JpaRepository的第一个类型参数(即实体类型)Type[]actualTypeArguments=paramType.getActualTypeArguments();if(actualTypeArguments.length>=1){TypeentityType=actualTypeArguments[0];if(entityTypeinstanceofClass){returnOptional.of((Class<?>)entityType);}elseif(entityTypeinstanceofParameterizedType){//处理嵌套泛型(例如实体类本身是泛型类,但实际使用中往往是具体类)TyperawEntityType=((ParameterizedType)entityType).getRawType();if(rawEntityTypeinstanceofClass){returnOptional.of((Class<?>)rawEntityType);}}}}}}//如果没有在直接实现的接口中找到,递归检查父接口for(Class<?>parentInterface:repositoryInterface.getInterfaces()){Optional<Class<?>>entityType=getEntityTypeFromInterfaces(parentInterface);if(entityType.isPresent()){returnentityType;}}returnOptional.empty();}}```###使用示例:假设有一个实体`User`和对应的`UserRepository`:```java@EntitypublicclassUser{@IdprivateLongid;//...}publicinterfaceUserRepositoryextendsJpaRepository<User,Long>{//...}```在代码中获取实体类型:```javaClass<?>entityType=RepositoryEntityTypeUtil.getEntityType(UserRepository.class);System.out.println(entityType);//输出:classcom.example.User```###解释:1.**反射获取泛型参数**:我们遍历Repository接口的所有泛型接口,查找继承了`JpaRepository`的那个接口,然后获取其第一个类型参数(即实体类型)。2.**处理嵌套泛型**:如果实体类型本身是泛型(如`MyEntity<T>`),我们取其原始类型(即`MyEntity`),但通常我们的实体都是具体类。3.**递归查找**:如果Repository接口是通过多层继承的(例如`UserRepository`继承自`BaseRepository`,而`BaseRepository`继承自`JpaRepository`),我们需要递归查找父接口。###注意事项:-此方法适用于大多数情况,但如果Repository是通过自定义基类(非接口)实现的,则可能无法直接使用,需要调整代码检查父类。-如果Repository接口同时实现了多个泛型接口,且都继承自`JpaRepository`,则此方法可能返回第一个匹配的实体类型。---§§相关问题§§:1.如何获取Repository接口中主键(ID)的类型?2.如果Repository接口继承多个泛型接口,如何准确获取目标实体类型?3.在Spring上下文中如何动态获取RepositoryBean并解析其实体类型?4.如何为SpringDataJPA的Repository接口动态创建代理实现?</think> # Spring Data JPA中动态获取Repository的实体类型详解 在Spring Data JPA中动态获取Repository管理的实体类型是一个常见的需求,尤其在使用通用Service或实现自定义框架功能时特别有用。以下提供两种主要方法实现此功能: ## 方法一:通过反射获取实体类型(推荐) ```java import org.springframework.data.jpa.repository.JpaRepository; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class RepositoryEntityTypeExtractor { /** * 获取Repository接口的实体类型 * @param repositoryClass Repository接口的Class对象 * @return 实体类的Class对象 */ public static Class<?> getEntityType(Class<?> repositoryClass) { // 获取Repository实现的接口列表 Type[] interfaces = repositoryClass.getGenericInterfaces(); for (Type type : interfaces) { // 检查是否ParameterizedType(泛型接口) if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; // 检查原始类型是否是JpaRepository或其子类 if (isJpaRepository(paramType.getRawType())) { // 获取第一个类型参数(实体类型) Type[] actualTypes = paramType.getActualTypeArguments(); if (actualTypes.length > 0) { return (Class<?>) actualTypes[0]; } } } } // 如果未找到则抛出异常 throw new IllegalArgumentException("无法从Repository获取实体类型: " + repositoryClass.getName()); } /** * 检查类型是否是JpaRepository或其子类 */ private static boolean isJpaRepository(Type type) { if (!(type instanceof Class)) return false; Class<?> clazz = (Class<?>) type; return JpaRepository.class.isAssignableFrom(clazz); } } ``` ### 使用示例 ```java // 定义实体 @Entity public class User { /* ... */ } // 定义Repository public interface UserRepository extends JpaRepository<User, Long> { /* ... */ } // 使用实体提取器 Class<?> entityType = RepositoryEntityTypeExtractor.getEntityType(UserRepository.class); System.out.println(entityType.getName()); // 输出: com.example.User ``` ### 方法说明: 1. **反射原理**:通过分析Repository接口实现的泛型接口 2. **关键步骤**: - 获取Repository类实现的所有接口 - 查找继承了`JpaRepository`的接口 - 提取该接口的第一个类型参数(实体类型) 3. **适用场景**:任何需要静态获取Repository实体类型的情况 ## 方法二:从Repository实例获取实体类型 ```java import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.repository.core.support.RepositoryFactoryInformation; public class RepositoryEntityExtractor { /** * 获取Repository实例管理的实体类型 * @param repository Repository实例 * @return 实体类的Class对象 */ public static Class<?> getEntityType(Object repository) { RepositoryFactoryInformation factoryInfo = RepositoryFactorySupport.getRepositoryFactoryInformationFor(repository); if (factoryInfo != null) { RepositoryMetadata metadata = factoryInfo.getRepositoryMetadata(); return metadata.getDomainType(); } throw new IllegalArgumentException("无法从Repository实例获取实体类型"); } } ``` ### 使用示例 ```java @Service public class UserService { @Autowired private UserRepository userRepository; public void process() { Class<?> entityType = RepositoryEntityExtractor.getEntityType(userRepository); System.out.println(entityType.getName()); // 输出: com.example.User } } ``` ### 方法说明: 1. **核心机制**:使用Spring Data提供的`RepositoryMetadata` 2. **关键组件**: - `RepositoryFactoryInformation`:提供Repository的元数据访问 - `RepositoryMetadata`:包含Repository的类型信息 3. **适用场景**:在运行时通过Repository实例动态获取实体类型 ## 两种方法对比 | 特点 | 方法一(反射) | 方法二(RepositoryMetadata) | |------|---------------|-----------------------------| | 依赖 | 只需要Repository接口 | 需要Spring应用上下文 | | 获取方式 | 静态 | 动态(需要Repository实例) | | 场景 | 编译时/静态处理 | 运行时动态处理 | | 复杂度 | 低 | 中(需要Spring上下文) | | 准确性 | 高 | 非常高(使用Spring内部机制) | ## 实际应用场景 这两种方法在以下场景中特别有用: 1. **通用服务层实现**:创建适用于所有实体的通用CRUD服务 2. **审计日志**:实现自动化的实体变更日志记录 3. **实体校验框架**:基于实体类型的通用校验器 4. **元数据管理**:构建实体元数据管理系统 5. **动态查询生成**:根据实体字段动态构造查询语句
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值