nightfly
nightfly

Reputation: 455

not being able to persist entity in JPA in spring 3

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

http://pastebin.com/pKqzW9h1

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

Answers (2)

axtavt
axtavt

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 methods
  • Your saveTeacher() looks like a service method, it would be better to place it in the separate service class and annotate as @Transactional
  • You don't need persist() in saveTeacher() - changes made to persistent objects should be saved automatically
  • Beware of dynamic proxy vs target class proxy distinction (regarding to TestDao) - see the links below

See also:

Upvotes: 5

Philipp Jardas
Philipp Jardas

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

Related Questions