Hi I'm trying to Process some Employees objects and Persist in Database. The Service layer may get an exception from DAO layer. But I still need to process other employee records.
Please look the below Class Example. Here Class A.processEmployees() calls other class EmpHandler which calls DAO. Even if some exception came for an employee, I need other Employees to be processed and Persisted in Database. However, I'm getting Exception as org.hibernate.HibernateException: Current transaction is not in progress
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
Class A
{
public void processEmployees(List<Employee> empList)
{
for (Employee emp : empList)
new EmpHandler().processEmployee(emp);
}
}
You create EmpHandler by call the new method. Therefore that's methods do not handle by Spring Framework and some other containers. You do not should create the EmpHandler object by yourself. Spring Framework should create this object.
You can do following:
Class A
{
private IEmpHandler handler;
public void setEmpHandler(IEmpHandler handler) {
this.handler = handler;
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void processEmployees(List<Employee> empList)
{
for (Employee emp : empList)
handler.processEmployee(emp);
}
}
class EmpHandler implements IEmpHandler {
#Override
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void processEmployee(...) {
}
}
In your context.xml:
<bean id="handler" class="...EmpHandler"/>
<bean id="a" class = "...A">
<property name="handler" ref="handler"/>
</bean>
Related
I have the next problem. When a controller invokes a service method with #Transactional annotation and this method invokes a #Transactional(readonly=true) method, the changes in the object doesn't get saved in the database. I don't know why. I thought that the first method begins a 'read and write' transaction and the second method uses the same transaction.
Example :
/** Controller class **/
#RestController
#RequestMapping("/path1")
public class MyObjectRestController {
...
#Autowired
MyObjectService myObjectService;
#PostMapping(value = "/path2")
#ResponseStatus(HttpStatus.CREATED)
public void save(#RequestBody MyObject myObject) {
myObjectService.save(myObject);
}
...
}
/** Service class **/
public interface MyObjectService {
public MyObject save(MyObject myObject);
public MyObject findOne(long id);
}
/** Service implementation class **/
#Service
public class MyObjectServiceImpl implements MyObjectService {
/**
* Save Object
*/
#Override
#Transactional
public Object save(Object newObject) {
Object myObject = this.findOne(newObject.getId());
// Modify Object
myObject.setAttribute1(...)
myObject.setAttribute2(...)
// Save Object (using Repository)
return MyObjectDao.save(myObject);
}
/**
* Get Object by Id
*/
#Override
#Transactional(readOnly = true)
public MyObject findOne(long id) {
Optional<MyObject> myObject = MyObjectDao.findById(id);
return myObject.isPresent() ? myObject.get() : null;
}
}
This example doesn't update the object attributes. But if I remove the #Transactional annotation in save(..) method ... this example works successfully! (the object is modified).
Why is this happening?
I see save method is annotated with #Transactional and calls findOne method which is also #Transactional. So in this case the same transaction is getting propagated to findOne method and it applies readonly behaviour to the current transaction. And thus it does not perform Flush and you see entity is not modified.
So in this case, you should use a separate transaction for read and writes.
findOne method can be annotated with below -
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
I know this has been asked many, MANY times before - but I can't see an exact answer to this particular situation:
I have a controller class which deals with #RequestMapping(params = "type", method = RequestMethod.GET) onLoad(...) and #RequestMapping(method = RequestMethod.POST) onSubmit(...) in two separate methods.
Those methods then call
#Transactional()
void load(TypeOfForm form, Long id, TypeOfSessionParams sessionParams);
and
#Transactional()
void store(TypeOfForm form);
on the logic class respectively.
The load method goes off to the dao and gets an instance of a Model from the database; but the model contains the following:
#OneToMany(mappedBy = "company", cascade = CascadeType.PERSIST)
public Set<CompanyLocations> getCompanyLocations() {
return companyLocations;}
This isn't called until the store() method. Because it's lazy loading, I'm getting:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.orgName.modelName.companyLocations, could not initialize proxy - no Session
The typical answers I see around are:
add #Transactional() annotation
I have this already, on the load() and store() methods. Am I doing this wrong? Because it doesn't seem to help (I think because the getCompanyLocations method is called in store(), which is not the same transaction as when the model object was initially created)
add , fetch = FetchType.EAGER to the model property's get method
This may work, I don't know - it made the page load time so long that I gave up with it - so it's not a valid solution anyway.
Use an OpenSessionInViewInterceptor/Filter
Actually, the application currently uses both, as far as I can see:
In applicationContext-hibernate.xml file:
<bean id="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="transactionInterceptor" class="org.orgName.util.TransactionalWebRequestInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttribute" value="PROPAGATION_REQUIRES_NEW"/>
</bean>
In web.xml:
<filter>
<filter-name>open-session-in-view</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>open-session-in-view</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
add <prop key="hibernate.enable_lazy_load_no_trans">true</prop> to my sessionFactory properties
This is the only solution that works, but everywhere I've looked says it's a REALLY BAD idea.
So, what would be the correct way to do this?
If it helps, here's the TransactionalWebRequestInterceptor class:
public class TransactionalWebRequestInterceptor implements WebRequestInterceptor, TransactionDao {
private static final Log log = LogFactory.getLog(TransactionalWebRequestInterceptor.class);
private PlatformTransactionManager transactionManager;
private TransactionAttribute transactionAttribute;
private ThreadLocal<TransactionStatus> threadTransactionStatus = new ThreadLocal<TransactionStatus>();
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setTransactionAttribute(TransactionAttribute transactionAttribute) {
this.transactionAttribute = transactionAttribute;
}
public void preHandle(WebRequest request) throws Exception {
log.debug("preHandle");
beginTransaction();
}
public void postHandle(WebRequest request, ModelMap model) throws Exception {
log.debug("postHandle");
commitTransactionIfNoErrors();
}
public void afterCompletion(WebRequest request, Exception e) throws Exception {
log.debug("afterCompletion");
rollBackTransactionIfInProgress();
}
public void setRollbackOnly() {
log.debug("setRollbackOnly");
TransactionStatus status = threadTransactionStatus.get();
if (status == null) {
log.debug("rollback requested but no transaction in progress");
return;
}
status.setRollbackOnly();
}
private void beginTransaction() {
if (threadTransactionStatus.get() != null)
throw new IllegalStateException("transaction already in progress");
TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
threadTransactionStatus.set(status);
log.debug("transaction begun");
}
private void commitTransactionIfNoErrors() {
TransactionStatus status = threadTransactionStatus.get();
if (status == null)
throw new IllegalStateException("no transaction in progress");
if (status.isRollbackOnly()) {
log.debug("commitTransactionIfNoErrors: transaction is rollback-only; not committing");
return;
}
UserAttributes.getCurrent().getUser().setIsTransactionCompleted(true);
threadTransactionStatus.set(null);
transactionManager.commit(status);
log.debug("transaction committed");
}
private void rollBackTransactionIfInProgress() {
TransactionStatus status = threadTransactionStatus.get();
if (status == null) {
log.debug("rollBackTransactionIfInProgress: no transaction in progress");
return;
}
threadTransactionStatus.set(null);
transactionManager.rollback(status);
log.debug("transaction rolled back");
}
}
You should do the loading and the storing in the same transaction, so whatever method calls load and store should be #Transactional.
Lazy loading issues are usually solved by using a dedicated DTO model that fetches exactly what is needed. I wrote about some solutions and their pros and cons here:
https://blazebit.com/blog/2016/getting-started-with-blaze-persistence-entity-views.html
https://blazebit.com/blog/2017/entity-view-subview-as-rescue-for-lazyinitializationexception.html
If you have two requests, then you have two options. Use EntityManager.merge in store to apply the state as-is to the DB or use EntityManager.find to load the existing data and apply the changed data onto that instance within the transaction of the store method.
I have just rest controller, service and dao methods and . Checking if there is an existing student in the db if it is present then updating the existing object with current request values and storing in the database. But some how in multi threaded environment the values are getting overwritten for few of the records. My understanding is each thread has its own stack and variables that live inside stack which is specific to thread. In this scenario not understanding how the student object is getting overriden some times. Could any one have any idea could you please provide some inputs.
#RestController
public class HelloWorldController {
#Autowired
private StudentService service;
#RequestMapping(value = "/student", method = RequestMethod.PUT)
public String updateStudent(StudentDto studentDto) {
service.updateStudent(studentDto);
return "Success";
}
}
...
#Service
public class StudentService {
#Autowired
private StudentDao studentDao;
public void updateStudent(StudentDto dto) {
Student student = studentDao.findByStudentId(dto.getId());
if (student != null) {
student.setName(dto.getName());
studentDao.update(student); // dao extends couchbaseReository
}
}
}
I think you can use :
studentDao.save(student);
instead of
studentDao.update(student);
And if you want to handle task in parallel thread you can use the Annotation #Async (Spring)
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.
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