1

I found a strange behaviour in my Spring App.

The @autowired of an Entity is not working all the time.

I use the Elide project to build an JSONAPI and some custom controllers.

In one controller, one @Autowrited of an entity stays null however it use correctly working when called from Elide.

Controller:

@RestController
public class UploadController {
    @Autowired
    private ProjectRepository projectRepository;

    @PostMapping(value = "/api/projects/{projectId}/upload")
    public String uploadItem(@PathVariable long projectId, @RequestParam("file") MultipartFile file,
                              @RequestParam("projectName") String projectName,
                              RedirectAttributes redirectAttributes) throws IOException {
        Project project = projectRepository.findOneByProjectIdAndName(projectId, projectName);
        Integer result = project.getNumberOfItems();

        return "";
    }
}

Entity

@Setter
@NoArgsConstructor
@Table(name = "projects")
@Entity
@Include(rootLevel = true, type = "projects")
public class Project extends DiffShelfBase {

    @Autowired
    @Transient
    private ItemRepository itemRepository;

    @Transient
    @ComputedAttribute
    public Integer getNumberOfItems() {
        return itemRepository.countByProjectId(this.getId());
    }
}

Repository

@Repository
@Transactional
public interface ItemRepository extends JpaRepository<Item, Long> {
    Integer countByProjectId(long projectId);
}

Configuration

@Configuration
@EnableSpringConfigured
public class MyConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedMethods("*")
                        .allowedOrigins("http://localhost:4200");
            }
        };
    }
}

I don't understand why but the itemRepository in null in Project.getNumberOfItems.

Charles
  • 11,367
  • 10
  • 77
  • 114

3 Answers3

3

This is not a way how you should do that. Don't use @Autowired in your entities. This annotation is managed by Spring, but your entities are managed and created by Hibernate and it doesn't care about Spring annotations. You could find another way to add this property to your entity(let it be field in a database for example).

Vlad Hanzha
  • 464
  • 3
  • 11
1

The problem you have is, that your Project is no managed by spring. You have to add the @Configurable annotation to your class. Then your Autowired annotation will work.

But I would really refactor your code. You should not have spring-objects in your Entities.

Christian
  • 22,585
  • 9
  • 80
  • 106
  • Out of curiosity, what's the performance impact when dealing with a lot of Configurable instances? Seems it is based on AOP proxies. Do you have any direct experience with that? – LppEdd Feb 16 '19 at 21:13
  • Thanks but `@Configurable` was not enough. Do you have an idea if the answer of Vlad is 100% accurate? – Charles Feb 16 '19 at 21:17
  • I've found another answer. Check it out: https://stackoverflow.com/questions/5594934/hibernate-entity-autowire – Christian Feb 16 '19 at 21:20
  • @Charles isn't that you're missing the EnableSpringConfigured configuration annotation? – LppEdd Feb 16 '19 at 21:58
  • Thanks but even by adding `@EnableSpringConfigured` in `MyConfiguration` and `@Configurable` in Project it's not working. – Charles Feb 16 '19 at 22:23
0

An Entity should be a glorified POJO, never complicate it embedding repos inside

so autowire ItemRepository a level up, atUploadController itself.

@RestController
public class UploadController {

    @Autowired
    private ProjectRepository projectRepo;

    @Autowired
    private ItemRepository itemRepo;

    @PostMapping(value = "/api/projects/{projectId}/upload")
    public String uploadItem(@PathVariable long projectId, @RequestParam("file") MultipartFile file,
                              @RequestParam("projectName") String projectName,
                              RedirectAttributes redirectAttributes) throws IOException {

        Project project = projectRepo.findOneByProjectIdAndName(projectId, projectName);
        Integer result = itemRepo.countByProjectId(project.getId());

        return "";
    }
}

and as a good practice, you can always do a constructor injection making it easy to test

e.g. instead of

@Autowired
private ProjectRepository projectRepo;

@Autowired
private ItemRepository itemRepo;

do a constructor injection

private final ProjectRepository projectRepo;
private final ItemRepository itemRepo;

@Autowired
public UploadController(final ProjectRepository projectRepo, final ItemRepository itemRepo) {
    this.projectRepo = projectRepo;
    this.itemRepo = itemRepo;
}

this way,

  1. it is easy to test by passing mockito instances.
  2. you can make them final (which is not possible with @Autowire on variable directly)
Sharan Arumugam
  • 353
  • 6
  • 12
  • 1
    Thanks for your answer. As I said, I'm using Elide to create a JSONAPI interface. `getNumberOfItems` is used by Elide to be sent to my JavaScript App through JSONAPI. This works well this is why I wunder why it's not working from the Controller. I totally understand it's a bad practice but I'm created a MVP and I prefer to code quick and dirty atm... – Charles Feb 16 '19 at 23:00
  • Ah Okay. Got it. – Sharan Arumugam Feb 16 '19 at 23:32