9

After constructing the bean, I want to retrieve data from the database, using the EntityManager. It is not possible in the constructor, because the EntityManager is injected after the constructor is called. So I tried to do it in a method annotated with @PostConstruct. According to the API, a PostConstruct Methods gets called after all injection are done. Executing the query works, but it always returns an empty list. If I use the same query in an other method, it returns the correct result. Does anybody know, why it does not work in the PostConstruct method?

@Stateful(mappedName = "price")
@Singleton
@Startup
public class PriceManagementBean implements PriceManagement {

    @PersistenceContext
    private EntityManager em;

    private List<PriceStep> priceSteps =  Collections.synchronizedList(new ArrayList<PriceStep>());


    public PriceManagementBean(){


    }


    @PostConstruct
    public void init(){
        javax.persistence.Query query = em.createQuery("SELECT ps FROM PriceStep ps");
        List<PriceStep> res = query.getResultList();
            .....
       }
}
punkyduck
  • 669
  • 2
  • 9
  • 17
  • See http://stackoverflow.com/questions/2399769/is-it-okay-to-pass-injected-entitymanagers-to-ejb-beans-helper-classes-and-use – mglauche Apr 30 '12 at 14:24
  • How and where are the PriceSteps added to the db? – Puce Apr 30 '12 at 14:31
  • Your bean is annotated both `Stateful` and `Singleton`, which isn't allowed. If your EJB container doesn't support EJB 3.1, perhaps the bean is actually stateful, and the PostConstruct is running with an undefined transaction context, and your application server doesn't support that well? What application server are you using? – Brett Kail Apr 30 '12 at 15:05
  • Bean has to be Stateful or Singleton, both it will not work. If you are using EJB3.1, only @Singleton will work since call back are transactional. If you are using EJB3.2, both (separately) will work. Take a look on this link : https://blogs.oracle.com/arungupta/entry/what_s_new_in_ejb – Riadh Jan 18 '17 at 15:08

2 Answers2

9

Does anybody know, why it does not work in the PostConstruct method?

Reason 1 You cannot create a bean that is at the same time @Stateful and @Singleton(Well you can but it will make no sense since Singletons are also Stateful), that is one of the reasons you are having trouble. There is no exceptions, but there is a conflict in there, you need to fix that first.

Just remember:

  • A Singleton bean is a bean that mantains its state. There is only one instance of a Singleton in an application and it is shared among all the users of the app. Also since it is a shared(maybe better say concurrent) bean, there is need to implement some kind of locking mechanism using the @Lock annotation.

  • A Stateful bean is a bean that mantains each state after a transaction. When working with
    Stateful beans each user gets a copy of the bean which will last as long as the session - lasts or until a method annotated with @Remove is called

Reason 2 Even if it works, you will be unable to access the results, because you are storing them in an object called res which is only accessible from inside the method init(). I suppose you would like to assign that returned value to the variable priceSteps.

Anyway there are many things wrong in your code, for not saying everything. I don't know what are your system requirements, but here i would give you a simple solution that will allow you to access the database:

I suppose you are trying to in some way return the data in the life cycle of the bean because you want to avoid sending queries again and again if the bean is @Stateful. The thing is, you don't have to do that, you can still make your bean @Stateless and avoid stressing your database with many queries. What you need to do is create a @NamedQuery.

So annotate your entity PriceStep with @NamedQuery and there enter the query string you wrote. In this link you will find information about how to use @NamedQueries: http://docs.oracle.com/cd/B31017_01/web.1013/b28221/ent30qry001.htm

The next thing i would suggest you is to annotate your class PriceManagementBean as *@Stateless*. Don't worry if in each request a new entityManager is created, that does not stress the database at all, because it interacts with the domain model. You don't need @PostConstruct, you just call your @NamedQuery whenever you need it and that's it. The app server will cache it and give it back to each user that requires it without interacting with the database all time.

Here a codesnipet:

@Entity
@NamedQuery(
    name="allPriceSteps",
    queryString="SELECT ps FROM PriceStep ps"
)
public class PriceStep implements Serializable {
...
}

Now the bean:

@Stateless
public class PriceManagementBean implements PriceManagement {

    @PersistenceContext
    private EntityManager em;

    public List<PriceStep> getAllPriceSteps() {
         Query query =  em.createNamedQuery("allPriceSteps");
         return query.getResultList();
     }
}

I hope this is useful. If you give more information about your system requirements we could give you advice on a best practice.

javing
  • 12,307
  • 35
  • 138
  • 211
  • I am experiencing the exact same problem, but your answer doesn't cover my case. We have a cache as a `@Singleton` bean, which gets populated in the `@PostConstruct` method. This has worked well but I am starting to experience problem during rewrite of tests. I think the reason is that previously we ran the tests against an already populated database, but we now try to use HSQLDB in-memory. Since the test run in a transaction, I suspect that the code in `@PostConstruct` is not part of the same transaction, resulting in an empty list. Do you have any ideas about that? – Magnilex Oct 03 '14 at 10:30
  • Same here, and I am not using statefuls or anything like that. – mjs Jan 04 '15 at 10:27
  • If you make it `@Stateless` then you are dealing with a pool of them, which defeats the purpose of `@Singleton`. I'm not sure how using a named query is supposed to resolve the issue of the `@PostConstruct` not yet having a transaction? – Alkanshel Jul 25 '19 at 20:30
-2

Based on your requirement please try the following

  • Remove @Stateful [CAN'T use both at a time]

  • @Startup will initialize singleton bean during APPLICATION INIT [Please note the application was NOT fully initialized]. This might caused some issue in loading EntityManager and i assume the EntityManager bridge was not completely initialized. Try to call the init after complete application startup [i.e.,] Remove @Startup

  • But then I have to call into it with some other outside initialization code, that's exactly what I'm trying to avoid with @Startup – Alkanshel Jul 25 '19 at 20:19