Consider this scenario:
#Stateless
public class A{
#PersistenceContext
private EntityManager em;
#EJB
B b;
#EJB
C c;
public void doSomeWork(){
b.doSomeWork();
c.doSomeWork();
em.persist(someStuff);
}
}
#Stateless
public class B{
#PersistenceContext
private EntityManager em;
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
#Stateless
public class C{
#PersistenceContext
private EntityManager em;
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
In this scenario I'm using three different EntityManagers for the same transaction (started in method doSomeWork() from class A
Now, the same transaction but with only one EntityManager:
#Stateless
public class A{
#PersistenceContext
private EntityManager em;
#EJB
B b;
#EJB
C c;
public void doSomeWork(){
b.setTheEntityManager(em);
b.doSomeWork();
c.setTheEntityManager(em);
c.doSomeWork();
em.persist(someStuff);
}
}
#Stateless
public class B{
private EntityManager em;
public void setTheEntityManager(EntityManager em){
this.em = em;
}
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
#Stateless
public class C{
private EntityManager em;
public void setTheEntityManager(EntityManager em){
this.em = em;
}
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
I've been reading about the EntityManager lifecycle but I just can't get if there's any advantage (in terms of performance, database connections,..) in the second scenario. My first answer would be yes because there's only one EntityManager but on the other hand I don't see any code examples using this approach (passing the EntityManager from EJB to EJB). Also, the Oracle Docs say "By automatically propagating the persistence context, application components don’t need to pass references to EntityManager instances to each other in order to make changes within a single transaction. The Java EE container manages the lifecycle of container-managed entity managers."
Anything you #Inject or #Resource from the container should never ever be passed somewhere else. That's a surefire way to create a memory leak and/or undefined behavior. There also is no advantage to doing the second, as the container automatically injects the correct EntityManager for you every time :)
So in short, never ever do the second scenario, always do the first scenario. The container is actually inject a Proxy that routes you to the correct (the same in your case since you haven't exited your transaction boundary) entity manager.
What's happening is the call into a.doSomeWork() has an implied TransactionAttribute.Required. If a transaction has not already been started, the container starts one for you. Subsequent calls to B and C now participate in that transaction. When you exit a.doSomeWork(), assuming that's where the transaction started, that's where the transaction commits. Anything done by A, B, and C all occur in the same transaction.
Related
#Stateless
public class MyAccountsBean {
#Inject SomeEntityClass someOtherBean;
#Resource UserTransaction jtaTx;
#PersistenceContext(unitName="AccountsPU") EntityManager em;
#Resource QueueConnectionFactory accountsJMSfactory;
#Resource Queue accountPaymentDestinationQueue;
public List<Account> processAccounts(DepartmentId id) {
// Use all of above instance variables with no additional setup.
// They automatically partake in a (server coordinated) JTA transaction
}
}
There are likely multiple issues, but one that sticks out is that all of the fields should be marked with the private modifier.
In addition, can you post the rest of your codebase and the error you are getting?
I have a minimal spring boot application, consisting of 3 classes: an Entity, a component that tries to populate db in #PostConstruct and an application class. Nothing else.
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
#Component
#Transactional
public class Initializer {
#Autowired
EntityManager em;
#PostConstruct
public void populate() {
em.persist(new MyEntity());
}
}
#Entity
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
}
When I run the application I get an javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
I'm not the only one ever getting that error and I read a lot of the posts, but did not find a magic solution.
If I autowire an EntityMananagerFactory and instead do:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new MyEntity());
em.getTransaction().commit();
em.close();
It works. Question is: Is there a simpler way (place the right annotation at the right spot) to get an EntityManager that can persist an entity? I have good reasons for not creating a repository (I tried doing that and that works).
Best regards Jens
So after trying a lot of different stuff, I think that I found a working solution, where the initalization is done in an ApplicationReadyEvent handler instead of in a #PostConstruct method:
#Component
public class Initializer {
#PersistenceContext
EntityManager em;
#EventListener(ApplicationReadyEvent.class)
#Transactional
public void init() {
em.persist(new MyEntity());
}
}
Working example: https://github.com/djarnis73/spring-boot-db-init-with-jpa-entity-manager
As per my understanding, #PostConstruct gets called on app startup when we want to initialize the beans and the configs. I think #PostConstruct is not the right place to do so.
However you can use #PersistenceContext on your entityManger instead of autowiring it.
We have set up the Spring Framework like this:
#Eager
public interface CatalogElementRepository extends PagingAndSortingRepository<CatalogElementEntity, Long> {
}
#Service
public class CatalogImpl implements CatalogManager {
#Inject
CatalogElementRepository catalogElementRepository;
#Override
public CatalogElement createCatalogElement(CatalogElementEntity catalogElement) {
return this.catalogElementRepository.save(catalogElement);
}
}
#Stateless
#Remote(CatalogManager.class)
public class CatalogManagerBean implements CatalogManager {
#Inject
CatalogManager delegate;
#Override
public CatalogElement createCatalogElement(CatalogElementEntity catalogElement) {
return this.delegate.createCatalogElement(catalogElement);
}
}
So whenever someone calls the method on the remote interface createCatalogElement, I'd assume the entity gets stored in the database. It does not (weirdly enough, findOne still returns the very same entity, but it can't be found via findByProperty).
Other questions said to add #Transactional, so I added #javax.transaction.Transactional and org.springframework.transaction.annotation.Transactional on the methods and classes to be on the safe side, nothing worked.
What could be the problem?
I don't see any configuration files for the Spring Framework, but it's a legacy project, so they might just be hidden very well.
For some reason using this class as a producer for the EntityManager helped:
public class SpringConfig {
#PersistenceUnit
EntityManagerFactory emf;
#PersistenceContext
EntityManager em;
#Produces
#ApplicationScoped
public EntityManagerFactory createEntityManagerFactory() {
return this.emf;
}
#Produces
public EntityManager createEntityManager() {
return this.em;
}
public void close(#Disposes EntityManagerFactory entityManagerFactory) {
entityManagerFactory.close();
}
public void close(#Disposes EntityManager entityManager) {
entityManager.close();
}
}
I was reading a Java EE book recently, and apparently entity beans were recently removed from the EJB specification. You are supposed to use JPA instead. But I want entity beans!! What I am really looking for is a JPA persistent entity that is remotely accessible, like an EJB. Something like this:
#Entity
#Remote(MyEntityRemote.class)
#LocalBean
public class MyEntityEJB implements MyEntityRemote {
public void doSomething() {
// actually do something
}
}
Is this at all possible without removing the bean annotations and writing a session bean like this:
#Stateless
#Remote(StatelessInterfaceToMyEntityRemote.class)
#LocalBean
public class StatelessInterfaceToMyEntity implements StatelessInterfaceToMyEntityRemote {
public void doSomething(MyEntity entity) {
entity.doSomething();
}
}
If I understand you correctly it is possible
first you create an Entity:
#Entity
#Table('MyEntityTable')
public class MyEntity {...}
Then you create a session bean facade for the entity exposing through it any interfaces you may need
#Stateless //Facade is a seesion bean so it can be #Stateless or #Statefull for basic CRUD it shoud be #Stateless
public class EntityFacade extends AbstractFacade<MyEntity> {
#PersistenceContext(unitName = "MyPersistanceUnit") //remember to define it first
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
Now u can define any methods that work with your Entity class. Remember if you expose your entity via Remote Interfaces it will be in a detached state. So after updating you will first nee to use the merge(object) method of EntityManager
EDIT
Abstract facade is a concept that is used with JPA Entity, NetBeans in version 7.3 generates it for you automatically. It is used to define the most common operations on Entities so you dont have to repeat the code for every Entity. It goes like this
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
...
}
The function above do some basic CRUD operations without much effort. So extending the facade gives you the ability to have does operations defined you could say out of the box. Of course this just for basic configurations the Entity facade can use many entities and do some business logic before it persists anything. So in your case it would go like this:
public class EntityFacade extends AbstractFacade<MyEntity> {
#PersistenceContext(unitName = "MyPersistanceUnit") //remember to define it first
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public doSomething(MyEntity entity)
{
entity.get(...);
...
entity.set(...)
if(iWantToPesristIt)
edit(entity)
else
return;
}
}
I'm using decorator pattern in java ee 7 (glassfish 4).
I've this decorator
#Decorator
public class FooIndexer implements FooService {
#Inject
#Delegate
FooService fooService;
private Logger logger = Logger.getLogger(FooIndexer.class.getName());
//#Inject
//Indexer indexer;
#Override
public Foo create(Foo foo, boolean index) {
fooService.create(foo, index);
if (index) {
System.out.println("Im in");
}
return foo;
}
}
And this ejb class
#Stateless(name = "fooService")
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#DeclareRoles({"ADMINISTRATOR", "USER"})
public class FooServiceImpl implements FooService {
#PersistenceContext(unitName = "foo")
private EntityManager em;
#Resource(lookup="java:comp/EJBContext")
private SessionContext ctx;
/** CRUD **/
#RolesAllowed("ADMINISTRATOR")
public Foo create(Foo foo, boolean index) {
Principal cp = ctx.getCallerPrincipal();
System.out.println(cp.getName());
em.persist(foo);
return foo;
}
}
When I use this decorator pattern, EntityManager in EJB is null (without decorator, everything goes fine). I supose is because of decorator use #Inject instead of #EJB annotation (#EJB annotation can't be used in #Decorator), and EntityManager is not being injected.
But, what can I do to get entitymanager will be injected using #decorator?
Thank you
Try adding an empty beans.xml in your META-INF, this will activate CDI bean discovery. I had a similar issue with my project.
See oracle doc here : http://docs.oracle.com/javaee/6/tutorial/doc/gjbnz.html
You must create an empty beans.xml file to indicate to GlassFish Server that your application is a CDI application. This file can have content in some situations, but not in simple applications like this one.
http://docs.oracle.com/javaee/6/tutorial/doc/gjbju.html#gjcvh
Good luck !
Alexander Kirilov