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
>
>
>
>
>