Hi,

i discover a bit more spring data jpa and saw it was possible to create
dynamically classes!!

it is exactly the same than the stateless without interface excepted here
the interface has no implementation.

what do you think if we had it to OpenEJB, it is not standard but it is
pretty cool.

Here what i think we could do:

   1. create a API to scan interfaces
   1. we need persistencecontext information so we could use
      @PersistenceContext (i don't like) or add another annotation withthe same
      information (@Repository?)
      2. we need a name, @Named can probably used or we can add it to
      @Repository
      2. we scan "@Repository" interface constructing pseudo injection to be
   able to get an entitymanager
   3. then we deploy it in JNDI
      1. instead of binding a class we bind a proxy which manage to create
      the query from the name


It is probably no clear so here some snippets:

My repository:

@Repository(name = "user")
public interface UserDAO {
    User findById(long id);
    Collection<User> findByName(String name);
    Collection<User> findAll();
}


One very simple implementation of the invocation handler which manage only
one condition (this version need an EntityManagerHolder which is here only
to be able to get the em, it is just an interface with a method
getEntityManager()...just to do the poc):


public class QueryProxy<T> implements InvocationHandler {
    public static final String FIND_PREFIX = "find";

    private static final Map<String, List<String>> CONDITIONS = new
ConcurrentHashMap<String, List<String>>();

    private EntityManagerHolder entityManagerHolder;
    private Class<T> type;

    public QueryProxy(EntityManagerHolder holder, Class<T> entityClass) {
        entityManagerHolder = holder;
        type = entityClass;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
        if (!method.getName().startsWith(FIND_PREFIX)) {
            throw new IllegalArgumentException("finder should start with
find");
        }

        Query query = getQuery(entityManagerHolder.getEntityManager(),
method.getName(), args);
        if (Collection.class.isAssignableFrom(method.getReturnType())) {
            return query.getResultList();
        }
        return query.getSingleResult();
    }

    private Query getQuery(EntityManager entityManager, String methodName,
Object[] args) {
        final List<String> conditions = parseMethodName(methodName);
        final EntityType<T> et = entityManager.getMetamodel().entity(type);
        final CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        CriteriaQuery<Object> query = cb.createQuery();
        Root<T> from = query.from(type);
        query = query.select(from);

        int i = 0;
        for (String condition : conditions) {
            SingularAttribute<? super T, ?> attribute =
et.getSingularAttribute(condition);
            Path<?> path = from.get(attribute);
            Class<?> javaType = attribute.getType().getJavaType();
            if (javaType.equals(String.class)) {
                query = query.where(cb.like((Expression<String>) path,
(String) args[i++]));
            } else if (Number.class.isAssignableFrom(javaType) ||
javaType.isPrimitive()) {
                query = query.where(cb.equal(path, args[i++]));
            }
        }

        return entityManager.createQuery(query);
    }

    private List<String> parseMethodName(final String methodName) {
        List<String> parsed;
        if (CONDITIONS.containsKey(methodName)) {
            parsed = CONDITIONS.get(methodName);
        } else {
            parsed = new ArrayList<String>();
            String toParse = methodName.substring(FIND_PREFIX.length());
            // TODO
            if (toParse.startsWith("By")) {
                toParse = StringUtils.uncapitalize(toParse.substring(2));
                parsed.add(toParse);
            }
            CONDITIONS.put(methodName, parsed);
        }
        return parsed;
    }
}


Finally i can do:

public class QueryTest {
    private static Context context;
    private static UserDAO dao;

    @BeforeClass public static void init() throws Exception {
        context = EJBContainer.createEJBContainer().getContext();
        dao = (UserDAO)
Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { UserDAO.class },
                new QueryProxy((EntityManagerHolder)
context.lookup("java:global/dynamic-query/EMH"), User.class));


        InitUserDAO init = (InitUserDAO)
context.lookup("java:global/dynamic-query/InitUserDAO");
        for (int i = 0; i < 10; i++) {
            User u = new User();
            u.setAge(i * 8);
            if (i % 3 == 0) {
                u.setName("foo");
            } else {
                u.setName("bar-" + i);
            }
            init.insert(u);
        }
    }

    @AfterClass public static void close() throws Exception {
        if (context != null) {
            context.close();
        }
    }

    @Test public void query() {
        Collection<User> u1 = dao.findByName("foo");
        Collection<User> users = dao.findAll();
        User u2 = dao.findById(1);
        System.out.println("\n\n" + users + "\n\n" + u1 + "\n\n" + u2);
    }
}

Any thoughts? should it be added to OpenEJB (after some enhancement of
course ;))?


we could extend it to persist, update etc... methods


- Romain

Reply via email to