Here is a patch:

http://pastebin.com/4CgcLkmH

it is a bit dirty at JNDI level but it works if you want to try:

The repository:

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

An entity:

@Entity
public class User {
    @Id @GeneratedValue private long id;
    private String name;
    private int age;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
        '}';
    }
}

A stateless to init the database:

@Stateless
public class InitUserDAO {
    @PersistenceContext private EntityManager em;
    public void insert(User user) {
        em.persist(user);
    }
}

The test:

package org.superbiz.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.superbiz.dao.InitUserDAO;
import org.superbiz.dao.UserDAO;
import org.superbiz.model.User;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import java.util.Collection;

/**
 * @author rmannibucau
 */
public class QueryTest {
    private static Context context;
    private static UserDAO dao;

    @BeforeClass public static void init() throws Exception {
        context = EJBContainer.createEJBContainer().getContext();
        InitUserDAO init = (InitUserDAO)
context.lookup("java:global/dynamic-query/InitUserDAO");
        dao = (UserDAO) context.lookup("java:global/openejb/Repository/" +
UserDAO.class.getName());
        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);
    }
}

and the persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence";
       version="2.0"
       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";>
  <persistence-unit name="user" transaction-type="JTA">
    <jta-data-source>My Default DataSource</jta-data-source>
    <class>org.superbiz.model.User</class>
    <properties>
      <property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>
    </properties>
  </persistence-unit>
</persistence>

Don't hesitate to give me feedback if i should continue or not, what you
think should be done or not.

- Romain

2011/7/29 Romain Manni-Bucau <[email protected]>

> 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