【问题标题】:How to autowire a transient attribute?如何自动装配瞬态属性?
【发布时间】:2020-01-08 21:52:37
【问题描述】:

我有一个实体,例如 Employee,它包含一个 @Transient 对象薪水,它将从相关表/实体 DailyTimeRecord (DTR) 派生。 DTR 对象数据检索使用联接,并且它也在 Employee 对象中自动装配。 DTR 对象列表将作为计算 Salary 对象值的基础。

我在这里发现[1]:Why is my Spring @Autowired field null? 应该避免使用new 关键字,让IoC Container 创建对象。另外,我想避免使用new关键字,以尽量减少我的代码耦合,并尽可能保证未来的兼容性和支持可扩展性。因此,我有接口 Salary 并由 SalaryImpl 类实现。

但每次我尝试运行在瞬态属性 Salary 上自动装配的代码时,它始终为空。我在这里找到了根本原因 [2]:How to populate @Transient field in JPA? 在 JPA 中 Transient 将始终为空。

我将如何创建一个避免使用 new 关键字的对象,而它是一个瞬态属性?

实体类

   @Entity
   Class Employee implements Serializable {
          //Attributes from DB here

          @OneToMany
          @JoinColumn(name="empNumber", referencedColumnName = "empNumber")
          private List<DTR> dtr;

          @Autowired
          @Transient
          private Salary salary;

          //getters ang setters here

          public double computeSalary(){

          }
   }

薪资界面

   public interface Salary {

          public double computeSalary(List<Benefit> benefits, List<Deduction> deductions);

   }

接口工资的基类/实现类

   @Service
   public class SalaryImpl implements Salary, Serializable {

          //other attributes here

          //getter and setters

          //other methods

          @Override
          public double computeSalary(List<Benefit> benefits, List<Deduction> deductions){
                 return 0;
          }
   }

【问题讨论】:

    标签: java spring-boot autowired transient


    【解决方案1】:

    首先,@Transient 来自 JPA,与 Spring 无关。

    其次,要让Spring能够将bean注入EmployeeEmployee也需要注册为spring bean。但实际上,您可以认为 Employee 是由 JPA 实现在幕后使用“new”创建的。这就是为什么 spring 不能自动将其他 bean 连接到它。

    如果确实需要,可以使用AspectJ,按照docs的说明进行。

    我个人没有尝试这种方法,因为您可以简单地让您的SalaryService 接受Employee 作为计算他的薪水的参数之一,这比AspectJ 方法更简单易懂。

    public interface SalaryService {
        public double computeSalary(Employee employee , List<Benefit> benefits, List<Deduction> deductions);
    } 
    

    客户端代码如下:

    @Service
    public class EmployeeService {
    
        @Autowired
        private SalaryService salaryService;
    
        @Transactional
        public void computeEmployeeSalary(Integer employeeId){
            Employee employee = entityManager.find(Employee.class , employeeId);
            salaryService.computeSalary(employee, .... ,.....);
        }
    
    }
    

    【讨论】:

    • 感谢您提供在 JPA 之外管理/处理数据的替代方案。是的,你是对的,我应该将业务逻辑从实体中移开,以将数据模型与业务逻辑完全分离,这将使代码更易于理解和维护。
    • 至于正式结束这个问题,您指出 AspectJ 作为我问题的答案是正确的。另一种选择只是奖金。 :D
    【解决方案2】:

    实体对象由 JPA 实现(如 Hibernate)创建,不由 spring 管理。

    它们既不是单例也不是原型,所以一般来说,你不能对实体 bean 的属性使用自动装配(因为自动装配只能在 spring bean 上完成)。

    您可能有兴趣阅读 This SO thread 了解一些解决方法的想法。

    【讨论】:

    • 感谢您的帮助。我发现我错了,我应该首先从 JPA 获得对象。然后,将 JPA 之外的对象管理到 Spring 可以管理 Objects 的 Services 中。通过添加 Component 的 Scope 或使用 Configurable 将 POJO 或业务逻辑添加到 Spring,现在可以管理和操作对象。我的结论正确吗?
    • 我相信是的(虽然我自己没有尝试过),尽管为此添加 aspectj 在我看来有点矫枉过正。可能更好的解决方案是重构,例如通过一些 dao 提供对实体生成代码 (jpa) 的访问,当查询完成时,遍历列表并手动注入属性(通过调用 setter)。 dao 可以自己驱动 spring 并且可以访问要作为数据字段注入的对象
    猜你喜欢
    • 2016-08-25
    • 2011-02-10
    • 2015-04-17
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2012-01-09
    • 2011-11-23
    • 1970-01-01
    相关资源
    最近更新 更多