LOAD and CACHE data scoped with @Singleton and @Stateless
I'm looking for elegant solutions to the old problem of loading and caching static shared data at application startup (with infinite lifecycle).
My old way was Spring Singleton Bean, but now I'm trying to achieve it with JAVA EE 6 (JPA2, EJB3.1, CDI).
I have @Entity
and the @Stateless
EJB lo is loading an object from the database. My thought was to add an @Singleton
EJB for data caching; I also decided to keep the original EJB separate so as not to break the SRP (and because it might be used by other actors to bypass the cache in the future).
Please take a look at this simplified proof of concept :
An object
@NamedQuery(name="Room.findAll", query="SELECT r FROM Room r")
@Entity
public class Room {
@Id
private Integer id; // GETTER, SETTER
private String description; // GETTER, SETTER
}
Loader
@Stateless
public class Rooms {
@PersistenceContext
EntityManager em;
public List<Room> findAll() {
return em.createNamedQuery("Room.findAll",Room.class).getResultList();
}
}
Cacher
@Singleton
public class RoomsCached {
@EJB
Rooms rooms;
private List<Room> cachedRooms; // GETTER
@PostConstruct
public void initCache(){
this.cachedRooms = Collections.unmodifiableList(rooms.findAll());
}
}
Can you see big problems, concept errors, or something in this example?
My main problems were
-
If both were
@Singleton
(mehh), I could add@DependsOn("Rooms")
a Bean to the cacher to ensure that Rooms are already loaded before use, but with@Singleton
and@Stateless
I cannot ... will the@Stateless
bean always load before CDI injects it@Singleton
? -
@Singleton
the call@Stateless
seems strange (I've seen examples of the opposite); should I change the design by putting an instance@Singleton
inside an@Stateless
EJB? -
Is it correct to load and cache the method
@PostConstruct
?
source to share
Ok I did some tests and I tried it as well @Decorator
. This still seems to be the best.
The @Entity bean and @Stateless bean are the same as the question, while I modified the @Singleton bean as follows, adding the classic time cache as well:
@Singleton
public class RoomsCached {
@Inject
Rooms rooms;
private List<Room> cachedRooms;
private long timeout = 86400000L; // reload once a day
private long lastUpdate;
public List<Room> getCachedRooms() {
initCache();
return cachedRooms;
}
public void initCache() {
if (cachedRooms == null || expired()) {
cachedRooms = Collections.unmodifiableList(rooms.findAll());
lastUpdate = System.currentTimeMillis();
}
}
private boolean expired() {
return System.currentTimeMillis() > lastUpdate + timeout;
}
}
No @PostConstruct or @EJB needed, no problems syncing with the base, @ inject-ed @Stateless bean.
It works great.
source to share