Say I have this setup:
MyDAO1.java:
public class MyDAO1 {
#Resource(name="myLibraryDAO")
private MyLibraryDAO myLibraryDAO;
#Transactional(value="myTransactionManager")
public void foo() {
myLibraryDAO.bar();
}
}
MyLibraryDAO.java
public class MyLibraryDAO {
#Resource(name="myUtilityDAO")
private MyUtilityDAO myUtilityDAO;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void bar() {
myUtilityDAO.baz();
}
}
In my spring context file, I am not specifying any default transaction-manager in my tx:annotation-driven directive (primarily because I have other data sources and their transaction managers and I don't want to declare one as the default). MyDAO1 and MyLibraryDAO operate on potentially different datasources (and I am not looking to do any XA-style management) and the requirement is that MyLibraryDAO not operate on the existing transaction (if one exists). To solve this, I have used propagation=REQUIRES_NEW.
Now, when I use #Transactional(propagation=REQUIRES_NEW), I want to know if there is a way that I can have it reuse the attributes of the transaction manager of the outer transaction if available? Since the MyLibraryDAO is intended to be a library and hence devoid of any specific usage related information, what is the best way to have it configured at runtime (based on the invoking DAO)?
Right now, it plainly fails when looking for the default transaction manager bean named "transactionManager".
I know I might be sounding confused, but would appreciate if someone asked more pointed questions.
Thanks!
Related
I have a method that is defined as #transactional. In fact I have a method calling a method that calls a method and all three are #transactional. The transactional logic worked fine, until I pulled a few methods out into an abstract class for some code reuse, which appears to have broken my logic somehow.
The transactional method is from an abstract class, here is a partial snippet of the relevant parts (I have to rewrite this by hand so forgive me for typos):
public abstract class ReadWriteService<ReadEntityTempalte extends IEntity, WriteEntityTemplate extends IEntity>
//extends jpaRepository, created using #enableJpaRepositories
private searchRepository<WriteEntityTemplate, String> writeRepository;
#PersistenceContext
private EntityManager em;
#transactional
public ReadEntityTemplate save(final WriteEntityTemplate entity){
if(entity == null) return null;
WriteEntityTemplate returnValue = writeRepository_.save(entity);
postSave(returnValue); //checks our security logic
flush();
ReadEntityTemplate returnEntity = find(returnValue.getId());
//required to detect changes made to the view by our save
em.refresh(returnEntity);
}
It's written this way because we are using views so the return value may be modified in the find() to the view. This logic worked in the past, and still works for a number of calls.
The method that fails is:
#Override
#transational
public void configure(EntityFileConfig config) throws ClassNotFoundException{
//load config from file
for(EntityConfig entityConfig: entityConfigs){
EntityType entityType=EntityTypeService_.find(entityConfig.getKey());
if(entityType==null){
entityType = EntityType.createByRequiredFields(entityConfig.getKey());
}
//update entityType to reflect config file.
entityType = entityTypeService_.save(entityType);
for(String permissionName: entityConfig.getPermissions()){
if(!entityTypeService_.hasPermission(entityType, permissionName)){
Permission permission = permissionSetup.getPermission(permissionName);
if(permission!=null)
//fails on below lines
permissionService._.addPermission(entityType, permission);
}
}
}
}
both the entityTypeService and the permissionService extend the above abstract class and use the same save method without alteration, addPermissions is a forloop that calls save on each permission.
The entityTypeService works, but the permissionService fails. When The permission service is called if I do em.isTransactionalEntity it returns false.
All #transactional annotations are using the spring annotation, not the javax one.
Actually, it seems as if a few of the permissions would save and others wouldn't, almost as if it's non-deterministic, but this may simple be due to modifying a database file that had some of the values already set and thus didn't need to run some of the logic the first time through.
I've done quite a bit of stumbling around but am no closer to determining what would cause my transaction to end. I had thought perhaps it was the #persistenceContext, since the JPARepos get their entityManager through a different approach then autowireing with #persistenceContext, but if that were the case everything would fail?
Any help would be appreciated, I'm pretty stumped on the cause of this.
Assuming you have enabled #EnableTransactionManagement on #Configuration class.
Since you didn't set any propagation on #Transaction the default value is Required. It means all methods must be part of transaction. Since one of your abstract methods is not part of the #Transactional hence the error.
For more information on Spring Transactions.
Note: Image taken from above link.
First of All I have to apologize for the semi-psuedo code as the classes I am going to reference are all too big and are pointless except for a few lines of each class to ask my question.
To Start off I am using
Jboss 6.3.2 EAP
Hibernate
EJB 3.0
I have two different EJB Classes
They are
Service Layer
#Stateless
#TransactionManagement
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class ServiceClass implements ILocalService
{
#EJB
ILocalDao localDao;
#Override
public myObject find(String id)
{
localDao.findByPK(id);
}
#TransactionalAttribute(TransactionAttributeType.REQUIRED)
#Override
public void create(myObject obj)
{
localDao.create(obj);
}
#TransactionalAttribute(TransactionAttributeType.REQUIRED)
#Override
public void update(myObject obj)
{
localDao.findByPK(obj.getId());
localDao.update(obj);
}
}
DAO Layer
#Stateless
#TransactionalAttribute(TransactionAttributeType.MANDATORY)
public class DaoClass extends AbstractDaoClass<myObject> implements ILocalDao
{
#Override
public myObject findByPK(String id)
{
super.find(id);
}
}
Okay with those two classes laid out. Trust me that everything else is correctly configured interface Classes, Abstract Class, persistence.xml, etc.
The question is How does the find method in the ServiceClass work? With the NOT_SUPPORTED being assigned to this method I would think that the DaoClass call would throw a Exception because no Transaction has been started yet and I am making the entire class MANDATORY. But my running app says otherwise.
To verify that no transaction is being created on the DaoClass.find method. I put in the #TransactionalAttribute(TransactionAttributeType.NEVER). And re-ran the application. Still works.
To verify that the NEVER works I call the ServiceClass's update method. Throws an exception as expected.
To round this all out I have looked at many online resources(http://docs.oracle.com/javaee/6/tutorial/doc/bncij.html, etc) I have looked into the source files for the EJB 3.0 and Jboss...I am not seeing anything.
So questions I have pondered
Is the TransctionAttributeType.MANDATORY on the class level not
working as I expect it to in the DaoClass?
TransactionManagement on the ServiceClass creating a Transaction on init of the ServiceClass and then the NOT_SUPPORTED suspending this transaction?
I am in the process of writing up some tests to hopefully log the transaction's behavior and see what is going on. But hopefully someone here can shed some light sooner and relieve this huge question mark I have.
Thanks in advance.
First, #TransactionManagement without parameters is useless, since default value is TransactionManagementType.CONTAINER and EJB uses TransactionManagementType.CONTAINER implicitly.
JBoss 4.x
EJB 3.0
I've seen code like the following (greatly abbreviated):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class EJB1 implements IEJB1
{
#EJB
private IEJB1 self;
#EJB
private IEJB2 ejb2;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod1()
{
return someMethod2();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod2()
{
return self.someMethod3();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod3()
{
return ejb2.someMethod1();
}
}
And say EJB2 is almost an exact copy of EJB1 (same three methods), and EJB2.someMethod3() calls into EJB3.someMethod1(), which then finally in EJB3.someMethod3() writes to the DB.
This is a contrived example, but have seen similar code to the above in our codebase. The code actually works just fine.
However, it feels like terrible practice and I'm concerned about the #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) on every method that doesn't even actually perform any DB writes. Does this actually create a new transaction every single time for every method call with the result of:
new transaction
-new transaction
--new transaction
---new transaction
...(many more)
-------new transaciton (DB write)
And then unwraps at that point? Would this ever be a cause for performance concern? Additional thoughts?
Does this actually create a new transaction every single time for
every method call
No, it doesn't. The new transaction will be created only when calling method by EJB reference from another bean. Invoking method2 from method1 within the same bean won't spawn the new transaction.
See also here and here. The latter is exceptionally good article, explaining transaction management in EJB.
Edit:
Thanks #korifey for pointing out, that method2 actually calls method3 on bean reference, thus resulting in a new transaction.
It really creates new JTA transaction in every EJB and this must do a serious performance effect to read-only methods (which makes only SELECTS, not updates). Use #SUPPORTS for read-only methods
My application loads entities from a Hibernate DAO, with OpenSessionInViewFilter to allow rendering.
In some cases I want to make a minor change to a field -
Long orderId ...
link = new Link("cancel") {
#Override public void onClick() {
Order order = orderDAO.load(orderId);
order.setCancelledTime(timeSource.getCurrentTime());
};
but such a change is not persisted, as the OSIV doesn't flush.
It seems a real shame to have to call orderDOA.save(order) in these cases, but I don't want to go as far as changing the FlushMode on the OSIV.
Has anyone found any way of declaring a 'request handling' (such as onClick) as requiring a transaction?
Ideally I suppose the transaction would be started early in the request cycle, and committed by the OSIV, so that all logic and rendering would take place in same transaction.
I generally prefer to use additional 'service' layer of code that wraps basic DAO
logic and provides transactions via #Transactional. That gives me better separation of presentation vs business logic and is
easier to test.
But since you already use OSIV may be you can just put some AOP interceptor around your code
and have it do flush()?
Disclaimer : I've never actually tried this, but I think it would work. This also may be a little bit more code than you want to write. Finally, I'm assuming that your WebApplication subclasses SpringWebApplication. Are you with me so far?
The plan is to tell Spring that we want to run the statements of you onClick method in a transaction. In order to do that, we have to do three things.
Step 1 : inject the PlatformTransactionManager into your WebPage:
#SpringBean
private PlatformTransactionManager platformTransactionManager;
Step 2 : create a static TransactionDefinition in your WebPage that we will later reference:
protected static final TransactionDefinition TRANSACTION_DEFINITION;
static {
TRANSACTION_DEFINITION = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
((DefaultTransactionDefinition) TRANSACTION_DEFINITION).setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
}
Feel free to change the TransactionDefinition settings and/or move the definition to a shared location as appropriate. This particular definition instructs Spring to start a new transaction even if there's already one started and to use the maximum transaction isolation level.
Step 3 : add transaction management to the onClick method:
link = new Link("cancel") {
#Override
public void onClick() {
new TransactionTemplate(platformTransactionManager, TRANSACTION_DEFINITION).execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
Order order = orderDAO.load(orderId);
order.setCancelledTime(timeSource.getCurrentTime());
}
}
}
};
And that should do the trick!
I am trying to inject entity manager in some helper class, I can pass it to the helper from the session bean, but the problem is I need to use the entity manager in the static init block of the helper class (some thing like):
class MySessionBeanHelperClass
{
// staff here...
static
{
SomeClass s = new SomeClass(entityManager);
}
}
So, I think the only way is to lookup the entity manager instead of injecting it. and also using the passed SessionContent will not work here. (is it????) (this is the first question)
The second question is:
If I used the ordinary way to lookup a resource (in this case the entity manager) (something like the following:)
Context ic = new InitialContext();
em = (EntityManager) ic.lookup("java:comp/env/persistence/em");
Is this will convert all transactions used by this entity manager to Bean-managed transaction??
Thanks!
I don't think it's a good idea to do that from static initializer. You have to be sure that all necessary services (such as JNDI, JPA) are up before the lookup occurs, but you can't guarantee that when you do it from a static initializer.
It's a known problem in EJB that there is no "standard" way of performing one-time task upon app. start/stop, but you can use the trick in the following link:
How to perform a DB cleanup operation upon shutdown in an EJB container
The example is for performing action upon app. stop, but you can override Servlet#init instead.
Answer to your second question, No.
First question, its not really a good idea. BTW, what are you up to? In case you need EntityManager in your helper class, its better to make it a private instance level variable, and pass that from your session bean using helper class constructor.