Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 189 Vote(s) - 3.56 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Bean injection inside a JPA @Entity

#1
Is it possible to inject beans to a JPA `@Entity` using Spring's dependency injection?

I attempted to @Autowire ServletContext but, while the server did start successfully, I received a NullPointerException when trying to access the bean property.

@Autowired
@Transient
ServletContext servletContext;
Reply

#2
Yes, of course you can. You just need to make sure the entity is also registered as a Spring managed bean either declaratively using `<bean>` tags (in some spring-context.xml) or through annotations as shown below.

Using annotations, you can either mark your entities with `@Component` (or a more specific stereotype `@Repository` which enables automatic exception translation for DAOs and may or may not interfere with JPA).

@Entity
@Component
public class MyJAPEntity {

@Autowired
@Transient
ServletContext servletContext;
...
}

Once you've done that for your entities you need to configure their package (or some ancestor package) for being scanned by Spring so that the entities get picked up as beans and their dependencies get auto wired.

<beans ... xmlns:context="..." >
...
<context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>

***EDIT** : (what finally worked and why)*<br />

- Making the `ServletContext` *static*. (remove *@Autowired*)

@Transient
private static ServletContext servletContext;

Since, JPA is creating a separate entity instance i.e. not using the Spring managed bean, it's required for the *context* to be shared.

- Adding a *@PostConstruct* `init()` method.

@PostConstruct
public void init() {
log.info("Initializing ServletContext as [" +
MyJPAEntity.servletContext + "]");
}

This fires `init()` once the Entity has been instantiated and by referencing `ServletContext` inside, it forces the injection on the *static* property if not injected already.

- Moving `@Autowired` to an *instance* method but setting the *static* field inside.

@Autowired
public void setServletContext(ServletContext servletContext) {
MyJPAEntity.servletContext = servletContext;
}

Quoting my last comment below to answer why do we have to employ these shenanigans:
> There's no pretty way of doing what you want since JPA doesn't use the Spring container to instantiate its entities. Think of JPA as a separate ORM container that instantiates and manages the lifecycle of entities (completely separate from Spring) and does DI based on entity relationships only.
Reply

#3
You can inject dependencies into objects not managed by the Spring container using `@Configurable` as explained here:

[To see links please register here]

.

As you've realized by now, unless using the `@Configurable` and appropriate AspectJ weaving configuration, Spring does not inject dependencies into objects created using the `new` operator. In fact, it doesn't inject dependencies into objects unless you've retrieved them from the `ApplicationContext`, for the simple reason that it simply doesn't know about their existence. Even if you annotate your entity with `@Component`, instantiation of that entity will still be performed by a `new` operation, either by you or a framework such as Hibernate. Remember, annotations are just metadata: if no one interprets that metadata, it does not add any behaviour or have any impact on a running program.

All that being said, I strongly advise against injecting a `ServletContext` into an entity. Entities are part of your domain model and should be decoupled from any delivery mechanism, such as a Servlet-based web delivery layer. How will you use that entity when it's accessed by a command-line client or something else not involving a ServletContext? You should extract the necessary data from that ServletContext and pass it through traditional method arguments to your entity. You will achieve a much better design through this approach.
Reply

#4
After a long time I stumbled across [this SO answer][1] that made me think of an elegant solution:

- Add to your entities all the @Transient @Autowired fields you need
- Make a @Repository DAO with this autowired field:
```
@Autowired private AutowireCapableBeanFactory autowirer;
```
- From your DAO, after fetching the entity from DB, call this autowiring code:
```
String beanName = fetchedEntity.getClass().getSimpleName();
autowirer.autowireBean(fetchedEntity);
fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);
```

Your entity will then be able to access the autowired fields as any @Component can.


[1]:

[To see links please register here]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through