4

Im trying to add a parameter constraint configuration (a bean validation) on the create and edit method of the standrd AbstractFacade (as generated by NetBeans).

So I tried:

@Override
public void create(@WkTeilnahmePlanedResult  WkTeilnahme entity) {
   super.create(entity);
}

This returned the message

A method overriding another method must not alter the parameter constraint configuration when deploying it to Glassfish 4

So next try was

  @Override
  public void create(WkTeilnahme entity) {
    checkedCreate(entity);
  }


  private void checkedCreate(@WkTeilnahmePlanedResult WkTeilnahme entity) {
    super.create(entity);
  }

which deploys without any problems ... but the validator is never called.

Can you tell me why?

BTW:

  @Override
  public void create(WkTeilnahme entity) {
    throw new UnsupportedOperationException(
            "Create not supported! Use checkedCreate() instead!");
  }


  public void checkedCreate(@WkTeilnahmePlanedResult WkTeilnahme entity) {
    super.create(entity);
  }

This works but isn't really very cool!

mpunktw
  • 267
  • 2
  • 4
  • 12

4 Answers4

5

Regarding your first attempt, it does not work, because Bean Validation constraints must follow the Liskov Substitution Principle. See also the relevant Bean Validation specification section - http://beanvalidation.org/1.1/spec/#constraintdeclarationvalidationprocess-methodlevelconstraints-inheritance

From the specification:

Very informally speaking, the Liskov substitution principle says that where a given type T is used, it should be possible to replace T with a sub-type S of T ("Behavioral subtyping"). If S overrides/implements a method from T and S would strengthen the method's preconditions (e.g. by adding parameter constraints) this principle would be violated as client code working correctly against T might fail when working against S. Also if S overrides/implements a method from T and S weakens the method's postconditions this principle would be violated. However S may strengthen the method's postconditions (by adding return value constraints), as client code working against T still will work against S.

I think your second example should actually work, however, I am not familiar with the NetBeans AbstractFacade. My guess is that the call to checkedCreate(entity); is not going via a proxies instance and hence is not intercepted. Maybe you could post the full code for the involved classes? What type of class contains these methods? A session bean?

Hardy
  • 18,659
  • 3
  • 49
  • 65
  • Ok, I can understand Liskov ... hmmm ... but 2nd example doesn't work either! The validator simply isn't called. 3rd one is ok and in fact thats it what I use in the moment (but being unhappy with). – mpunktw Apr 01 '14 at 17:59
  • @mpunktw I believe your second version doesn't work because neither jdk dynamic proxy not cglib proxy supports self invocation. – IKo Aug 05 '22 at 13:23
2

I faced the same problem.

My code was like this:

public interface SomeService{
    List<Object> getObjects(Integer id);
}

public class SomeServiceImpl implements SomeService{
    @Override
    public List<Object> getObjects(@NotNull Integer id) { ... }
}

I added the same annotation to the service and the problem has been soled:

public interface SomeService{
    List<Object> getObjects(@NotNull Integer id);
}
menoktaokan
  • 346
  • 3
  • 13
1

You should do these:

inteface Test {
   void create(@WkTeilnahmePlanedResult  WkTeilnahme entity);
}

@Validated
class TestImpl implement Test {
    @Override
    public void create(WkTeilnahme entity) {
       super.create(entity);
    }
}
0

Thanks Hardy. Shure I can post them:

package at.wima.member.facade;

import java.util.List;
import javax.persistence.EntityManager;

public abstract class AbstractFacade<T> {
  private Class<T> entityClass;

  public AbstractFacade(Class<T> entityClass) {
    this.entityClass = entityClass;
  }

  protected abstract EntityManager getEntityManager();

  public void create(T entity) {
    getEntityManager().persist(entity);
  }

  public T edit(T entity) {
    return getEntityManager().merge(entity);
  }

  public void remove(T entity) {
    getEntityManager().remove(getEntityManager().merge(entity));
  }

  public T find(Object id) {
    return getEntityManager().find(entityClass, id);
  }

  public List<T> findAll() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    return getEntityManager().createQuery(cq).getResultList();
  }

  public List<T> findRange(int[] range) {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    javax.persistence.Query q = getEntityManager().createQuery(cq);
    q.setMaxResults(range[1] - range[0]);
    q.setFirstResult(range[0]);
    return q.getResultList();
  }

  public int count() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
    cq.select(getEntityManager().getCriteriaBuilder().count(rt));
    javax.persistence.Query q = getEntityManager().createQuery(cq);
    return ((Long) q.getSingleResult()).intValue();
  }

}

and the subclass

package at.wima.member.facade;

import at.wima.member.entity.*;
import at.wima.member.validations.*;
import java.util.*;
import javax.ejb.Stateless;
import javax.persistence.*;


@Stateless
public class WkTeilnahmeFacade extends AbstractFacade<WkTeilnahme> {

  @PersistenceContext(unitName = "memberPu")
  private EntityManager em;


  public WkTeilnahme findByKey(Mandant mandant, Wettkampf wettkampf,
                               Person person) {
    Query query = em.createNamedQuery("wkTeilnahmeFindByKey");
    query.setParameter("mandant", mandant);
    query.setParameter("wettkampf", wettkampf);
    query.setParameter("person", person);
    List rl = query.getResultList();

    if (rl.size() <= 0) {
      return null;
    }
    return (WkTeilnahme) (rl.get(0));
  }


  public List<WkTeilnahme> findAllSort(Mandant mandant) {
    Query query = em.createNamedQuery("wkTeilnahmeFindAllSort");
    query.setParameter("mandant", mandant);
    return query.getResultList();
  }


  public List<WkTeilnahme> findByWettkampf(Mandant mandant, Wettkampf wettkampf) {
    Query query = em.createNamedQuery("wkTeilnahmeFindByWettkampf");
    query.setParameter("mandant", mandant);
    query.setParameter("wettkampf", wettkampf);
    return query.getResultList();
  }


  @Override
  public void create(WkTeilnahme entity) {
    throw new UnsupportedOperationException(
            "Create not supported! Use checkedCreate() instead!");
  }


  public void checkedCreate(@WkTeilnahmePlanedResult WkTeilnahme entity) {
    super.create(entity);
  }


  @Override
  public WkTeilnahme edit(WkTeilnahme entity) {
    throw new UnsupportedOperationException(
            "Edit not supported! Use checkedEdit() instead!");
  }


  public WkTeilnahme checkedEdit(@WkTeilnahmePlanedResult WkTeilnahme entity) {
     return super.edit(entity);
  }


  @Override
  protected EntityManager getEntityManager() {
    return em;
  }


  public WkTeilnahmeFacade() {
    super(WkTeilnahme.class);
  }
}
mpunktw
  • 267
  • 2
  • 4
  • 12