I've been looking for a way to call some method from a service, when my entity is saved.
My App was created using roo 1.2.4.RELEASE
I have a Balance Entity called SaldoCliente and a ClientAction Entity called AuxCliente.
I need to update a client balance every time a new ClientAction entity gets persisted.
This is my code:
@RooJavaBean
@RooToString
@RooJpaEntity(entityName = "AUX_CLIENTES")
public class AuxCliente {
@Transient
@Autowired
static private SaldoClienteService saldoClienteService;
...
@PostConstruct
public void init()
{
System.out.println("Initializing with dependency ["+ saldoClienteService + "]");
}
@PostPersist
private void afectaSaldoCliente(/*Long idTrans, Cliente, Integer cargo, BigDecimal importe, Integer creditos*/) {
if (saldoClienteService == null) {
System.out.println("saldoClienteService FUE NULL");
}
...
I don't know why saldoClienteService is always null.
(Note I don't want a saldoClienteService
field saved in my DB, hence the @Transient annotation)
I've been searching for a solution without success. Many of the explanations say something like this where says: You need <context:annotation-config/> (or <context:component-scan/>) to enable @PostConstruct handling.
And I do have <context:component-scan>
in my applicationContext.xml (created by Roo).
The documentation says:
By default, the Spring-provided @Component, @Repository, @Service, and @Controller stereotypes will be detected. Note: This tag implies the effects of the 'annotation-config' tag, activating @Required, @Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit annotations in the component classes, which is usually desired for autodetected components (without external configuration).
And at least the @Autowired
annotation works everywhere but there.
Does anybody have some pointers?
------------------ EDITED -----------------
First of all: I want to thank @Sotirios and @Ralph for taking the time to help me.
If I remove "static" from the field, it's the same. The injected fields are always null in my Entities. (See my comments in this question, I added that because of a "possible" solution).
I'm having trouble also with another class I need injected. I added this to the same class as before (AuxClientes):
@Transient
@Autowired
private ConfigUser configUser;
And configUser is always null, also.
This is the beginning of the other class, in case it matters.
@RooJavaBean(settersByDefault=false)
public class ConfigUser {
...
and ofcourse, in applicationContext.xml:
<bean class="com.i4b.adminctes.util.ConfigUser" id="appConfigUser" />
I'm successfully using configUser in constructors, services and repositories. But can’t use it in Entities.
If you think I should post any other part of my code, just let me know.
--------------- EDIT 2 ------------------
The same happens with all my entities.
--------------- EDIT 3.a ------------------
I changed the question title, for a better one. Before it was:
Spring roo (service autowiring) Entity not calling @PostConstruct . (Using JPA Repository with @RooJpaEntity)
--------------- EDIT 3.b ------------------
I have just created a minimal test project.
// Spring Roo 1.2.4.RELEASE [rev 75337cf] log opened at 2013-11-13 11:36:27
project --topLevelPackage org.example --projectName TestAutowiredOnEntities --java 7 --packaging WAR
jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
entity jpa --class ~.domain.MyEntity --testAutomatically --activeRecord false
field string --fieldName text
repository jpa --interface ~.repository.MyEntityRepository --entity ~.domain.MyEntity
service type --interface ~.service.MyEntityService --entity ~.domain.MyEntity
web mvc setup
web mvc all --package org.example.web
Edited the service:
package org.example.service;
public class MyEntityServiceImpl implements MyEntityService {
@Override
public String testAutowire() {
return "Some data";
}
}
Edited the entity:
package org.example.domain;
import javax.persistence.PrePersist;
import javax.persistence.Transient;
import org.example.service.MyEntityService;
import org.example.service.MyEntityServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.entity.RooJpaEntity;
import org.springframework.roo.addon.tostring.RooToString;
@RooJavaBean
@RooToString
@RooJpaEntity
public class MyEntity {
@Transient
@Autowired
MyEntityService myEntityService;
/**
*/
private String text;
@PrePersist
public void prePersist() {
if (myEntityService == null) {
System.out.println("myEntityService IS NULL");
} else {
String data=myEntityService.testAutowire();
System.out.println("it works: " + data);
this.text = data;
}
}
}
And edited the create.jspx to hide the service field. otherwise it doesn't let you save.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8"/>
<jsp:output omit-xml-declaration="yes"/>
<form:create id="fc_org_example_domain_MyEntity" modelAttribute="myEntity" path="/myentitys" render="${empty dependencies}" z="T0LoTr6PZAwfIQHkjOZMmPW7cO8=">
<field:input field="myEntityService" id="c_org_example_domain_MyEntity_myEntityService" render="false" z="12sHnsW2dWYyuD+vDtbTve/jWuI="/>
<field:input field="text" id="c_org_example_domain_MyEntity_text" z="jUCTnP7E3pYPcZcfGn1tyJ2VeFI="/>
</form:create>
<form:dependency dependencies="${dependencies}" id="d_org_example_domain_MyEntity" render="${not empty dependencies}" z="Un0bJ/PmWmczxoVTom9NowwIRWk="/>
</div>
Then executed the application and created a new "My Entity". Leaving the field empty, I preassed the save button.
The log shows:
INFO: Reloading Context with name [/testAutowiredOnEntities] is completed
nov 13, 2013 2:31:52 PM org.apache.jasper.compiler.TldLocationsCache tldScanJar
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
myEntityService IS NULL
And the entity has an empty text field.
Just to be sure, I added @Component to "MyEntity" class:
...
@Component
@RooJavaBean
@RooToString
@RooJpaEntity
public class MyEntity {
...
Nothing changed. The service is still null.
I really hope it helps someone more knowledgable than me to help me find a solution.
Thank you all.
In the mean time, I'll re-read the documentation section @Ralph pointed out. I'm obviously doing something wrong. I don't bielieve I'm the only one needing this.
Again: Thank you all
Spring does not inject in static
fields.
@RooJavaBean
@RooToString
@RooJpaEntity(entityName = "AUX_CLIENTES")
public class AuxCliente {
@Transient
@Autowired
private SaldoClienteService saldoClienteService;
@PostPersist
void afectaSaldoCliente() {
this.saldoClienteService.doWhatYouWant();
}
}
regarding update #2
It looks like ConfigUser
is an Hibernate/JPA/Whatever Entity but not an Spring Bean. But you can only inject Spring Beans (like Services, Dao, ... (Every thing that has an @Component
, @Controller
, @Service
, @Repository
annotation*.))
And you can only inject in Spring Beans or Classes annotated with @Configurable
(requires AspectJ) See Spring Reference Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
Using @Configurable
is the way Spring-Roo does (There must be a file AuxCliente_Roo_Configurable.aj
next to your Entity (In Eclipse it needs to disable the "Hide generates Spring Roo ITDs"-Filter to display this file in the Package Explorer)).
* There are some more Ways that an object become a spring bean, but this does not matter here