0

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;
          }
   }
David Brossard
  • 13,584
  • 6
  • 55
  • 88
Itami
  • 78
  • 1
  • 8

2 Answers2

2

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, .... ,.....);
    }

}
Monolith
  • 1,067
  • 1
  • 13
  • 29
Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • 1
    Thank for giving alternative of managing/processing the data outside JPA. Yes, you are correct, I should have moved the business logic away from the Entity to fully separate the Data Model from business logic which will make the code easier to understand and maintain. – Itami Sep 06 '19 at 04:00
  • 1
    As to formally close the question, you are correct in pointing out the AspectJ as the answer to my question. The alternative is just a bonus. :D – Itami Sep 06 '19 at 04:05
1

Entity objects are created by JPA implementation (like Hibernate) and not managed by spring.

They're neither Singletons nor Prototypes, so generally speaking, you cannot use Autowiring on properties of entity beans (because Autowiring is something that only can be done on spring beans).

You might be interested to read This SO thread for ideas for some workarounds.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thanks for help. I found were I got wrong, and I should have get first the Object from JPA. Then, manage the object outside JPA into Services where Spring can manage Objects. By Adding Scope of the Component or making the POJO or business logic to Spring using Configurable, the object can now be manage and manipulate. Am I correct on my conclusion? – Itami Sep 06 '19 at 03:49
  • I believe yes (havent tried that by myself though), although adding aspectj for this seems to me an overkill. Probably a better solution would be refactoring like providing an access to entities generation code( jpa) via some dao, and when query is done, iterating through the list and injecting the property manually (by calling a setter). The dao could be spring driven by itself and had an access to the object to be injected as a data field – Mark Bramnik Sep 06 '19 at 06:12
  • i have a transient variable (repository userDAO) as part of my user entity... i'm getting null value rather than the userDAO... – A B Jul 09 '23 at 10:23