从DAO回来时的Spring/JPA分离的实体

问题描述:

我正在尝试JPA/Hibernate和Spring。我有一个控制器 - >服务 - > DAO结构,我有一些问题JPA。我认为它的交易相关,但找不到问题。从DAO回来时的Spring/JPA分离的实体

我有@Service和@Transactional注解的服务。我有一个“removeAll()”方法,我首先调用“findAll”,然后迭代列表以在实体上调用“remove()”。当我调用remove方法时,出现“非法参数异常:删除分离的实例”。

从我的红色,我应该只将@Transactional注释放在我的服务类上,我的DAO应该以某种方式加入事务。如果我把所有@Transaction的东西放在DAO类上,一切正常。但是我记得,我应该只需要服务类的注释。可能存在配置问题,但无法找到它。

所以,如果有人可以看看,也许你会马上看到它。

这里我的服务类的代码片段:

@Service("courseService") 
@Transactional 
public class CourseServiceImpl implements CourseService { 
    @Autowired 
    @Qualifier("courseTemplateDAO") 
    private CourseTemplateDAO courseTemplateDAO; 

    public Integer removeAllTemplates() { 
     int removed = 0; 

     List<CourseTemplate> courseTemplates = getCourseTemplateDAO().findAll(); 
     for (CourseTemplate currCourseTemplate : courseTemplates) { 
      getCourseTemplateDAO().remove(currCourseTemplate); 
      removed++; 
     } 

     return removed; 
    } 

    public CourseTemplateDAO getCourseTemplateDAO() { 
     return courseTemplateDAO; 
    } 

    public void setCourseTemplateDAO(CourseTemplateDAO courseTemplateDAO) { 
     this.courseTemplateDAO = courseTemplateDAO; 
    } 
} 

泛型DAO类,非常标准:

public abstract class JPADAOImpl<T> extends JpaDaoSupport implements JPADAO<T> { 
    private Class<T> entityClass; 

    public JPADAOImpl() { 
     ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); 
     this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0]; 
    } 

    public void persist(T entity) { 
     getJpaTemplate().persist(entity); 
    } 

    public void remove(T entity) { 
     getJpaTemplate().remove(entity); 
    } 

    public T merge(T entity) { 
     return getJpaTemplate().merge(entity); 
    } 

    public void refresh(T entity) { 
     getJpaTemplate().refresh(entity); 
    } 

    public T flush(T entity) { 
     getJpaTemplate().flush(); 
     return entity; 
    } 

    public T findById(long id) { 
     return getJpaTemplate().find(entityClass, id); 
    } 

    public List<T> findAll() { 
     List<T> res = getJpaTemplate().execute(new JpaCallback<List<T>>() { 
      public List<T> doInJpa(EntityManager em) throws PersistenceException { 
       Query q = em.createQuery("SELECT h FROM " + entityClass.getName() + " h"); 
       return q.getResultList(); 
      } 
     }); 

     return (List<T>) res; 
    } 

    public Integer removeAll() { 
     return getJpaTemplate().execute(new JpaCallback<Integer>() { 
      public Integer doInJpa(EntityManager em) throws PersistenceException { 
       Query q = em.createQuery("DELETE FROM " + entityClass.getName() + " h"); 
       return q.executeUpdate(); 
      } 
     }); 
    } 

    public Class<T> getEntityClass() { 
     return entityClass; 
    } 
} 

而且我CourseTemplateDAO实现类:

@Repository("courseTemplateDAO") 
public class CourseTemplateDAOImpl extends JPADAOImpl<CourseTemplate> implements CourseTemplateDAO { 

    @Autowired 
    private EntityManagerFactory entityManagerFactory; 

    public CourseTemplateDAOImpl() { 

    } 

    @PostConstruct 
    public void init() { 
     super.setEntityManagerFactory(entityManagerFactory); 
    } 
} 

我的春天应用程序上下文配置文件:

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" 
xmlns:task="http://www.springframework.org/schema/task" 
xsi:schemaLocation=" 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd 
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 

<context:annotation-config /> 
<context:component-scan base-package="org.ksshi"/> 

<tx:annotation-driven transaction-manager="transactionManager"/> 

<bean class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" id="entityManagerFactory"> 
    <property name="persistenceUnitName" value="KSSHIPersistenceUnit"/> 
</bean> 

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory"/> 
</bean> 

<bean id="messageSource" class="org.ksshi.service.i18n.impl.I18NMessageSource"/> 

