2010-12-08 15 views
2

Je ne suis pas en mesure de persister entité dans JPA, bien findAll fonctionne ici. Voici le JpaDAOne pas être en mesure de persister entité dans JPA au printemps 3

 

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(); 
    } 

    }); 
} 

} 
 

Voici la classe TestDao


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; 
} 

} 

Voici le contexte du printemps xml

http://pastebin.com/pKqzW9h1

Ici, le findAll fonctionne mais quand nous faisons un changement à l'attribut du Teachermaster puis persister ou fusionner ne voit pas m pour sauver l'entité ... Si nous chasse d'eau nous obtenons exception

javax.persistence.TransactionRequiredException: no transaction is in progress 

S'il vous plaît conseiller

Répondre

2

Vous appelez une méthode locale lors de l'appel de persist() à partir de votre classe de test. De cette façon, le proxy qui créerait des transactions n'est pas appelé, donc votre appel à persist() n'a aucune transaction.

La façon correcte de procéder est d'avoir la classe de test et non d'étendre l'objet à tester mais de le faire injecter. De cette façon, le proxy sera déclenché et la transaction sera créée. En passant, je dois ajouter que je trouve votre design de classe un peu bizarre. Puis-je suggérer de créer une structure comme celle-ci?

interface DAO:

public interface FooDao { 
    void persist(Foo foo); 
    // ... 
} 

mise en œuvre DAO:

public class FooDaoImpl implements FooDao { 
    @PersistenceContext 
    private EntityManager entityManager; 

    @Transactional 
    public void persist(Foo foo) { 
     entityManager.persist(foo); 
    } 
} 

classe Test:

@RunWith(SpringJunit4ClassRunner.class) 
@ContextConfiguration(...) 
public class FooDaoTest { 
    @Autowired 
    private FooDao fooDao; 

    @Test 
    public void testPersist() { 
     // do some testing 
    } 
} 

Vous pouvez, si vous le souhaitez, extraire la majeure partie de la logique dans les implémentations de DAO dans une superclasse générique.

5

Spring utilise AOP à base de proxy, par conséquent les aspects (y compris les aspects transactionnels) ne sont pas appliquées lorsque les méthodes sont appelées de la même classe.

D'une manière générale

  • @Transactional annotations doivent être généralement placés sur les méthodes de service plutôt que sur les méthodes de DAO
  • Votre saveTeacher() ressemble à une méthode de service, il serait préférable de le placer dans la classe de service distinct et annoter comme @Transactional
  • Vous n'avez pas besoin persist() dans saveTeacher() - les modifications apportées aux objets persistants doivent être enregistrés automatiquement
  • Méfiez-vous de distinction proxy dynamique vs classe proxy cible (en ce qui concerne à TestDao) - voir les liens ci-dessous

Voir aussi: