Consider the following situation:
#Stateless
#Clustered
public class FacadeBean implements Facade {
#EJB
private Facade facade;
#Override
public void foo(List<Integer> ids) {
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void bar(Integer id) {
// save something to the db
}
}
Method foo gets called from outside the ejb. I want each id to be executed in its own transaction, so that data get's persisted directly to the database. It is not possible to have the foreach outside of this class. I am wondering what is the best way to do this? At the moment I am injecting the interface again, to step over the ejb boundaries (so that the TransactionAttribute get's evaluated).
Your approach as to circular reference is perfectly fine. Circular reference in EJBs is allowed. This is even mandatory in order to start out a new transaction, or an #Asynchronous thread (otherwise the current thread would still block).
#Stateless
public class SomeService {
#EJB
private SomeService self; // Self-reference is perfectly fine.
// Below example starts a new transaction for each item.
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.fooInNewTransaction(id);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void fooInNewTransaction(Long id) {
// ...
}
// Below example fires an async thread in new transaction.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void bar(Iterable<Long> ids) {
for (Long id : ids) {
self.fooAsynchronously(id);
}
}
#Asynchronous
public void fooAsynchronously(Long id) {
// ...
}
}
Only in older containers, this did not work, most notably JBoss AS 5 with the ancient EJB 3.0 API. That's why people invented workarounds like SessionContext#getBusinessObject() or even manually grabbing via JNDI.
Those are unnecessary these days. Those are workarounds not solutions.
I'd personally only do it the other way round as to transactions. The foo() method is clearly never intented to be transactional.
#Stateless
public class SomeService {
#EJB
private SomeService self;
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.foo(id);
}
}
public void foo(Long id) {
// ...
}
}
Depending on the concrete functional requirement, you could even make the foo(Long id) #Asynchronous, hereby speeding up the task.
Do you really have to have both methods in one class? You can move bar() to an own bean and make it transactional. Then you don't have to use this kind of self-injection.
You can also try to use SessionContext#getBusinessObject() method.
#Resource
SessionContext sessionContext;
#Override
public void foo(List<Integer> ids) {
Facade facade = sessionContext.getBusinessObject(Facade.class);
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}
Related
I want to use it for manipulating data from the repository with catalog service, but Autowired annotation is not injecting dependency.
#Service
public class PostEntityListener {
#PostLoad
public <T> void onPrePersist(T obj) {
if (obj != null) {
String type = obj.getClass().getSimpleName();
switch (type) {
case "Order":
break;
case "OrderItem":
try {
OrderItem orderItem = (OrderItem) obj;
if (orderItem != null) {
catalogService.save(orderItem);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
#Autowired
private CatalogService catalogService;
}
Here are my CatalogService details:
public interface CatalogService {
void save(OrderItem orderItem);
}
#Service
public class CatalogServiceImpl implements CatalogService {
public void save(OrderItem orderItem) {
catalogRepository.save(orderItem);
}
#Autowired
private CatalogRepository catalogRepository;
}
First of all, have in mind that, according to the JPA specification, you should not mess (either directly or indirectly) with the same persistence context that is invoking your lifecycle method:
In general, the lifecycle method of a portable application should not invoke EntityManager or query operations, access other entity instances, or modify relationships within the same persistence context [46] . [47] A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.
So your call to catalogService.save(orderItem); looks suspicious.
Regarding your main question, the problem is that EntityListener instances are instantiated by the JPA provider and not by Spring, so you cannot directly use Spring goodies in them. You can implement some workarounds, though, read this question for more details, specially Den B's answer looks easily adaptable to your code.
i have tried it , just follow below snippet code.
#Component
public class DeptItemTransferBean {
#Autowired
private CatalogRepository catalogRepository;
public void save(OrderItem orderItem) {
catalogRepository.save(orderItem);
I have solved this issue with setter-injection. I used the following code to inject dependency:
#Autowired
public void setCatalogService(CatalogService catalogService) {
this.catalogService = catalogService;
}
I'm not sure where to open my Transaction object. Inside the service layer? Or the controller layer?
My Controller basically has two services, let's call them AService and BService. Then my code goes something like:
public class Controller {
public AService aService = new AService();
public BService bService = new BService();
public void doSomething(SomeData data) {
//Transaction transaction = HibernateUtil.getSession().openTransaction();
if (data.getSomeCondition()) {
aService.save(data.getSomeVar1());
bService.save(data.getSomeVar2());
}
else {
bService.save(data.getSomeVar2());
}
//transaction.commit(); or optional try-catch with rollback
}
}
The behavior I want is that if bService#save fails, then I could invoke a transaction#rollback so that whatever was saved in aService would be rolled back as well. This only seems possible if I create one single transaction for both saves.
But looking at it in a different perspective, it looks really ugly that my Controller is dependent on the Transaction. It would be better if I create the Transaction inside the respective services, (something like how Spring #Transactional works), but if I do it that way, then I don't know how to achieve what I want to happen...
EDIT: Fixed code, added another condition. I am not using any Spring dependencies so the usage of #Transactional is out of the question.
You can accomplish what you're asking with another layer of abstraction and using composition.
public class CompositeABService {
#Autowired
private AService aservice;
#Autowired
private BService bservice;
#Transactional
public void save(Object value1, Object value2) {
aservice.save( value1 );
bservice.save( value2 );
}
}
public class AService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
public class BService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
This same pattern is typically used when you need to interact with multiple repositories as a part of a single unit of work (e.g. transaction).
Now all your controller needs to depend upon is CompositeABService or whatever you wish to name it.
I am using Spring MVC + Hibernate
//Class for Generic Methods for **save and update**
#Service("PersistenceTemplate")
#Transactional
public class PersistenceTemplate {
#Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
// SAVE
public <T> long save(T entity) throws DataAccessException {
Session session = sessionFactory.getCurrentSession();
long getGenVal=(Long) session.save(entity);
return getGenVal;
}
//UPDATE
public <T> void update(T entity) throws DataAccessException {
sessionFactory.getCurrentSession().update(entity);
}
}
AT Controller
#Resource(name = "PersistenceTemplate")
private PersistenceTemplate pt;
long result=pt.save(receiveTrxObj1);
pt.Update(receiveTrxObj2);
Problem statement
How to roll back save statement if Update fails to update the entity in database ?
You could use application level exception to rollback your entity operations. When this custom exception thrown the related operations rollback. Please see following documents to see how to define custom rollback in Spring.
first your #Service("PersistenceTemplate") should be marked as #Repository because its doing the work of DAO layer.
from the controller you should call a Service which should be annotated with #service and #Transactional and inside this service you create a method which will call a DAO layer.
if save or Update fails to update the entity in database the method from which it is called (ie. the method in service layer) will not complete and the transaction is cancelled automatically because persistence objects are synchronized with database near the end of the completion of method of service layer once the control comes back to it.
See the below example.
#Service("authorLoadService")
#Transactional
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorEntityLoadService implements EntitiesLoadService{
private AuthorDAO authorDao;//this is my DAO
#Autowired
#Qualifier("authorDAO")
public void setAuthorDao(AuthorDAO authorDao) {
this.authorDao = authorDao;
}
#Override
public void deleteEntities(Object o) {
// TODO Auto-generated method stub
}
#Override
public void loadEntities(Object o) {
Set<author_pojo> author=(Set<author_pojo>)o;
Iterator<author_pojo> itr=author.iterator();
while (itr.hasNext()) {
author_pojo authorPojo = (author_pojo) itr.next();
authorDao.save(authorPojo);
}
}
#Override
#Transactional(readOnly=true)
public List getEntities() {
// TODO Auto-generated method stub
return null;
}
#Override
#Transactional(readOnly=true)
public Object getEntity(Object o) {
String author=(String)o;
author_pojo fetAuthor=authorDao.findOneByName(author);
return fetAuthor;
}
}
My Abstract Generic DAO
public abstract class AbstractHibernateDAO<T extends Serializable> {
public Class<T> clazz;//class object reference
protected SessionFactory mysessionFactory;
#Autowired
public void setMysessionFactory(SessionFactory mysessionFactory) {
this.mysessionFactory = mysessionFactory;
}
public T findOneByName(final String name){
return (T) getCurrentSession().createQuery("from "+clazz.getName()).uniqueResult();
}
public void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T findOne(final Long id) {
return (T) getCurrentSession().get(clazz, id);
}
#SuppressWarnings("unchecked")
public List<T> findAll() {
return getCurrentSession().createQuery("from " + clazz.getName()).list();
}
public void save(final T entity) {
getCurrentSession().merge(entity);
}
public void update(final T entity) {
getCurrentSession().update(entity);
}
public void delete(final T entity) {
getCurrentSession().delete(entity);
}
public void deleteById(final Long entityId) {
final T entity = findOne(entityId);
delete(entity);
}
protected Session getCurrentSession() {
return mysessionFactory.getCurrentSession();
}
}
my concerete DAO
#Repository("authorDAO")
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorDAO extends AbstractHibernateDAO<author_pojo> {
public AuthorDAO() {
setClazz(author_pojo.class);
}
public author_pojo findOneByName(final String name){
System.out.println(clazz);
return (author_pojo) getCurrentSession().createQuery("from "+clazz.getName() +" where authorName=:name").setParameter("name", name).uniqueResult();
}
}
For you to be able to rollback the save if the update fails, the save and update have to occur within the same transaction. Services are a natural place to put DAO calls that need to execute within the same transaction.
Putting a #Transactional annotation on the controller method would create complications due to proxying the controller, see the Spring MVC documentation, 17.3.2:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
See this question for what goes in a service as opposed to in a controller.
My previous question How to wrap Wicket page rendering in a Spring / Hibernate transaction? has led me to thinking about transaction demarcation in Wicket.
Whilst the example there was easily solved by moving business logic down into a Spring-managed layer, there are other places where this is not possible.
I have a generic DAO class, implemented by Hibernate, with
public class HibernateDAO<T> implements DAO<T> {
protected final Class<T> entityClass;
private final SessionFactory sessionFactory;
#Transactional
public T load(Serializable id) {
return (T) getSession().get(entityClass, id);
}
#Transactional
public void saveOrUpdate(T object) {
getSession().saveOrUpdate(object);
}
}
and a generic model to fetch it
public class DAOEntityModel<T> extends LoadableDetachableModel<T>{
private DAO<T> dao;
private final Serializable id;
public DAOEntityModel(DAO<T> dao, Serializable id) {
this.dao = dao;
this.id = id;
}
public <U extends Entity> DAOEntityModel(DAO<T> dao, U entity) {
this(dao, entity.getId());
}
public Serializable getId() {
return id;
}
#Override
protected T load() {
return dao.load(id);
}
}
Now I have a minimal form that changes an entity
public class ScreenDetailsPanel extends Panel {
#SpringBean(name="screenDAO") private DAO<Screen> dao;
public ScreenDetailsPanel(String panelId, Long screenId) {
super(panelId);
final IModel<Screen> screenModel = new DAOEntityModel<Screen>(dao, screenId);
Form<Screen> form = new Form<Screen>("form") {
#Override protected void onSubmit() {
Screen screen = screenModel.getObject();
dao.saveOrUpdate(screen);
}};
form.add(
new TextField<String>("name", new PropertyModel<String>(screenModel, "name")));
add(form);
}
}
So far so good - thanks for sticking with it!
So my issue is this - when the form is submitted, the PropertyModel will load the screenModel, which will happen in the transaction delineated by the #Transactional dao.load(id). The commit of the changes will when the (different) transaction started for dao.saveOrUpdate(object) is committed. In between these times all bets are off, so that the object may no longer exist in the DB to be committed.
I'm never entirely sure with DB code and transactions. Should I just shrug this off as unlikely, although I could construct other more complicated but more dangerous scenarios? If not I can't see how to demarcate the whole page logic in a single transaction, which is what my instinct tells me I should be aiming for.
Typically you would solve this by putting the #Transactional annotation on a service-level class, used by your front-end layer code, which wraps around the DAO operations - so that the load and save happens within the same transaction. In other words, you can solve this by creating a layer of code between the form and the DAO code, a "service layer", which provides the business-level logic and hides the presence of DAOs from the presentation layer.
I've not yet implemented it, but I'm pretty sure that #ireddick solution in How to control JPA persistence in Wicket forms? of lazily starting a tx in in the Wicket request cycle is the best solution here. I'm going to accept this proxy for it to stop Stack Overflow nagging me to accept an answer.
I have two services, like this (simplified code):
#Service
public class OuterService {
#Autowired
InnerService innerService;
#Transactional
public void doSomething() {
List<SomeEntity> list = entityRepo.findByWhatever(...);
for(SomeEntity listElement : list) {
innerService.processEntity(listElement);
}
}
}
#Service
public class InnerService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processEntity(Entity entity) {
// ...
StatusElement status = new StatusElement(...);
statusElementRepo.save(status);
}
}
The constructed StatusElement is now inserted by exiting InnerService.processEntity() and inserted again by exiting OuterService.doSomething().
If I change the #Transactional annotation of OuterService.doSomething() to #Transactional(readOnly = true), it is inserted just once.
Is it a problem with MySql (because it may not support nested transactions), do I need a special transaction manager, or is there something wrong with my code? TIA!
I solved it by using programmatically transactions using the PlatformTransactionManager.
see: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-programmatic-ptm