how to implement a cache in ejb 3.0?

2019-03-30 07:51发布

问题:

I have a customer who's stuck in an EJB 3.0 environment. No @Singleton, no bean-managed concurrency :-(

Considering thread management and synchronization is forbidden by the ejb specification, how to implement a cache? In essence, I want an non-synchronized object cache for some costly operations.

回答1:

The restriction of using static field and synchronization is stated in EJB 3.0 spec chapter 21.1.2. It also explains why.

• An enterprise bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final.

This rule is required to ensure consistent runtime semantics because while some EJB containers may use a single JVM to execute all enterprise bean’s instances, others may distribute the instances across multiple JVMs.

• An enterprise bean must not use thread synchronization primitives to synchronize execution of multiple instances.

This is for the same reason as above. Synchronization would not work if the EJB container distributed enterprise bean’s instances across multiple JVMs.

If you implement a cache by singleton POJO, you may have the risk that actually there are multiple cache instances in each JVM if EJB container distributed EJB instances across multiple JVMs, for instance, in a cluster environment.

So it depends,

  • If the application isn't deployed in cluster environment, you could do that. As far as I know, WebLogic and GlassFish run one server instance in one JVM.
  • If data consistency is not restrictedly needed, you could also do that. Usually it's allowed when talking about "cache".

If it doesn't work for you, probably you should think about caching data outside of EJB container, e.g. put in Redis or Hazelcast.



回答2:

I doubt whether I understood your question clearly or not! Following example is based on weblogic and eclipselink.

If you are using eclipselink JPA for your EJB, you can use cache for your every entities separately like this:

@Entity
@Cache(type=CacheType.SOFT)

If you want, you can enable caching for all the entities from the persistence.xml file also. This will be like this inside the property field(oracle doc's link):

<persistence-unit name="XYZ">
...
    <class>com.stack.Test</class>
    <properties>
        ...
        <property name="toplink.cache.type.default" value="Soft"/>
        ...
    </properties>
</persistence-unit>

Cache type can be different like Soft, Hard, Weak, Full, etc. Each of them is having their own meaning. You can find more about this cache type from here.



回答3:

Are even volatile variables forbidden in an EJB container? A volatile int variable provides some guarantees that are useful for multithreaded code. If a thread sees the change of a volatile variable, it is safe to assume that it sees everything that happened before.

Another idea would be a @Stateful bean that is instantiated only once (calls will be serialized by the container). I'm not sure about the implementation details, though.



回答4:

Well, I think that ConcurrentSkipListMap could be the solution: no synchronization but still thread-safe.