I have an Entity for example Employee that contains a @Transient Object Salary which will be derived from a related table/Entity DailyTimeRecord (DTR). DTR object data retrieval uses Joins and it is also autowired in the Employee object. The list of DTR objects will be the basis in computing the value of the Salary Object.
I found here [1]: Why is my Spring @Autowired field null? that using new
keyword should be avoided, and let IoC Container create objects. In addition, I want to avoid using new
keyword to minimize the coupling of my codes and ensure future compatibility and support scalability as much as possible. Therefore, I have interface Salary and implemented by a SalaryImpl class.
But Each time I tried to run the codes the autowired on a transient attribute Salary, it is always null. And I found the root cause here [2]: How to populate @Transient field in JPA? that Transient will always be null in JPA.
How will I ever create a object that avoiding the use of new
keyword while it is a transient attribute?
Entity Class
@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(){
}
}
Interface of Salary
public interface Salary {
public double computeSalary(List<Benefit> benefits, List<Deduction> deductions);
}
Base/Implementation class of interface salary
@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;
}
}
First, @Transient
is from JPA which is nothing to do with Spring .
Second, to be able to let Spring to inject beans into Employee
, Employee
is also required to be registered as a spring bean. But in realty, you can think that Employee is created using "new" by JPA implementation behind scene. That 's why spring cannot auto wire other beans to it.
If you really need do it, you can use AspectJ
to do it as described by the docs.
I personally did not try this approach as you can simply make your SalaryService
to accept an Employee
as one of its argument to compute his salary, which is much simpler and easy to understand than the AspectJ
approach.
public interface SalaryService {
public double computeSalary(Employee employee , List<Benefit> benefits, List<Deduction> deductions);
}
And the client code looks like:
@Service
public class EmployeeService {
@Autowired
private SalaryService salaryService;
@Transactional
public void computeEmployeeSalary(Integer employeeId){
Employee employee = entityManager.find(Employee.class , employeeId);
salaryService.computeSalary(employee, .... ,.....);
}
}