1

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

Community
  • 1
  • 1
elysch
  • 1,846
  • 4
  • 25
  • 43

2 Answers2

0

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

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • I updated my question. I get the same error with or without the field being "static". Any other ideas for me to try? – elysch Nov 12 '13 at 20:07
  • Wow... great response. Thank you so much. I have a question though. I'm not sure why the configUser @Autowired field is working fine on the services or other places. Do you know why? – elysch Nov 13 '13 at 15:27
  • Ok, thank you. About your comment about "update #2" I tried to add \@component to the ConfigUser and not in my Entity, in both and only in my Entity with no success. No matter what, configUser is always null in \@PrePersist. I even created e new roo project and it's the same. I will update my question – elysch Nov 13 '13 at 20:35
  • After all this time, I re-read the documentation and found that I didn't understand the @Configurable annotation. Don't know why I thought I couldn't use it in my case. To make the story short, just adding it to my entity class, made all the autowiring to get to work. At the end I decided to use a helper class to avoid all the Srping Roo created code for my "config" bean. I'll add a new answer to explain this. – elysch Nov 26 '13 at 11:25
0

Finally got it working.

As I mentioned in my last comment to @Ralph: adding @Configurable annotation did the trick.

If the class looks like the following, the autowiring works:

@Configurable                <--- added this
@RooPlural("Tarjetas")
@RooJavaBean
@RooToString
@RooJpaEntity
@Table(name = "TARJETAS")
public class Tarjeta {

    @Autowired
    @Transient
    private ConfigUser configUser;

...

But at the end I decided to use a helper class, in order to avoid all the Spring roo generated code for this attribute. I don't want to add a private getter for configUser in order to prevent the Spring Roo's public one to exist and mark 'render="false"' in all the scaffolded views.

Now I have:

@Configurable
@RooJavaBean
public class Helper {

    @Autowired
    private ConfigUser configUser;

    @Autowired
    private SaldoClienteService saldoClienteService;
}

When I need the autowired bean, I use something like this:

ConfigUser configUser = new Helper().getConfigUser();

Again: Thank you very much @Ralph

elysch
  • 1,846
  • 4
  • 25
  • 43