Java EE 6注释继承的神秘面纱

Ras*_*nke 10 inheritance annotations java-ee-6 jpa-2.0 ejb-3.1

我在几个场景中使用EJB继承,有时在超类中使用注释,就像这个通用实体DAO:

public class JpaDAO<T>{
    protected Class<T> entityClass;

    @PersistenceContext(unitName="CarrierPortalPU")
    protected EntityManager em;
    protected CriteriaBuilder cb;

    @PostConstruct
    private void init() {
        cb = em.getCriteriaBuilder();
    }

    public JpaDAO(Class<T> type) {
        entityClass = type;
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void create(T entity) {
        em.persist(entity);
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public T find(Object id) {
        return em.find(entityClass, id);
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public List<T> findAll(){
        CriteriaQuery<T> cq = cb.createQuery(entityClass);
        Root<T> entity = cq.from(entityClass);
        cq.select(entity);
        return em.createQuery(cq).getResultList();
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void remove(T entity) {
        em.remove(em.merge(entity));
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public T edit(T entity) {
        return em.merge(entity);
    }

}
Run Code Online (Sandbox Code Playgroud)

使用如下实现的示例子类:

@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class DepartmentDAO extends JpaDAO<Department> {

    public DepartmentDAO() {
        super(Department.class);
    }

    public Department findByName(String name){
        CriteriaQuery<Department> cq = cb.createQuery(Department.class);
        Root<Department> department = cq.from(Department.class);
        cq.where(cb.equal(department.get(Department_.name), name));
        cq.select(department);
        try{
            return em.createQuery(cq).getSingleResult();
        }catch(Exception e){
            return null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我最近读到java注释不是继承的(源代码).这会导致我的JpaDAO在访问其entitymanager或其标准构建器时抛出空指针异常(因为@PersistanceContext和@PostConstruct都会被忽略),但事实并非如此.有人可以澄清这是如何工作的吗?我很担心超类中的@TransactionAttributes会发生什么,当子类将NOT_SUPPORTED作为类默认值时,我是否可以信任从子类调用时实际使用事务?

Bre*_*ail 22

Java注释不会被继承,但JavaEE规范会更改规则以允许这些属性按预期工作.请参阅常见注释1.1规范.2.1节甚至以@TransactionAttribute为例.EJB 3.1第13.3.7.1节还明确说明了@TransactionAttribute的规则:

如果bean类具有超类,则应用以下附加规则.

  • 在超类S上指定的事务属性适用于由S定义的业务方法.如果未在S上指定类级事务属性,则它等效于S上的TransactionAttribute(REQUIRED)的规范.
  • 可以在由类S定义的业务方法M上指定事务属性,以便为方法M重写在类S上显式或隐式指定的事务属性值.
  • 如果类S的方法M覆盖由超类S定义的业务方法,则M的事务属性由应用于类S的上述规则确定.

简而言之,对于大多数JavaEE注释,方法级注释适用于该方法,除非子类重写该方法,并且类级注释仅适用于该类中定义的所有方法.该规则不适用于"组件定义"类级别注释,例如@Stateless(请参阅EJB 3.1规范第4.9.2.1节)