JPA和泛型,为什么我的班级不起作用?

时间:2014-02-12 11:56:42

标签: java spring generics jpa persistence

在我的Spring网络应用程序中,我使用的是通用dao类:

public abstract class GenericDaoImpl<T> implements GenericDao<T> {

    @Override
    public T create(final T t) {
        this.getEntityManager().persist(t);
        return t;
    }

    @Override
    public void delete(final Object id) {
        this.getEntityManager().remove(
                this.getEntityManager().getReference(getEntityType(), id));
    }

    @Override
    public T find(final Object id) {
        return (T) this.getEntityManager().find(getEntityType(), id);
    }

    @Override
    public T update(final T t) {
        return this.getEntityManager().merge(t);
    }
}

我为我的模型中的每个实体实现了这个类,它完美地运行。例如:

@Repository
public class GruppoDaoImpl extends GenericDaoImpl<Gruppo> implements GruppoDao {

}

我在服务层使用这些dao类。我的模型中的每个实体都有一个服务层,但是大多数这些类的方法是相同的,所以我尝试创建一个通用服务类,我可以使用与泛型dao相同的方式扩展:

public abstract class GenericAdminServiceImpl<ENTITY extends AbstractEntity, DTO extends AbstractDto>
    implements GenericAdminService<ENTITY, DTO> {

    private GenericDao<ENTITY> dao;
    private Class<ENTITY> entityClass;
    private Class<DTO> dtoClass;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected GenericAdminServiceImpl(GenericDao<ENTITY> dao) {
        this.dao = dao;
        //
        Type t = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) t;
        this.entityClass = (Class) pt.getActualTypeArguments()[0];
        this.dtoClass = (Class) pt.getActualTypeArguments()[1];
    }

    public DTO getById(Object id) {
        DTO dto = null;
        ENTITY entity = dao.find(id);
        if (entity != null) {
            try {
                dto = dtoClass.newInstance();
                initDto(entity, dto);
            } catch (Exception e) {
            }
        }
        return dto;
    }

    public void create(DTO dto) throws ServiceOperationException {
        ENTITY entity;
        try {
            entity = entityClass.newInstance();
            initEntity(dto, entity);
            Date dt = new Date();
            entity.setDataUltimoAggiornamento(dt);
            entity.setUtenteUltimoAggiornamento(dto.getLoggedUser());
            entity.setDataInserimento(dt);
            entity.setUtenteInserimento(dto.getLoggedUser());
            dao.create(entity);
        } catch (Exception e) {
            throw new ServiceOperationException("impossibile creare entity ["
                    + entityClass.getSimpleName() + "]", e);
        }
    }

    public void update(DTO dto) throws ServiceOperationException {
        ENTITY entity = dao.find(dto.getId());
        if (!entityExists(entity)) {
            throw new ServiceOperationException("entity non esistente ["
                    + entityClass.getSimpleName() + "#" + dto.getId() + "]");
        }
        initEntity(dto, entity);
        Date dt = new Date();
        entity.setDataUltimoAggiornamento(dt);
        entity.setUtenteUltimoAggiornamento(dto.getLoggedUser());
        dao.update(entity);
    }

    public void delete(Object id) throws ServiceOperationException {
        try {
            dao.delete((int) id);
        } catch (Exception e) {
            throw new ServiceOperationException(
                    "impossibile eliminare entity ["
                            + entityClass.getSimpleName() + "#" + id + "]", e); // TODO
        }
    }

    protected abstract void initDto(ENTITY entity, DTO outDto);

    protected abstract void initEntity(DTO dto, ENTITY outEntity);

    protected abstract boolean entityExists(ENTITY entity);

}

扩展这个类我只需要为每个实体实现特定的部分,将所有常见的东西留在abstract / generic类中。 问题是使用通用服务,合并,保留和删除不起作用。只有选择似乎工作,我不明白为什么......

当我在Eclipse中运行调试模式时,似乎都是正确的。一致的实体被传递给合并/持久化方法,为什么它们不起作用?你能救我吗?

更新#1

这是一个实施的例子:

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class GruppoServiceImplG extends
        GenericAdminServiceImpl<Gruppo, GruppoDto> implements GruppoServiceG {

    @Autowired
    protected GruppoServiceImplG(GruppoDao gruppoDao) {
        super(gruppoDao);
    }

    @Override
    protected void initDto(Gruppo entity, GruppoDto outDto) {
        outDto.setId(entity.getId());
        outDto.setNome(entity.getNome());
        outDto.setDescrizione(entity.getDescrizione());
        outDto.setDataInizioValidita(entity.getDataInizioValidita());
        outDto.setDataFineValidita(entity.getDataFineValidita());
    }

    @Override
    protected void initEntity(GruppoDto dto, Gruppo outEntity) {
        outEntity.setId(dto.getId());
        outEntity.setNome(dto.getNome());
        outEntity.setDescrizione(dto.getDescrizione());
        outEntity.setDataInizioValidita(dto.getDataInizioValidita());
        outEntity.setDataFineValidita(dto.getDataFineValidita());
    }

    @Override
    protected boolean entityExists(Gruppo entity) {
        return entity != null && entity.getId() > 0;
    }

}

更新#2

按照ŁukaszL。的建议,我向所有的crud方法添加了flush()。现在我得到了这个异常javax.persistence.TransactionRequiredException: no transaction is in progress。我的交易声明有什么问题?它适用于非通用服务......

2 个答案:

答案 0 :(得分:1)

如果您阅读了有关Spring and Hibernate flush behaviour的问题,那么提交您的交易并不容易使EntityManager保存所有更改。 Spring和JPA(Hibernate&amp; CO)的设计非常好(从Spring方面)但是,你必须断言你的实体管理器会在提交事务之前将所有查询写入数据库。

问题: JPA喜欢缓存。这意味着,他们倾向于避免发出查询。如果你做SELECT,他们别无选择 - 他们必须获取一些数据(只要未获取该数据部分 - 就像通过ID获取单个实体一样)。通过INSERT和UPDATE - 嗯,他们可以缓存。这意味着,在create上调用merge之前,removeflush()EntityManager通常不会向RDBMS发出查询。

如果您在不调用flush的情况下离开事务块,并且实体管理器正在延迟操作,那么您将提交未通过其发出修改查询的事务!

请务必至少在事务方法结束时调用EntityManager.flush()。您也可以在每次DML操作后调用它,这是您的选择(我更喜欢这种方式,因为它让我完全控制JPA发出DML查询的顺序,如果您大量使用DB约束/触发器,它可能是必不可少的)。

@Transactional
public void myTransactionalMethod() {
  getEntityManager().persist(a); // I've made some JPA change, that is not issued to DB yet
  ...
  // I'm doing something more
  ...
  getEntityManager().flush(); // the last moment to flush, after that instruction I leave transactional context
}

答案 1 :(得分:1)

按照ŁukaszL。的建议,我在通用类中发现了实际问题。

交易声明错了。我只在具体的服务类中设置@Transactional(propagation = Propagation.REQUIRES_NEW)

我这样解决了:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public abstract class GenericAdminServiceImpl<ENTITY extends AbstractEntity, DTO extends AbstractDto>
    implements GenericAdminService<ENTITY, DTO> {

    // ...

}

和(具体实施中):

@Service
@Transactional
public class GruppoServiceImplG extends
        GenericAdminServiceImpl<Gruppo, GruppoDto> implements GruppoServiceG {

    // ...

}