最后,我的持久性配置文件:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 
     version="2.0"> 

<persistence-unit name="KSSHIPersistenceUnit" transaction-type="RESOURCE_LOCAL"> 
    <description> 
     Persistence unit for the KSSHI application 
    </description> 

    <provider>org.hibernate.ejb.HibernatePersistence</provider> 

    <class>org.ksshi.entity.CourseTemplate</class> 
    <class>other classes</class> 

    <properties> 
     <property name="hibernate.hbm2ddl.auto" value="update"/> 
     <property name="hibernate.show_sql" value="true"/> 
     <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/> 
     <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> 
     <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/ksshi"/> 
     <property name="hibernate.connection.username" value="sa"/> 
     <property name="hibernate.connection.password" value=""/> 

     <property name="hibernate.c3p0.min_size" value="5"/> 
     <property name="hibernate.c3p0.max_size" value="20"/> 
     <property name="hibernate.c3p0.timeout" value="300"/> 
     <property name="hibernate.c3p0.max_statements" value="50"/> 
     <property name="hibernate.c3p0.idle_test_period" value="3000"/> 
    </properties> 
</persistence-unit> 

所有配置看起来是正确的。但是我不确定你在JpaDaoSupport以及从何处获取JPA模板(getJpaTemplate())所做的抽象。我认为不是在CourseTemplateDAOImpl类中注入entitymanager,而是在JPADAOImpl中配置实体管理器,因为它对所有DAO实现都是通用的。

我已经修改了JPADAOImpl类还增加了一般的参数作为主键(你认为它始终是长型)

public abstract class JPADAOImpl<T, PK extends Serializable> implements JPADAO<T, PK> { 
    private Class<T> entityClass; 

    @PersistenceContext(type=PersistenceContextType.TRANSACTION) 
    protected EntityManager entityManager; 

    @SuppressWarnings("unchecked") 
    public JPADAOImpl() { 
     ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); 
     this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0]; 
    } 

    public void persist(T entity) { 
     entityManager.persist(entity); 
    } 

    public void remove(T entity) { 
     entityManager.remove(entity); 
    } 

    public T merge(T entity) { 
     return entityManager.merge(entity); 
    } 

    public void refresh(T entity) { 
     entityManager.refresh(entity); 
    } 

    public T flush(T entity) { 
     entityManager.flush(); 
     return entity; 
    } 

    public T findById(PK id) { 
     return entityManager.find(getEntityClass(), id); 
    } 

    @SuppressWarnings("unchecked") 
    public List<T> findAll() { 
     String all = "select h from " + getEntityClass().getSimpleName() + " h"; 
     Query query = entityManager.createQuery(all); 
     return (List <T>)query.getResultList(); 
    } 

    public Integer removeAll() { 
     Query q = entityManager.createQuery("DELETE FROM " + getEntityClass().getName() + " h"); 
     return q.executeUpdate(); 
    } 

    public Class<T> getEntityClass() { 
     return entityClass; 
    } 
} 

JPADAO接口

public interface JPADAO<T, PK extends Serializable> { 

    void persist (T entity); 

    void remove(T entity); 

    T merge(T entity); 

    void refresh(T entity); 

    T flush(T entity); 

    T findById(PK id); 

    List<T> findAll(); 

    T update(T entity); 
} 

CourseTemplateDAOImpl类

@Repository("courseTemplateDAO") 
public class CourseTemplateDAOImpl extends JPADAOImpl<CourseTemplate, long> implements CourseTemplateDAO { 


} 

尝试将事务标记移动到方法定义,像(下面的代码是一个工作的代码已经在生产中):

@Transactional 
public void delete(User entity){ 
    userDAO.delete(entity); 
} 

@Transactional(readOnly=true) 
public User findUserByUsername(String username){ 
    return getUserDAO().findOne(username); 
} 

,你也可以与传播起到见参考文献在Spring Documentation特地需要嵌套功能。

CourseTemplate是怎么样的?也许你有懒惰的关系,并在同一时间在这种关系CascadeType.ALL?也许你应该在你的findAll中获取元素,以免它们分离?

你说的正确,我们比DAO层更喜欢服务层@Transactional方法。 我会建议更改事务隔离级别和Transaction propagation

更改为嵌套应该工作。

更好的是,在方法上放置@Transactional注解。在你的情况下

@Transactional public Integer removeAllTemplates(){ 
... 
}