【问题标题】:Spring/JPA Detached entity when coming back from DAO从 DAO 返回时 Spring/JPA 分离实体
【发布时间】:2011-12-28 05:35:37
【问题描述】:

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

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

据我所知,我应该只将@Transactional 注释放在我的服务类上,并且我的 DAO 应该以某种方式加入事务。如果我将所有 @Transaction 的东西放在 DAO 类上,一切都会正常工作。但我记得,我应该只需要服务类上的注释。可能是配置问题,但是找不到。

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

这里是我的 Service 类的代码 sn-p:

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

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

我的spring application-context配置文件:

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

【问题讨论】:

    标签: java spring jpa transactions


    【解决方案1】:

    尝试将事务符号移动到方法定义中,例如(下面的代码是已经在生产中的工作代码):

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

    您也可以使用 PROPAGATION 来查看Spring Documentation 的参考资料,特别是必需和嵌套功能。

    【讨论】:

      【解决方案2】:

      所有配置看起来都正确。但我不确定您在 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 {
      
      
      }
      

      【讨论】:

        【解决方案3】:

        您的CourseTemplate 看起来如何?也许你有懒惰的关系,同时在那个关系中有CascadeType.ALL?也许您应该在 findAll 中获取元素,这样它们就不会被分离?

        【讨论】:

          【解决方案4】:

          您说我们更喜欢服务层@Transactional 方法而不是DAO 层方法是正确的。 我建议更改事务隔离级别和Transaction propagation

          更改为嵌套应该可以。

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

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

          【讨论】:

            猜你喜欢
            • 2015-09-11
            • 2020-03-26
            • 2015-08-10
            • 2011-11-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多