Reputation: 455
I am not being able to persist entity in JPA, although findAll works here. Here is the JpaDAO
package aop.web.teacher.dao;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public abstract class JpaDAO extends JpaDaoSupport {
protected Class entityClass;
private static Logger log = Logger.getLogger(JpaDAO.class);
@SuppressWarnings("unchecked")
public JpaDAO() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass()
.getGenericSuperclass();
this.entityClass = (Class) genericSuperclass
.getActualTypeArguments()[1];
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void persist(E entity) {
getJpaTemplate().persist(entity);
}
@Transactional
public void remove(E entity) {
getJpaTemplate().remove(entity);
}
@Transactional
public E merge(E entity) {
return getJpaTemplate().merge(entity);
}
@Transactional
public void refresh(E entity) {
getJpaTemplate().refresh(entity);
}
@Transactional
public E findById(K id) {
return getJpaTemplate().find(entityClass, id);
}
@Transactional
public E flush(E entity) {
getJpaTemplate().flush();
return entity;
}
@SuppressWarnings("unchecked")
@Transactional
public List findAll() {
Object res = getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query q = em.createQuery("SELECT h FROM "
+ entityClass.getName() + " h");
return q.getResultList();
}
});
return (List) res;
}
@SuppressWarnings("unchecked")
@Transactional
public Integer removeAll() {
return (Integer) getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query q = em.createQuery("DELETE FROM " + entityClass.getName()
+ " h");
return q.executeUpdate();
}
});
}
}
Here is the TestDao class
package aop.web.teacher.dao;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import aop.web.teacher.rmodels.Teachermaster;
@Service
@Repository
public class TestDaoImpl extends JpaDAO implements TestDao {
@Autowired
EntityManagerFactory entityManagerFactory;
@PersistenceContext
private EntityManager em;
@PostConstruct
public void init() {
super.setEntityManagerFactory(entityManagerFactory);
}
public int saveTeacher() {
List teacherList = findAll();
Teachermaster m1 = teacherList.get(0);
logger.info("Found " + m1.getId() + " and " + m1.getRace());
m1.setRace(m1.getRace() + "::" + System.currentTimeMillis());
logger.info("New " + m1.getId() + " and " + m1.getRace());
persist(m1);
return 0;
}
}
Here is the spring context xml
Here the findAll works but when we make a change to the attribute of the Teachermaster then persist or merge does not seem to save the entity... If we flush it we get exception
javax.persistence.TransactionRequiredException: no transaction is in progress
Please advise
Upvotes: 2
Views: 4858
Reputation: 242686
Spring uses proxy-based AOP, therefore aspects (including transactional aspect) are not applied when methods are called from the same class.
Generally speaking
@Transactional
annotations should be usually placed on service methods rather than on DAO methodssaveTeacher()
looks like a service method, it would be better to place it in the separate service class and annotate as @Transactional
persist()
in saveTeacher()
- changes made to persistent objects should be saved automaticallyTestDao
) - see the links belowSee also:
Upvotes: 5
Reputation: 3302
You are calling a local method when invoking persist()
from your test class. This way the proxy that would create transactions is not invoked so your call to persist()
has no transaction.
The way to do this properly is to have the test class not extend the object under test but have it injected. This way the proxy will be triggered and the transaction will be created.
By the way, I must add that I find your class design a little bit beculiar. May I suggest creating a structure like the following?
DAO interface:
public interface FooDao {
void persist(Foo foo);
// ...
}
DAO implementation:
public class FooDaoImpl implements FooDao {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void persist(Foo foo) {
entityManager.persist(foo);
}
}
Test class:
@RunWith(SpringJunit4ClassRunner.class)
@ContextConfiguration(...)
public class FooDaoTest {
@Autowired
private FooDao fooDao;
@Test
public void testPersist() {
// do some testing
}
}
You can, if you wish, extract most of the logic in the DAO implementations into a generic superclass.
Upvotes: 2