You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomee.apache.org by Romain Manni-Bucau <rm...@gmail.com> on 2011/07/29 12:00:36 UTC

openejb & dynamic finders/dao?

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

Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
i'll push soon a first version but if someone could review the code. I
created the proxy in EjbHomeProxyhandler...

@David: was not really what you expected i think, is it a problem?

- Romain

2011/8/3 Romain Manni-Bucau <rm...@gmail.com>

> Thanks for this feedback.
>
> What you describe was my first thought but then i wanted to avoid standard
> annotation while this feature is not in the specs but if  you think it is ok
> it is ok for me too on this point.
>
> Where i'm not agree (at least for a first step) is to specify a proxy: the
> goal is exactly what you said at the end "sometimes the interface is enough"
> and it means too "sometimes implementation - as simple it can be - is too
> complex" so i don't want to add something which can be a bit complicated
> (proxy doesn't seem so easy to expose them IMHO). We could replce it by a
> convention: instead of a proxy the user give a class and the matching is
> done from methods name (_ could replace a star for regex). But in the spirit
> of give user simple thing i prefer to avoid any handler logic here which
> could conflict with interceptors for classical cases.
>
> I'll have a try to implement a first version and if it works i'll remove
> @Repository.
>
>
> - Romain
>
> 2011/8/2 David Blevins <da...@gmail.com>
>
>> A home interface impl is essentially a stateless session bean.  We could
>> do the same.
>>
>> We just need to tag the interface as a session bean in the
>> AnnotationDeployer and add it to the app.  Then we can support transactions,
>> security, interceptors, decorators and more.  It could even be a web service
>> or a jax-rs service.  We could allow people to tag their @Repository bean as
>> any kind of session bean so @Stateful with EXTENDED persistence context
>> would even work. The interface becomes a business local interface and people
>> can reference it via @EJB.  The persistence context reference could be added
>> to the bean meta-data as a plain <persistence-context> reference and
>> injected with a setter (just need to add another interface to supply the
>> setter method).
>>
>> We could do something tricky like use the Repository.class itself as the
>> bean class -- or some other well known class.  When we see it we create our
>> AnnotationFinder from the interface instead of the bean class.  We normally
>> check that people aren't using annotations in interfaces, but in this case
>> we would just skip checking @Repository beans and allow it.
>>
>> Then all the data is passed to the assembler as usual.  Very early in the
>> createApplication() method we would generate the proxy class for any
>> @Repository bean and set that class as the bean class.
>>
>> That just leaves "how to construct the proxy" as the last unsolved issue.
>>  For that we could do something simple.  This is cool... we could establish
>> the convention that if you use an implementation of
>> java.lang.reflect.InvocationHandler as your bean class, then we construct it
>> with full dependency injection, create the proxy for you and pass it back
>> from BeanContext.newInstance().  So I guess we wouldn't need a second
>> interface for the EntityManager setter, we just put it right in the
>> QueryProxy class.
>>
>> And really at this point, we actually don't need "Repository" specific
>> logic.  We could enable this processing for any interface that wishes to
>> supply a handler and be an bean.
>>
>>    @Stateless
>>    @PersistenceContext(unitName = "fooUnit", name = "foo")
>>    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
>>     public interface Foo {
>>        MyEntity findByXXX(String xxx);
>>
>>        MyEntity findById(long i);
>>
>>        List<MyEntity> findByBar(int bar);
>>        // ...
>>    }
>>
>> We could still offer a @Repository implementation, but as a
>> meta-annotation.  So when we scan for all @Proxy beans we would pick it up
>> automatically with the right implementation.
>>
>>    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
>>    @Metatype
>>    @Target({ElementType.TYPE})
>>    @Retention(RetentionPolicy.RUNTIME)
>>    public static @interface Repository {
>>    }
>>
>> Now we or anyone else can do a million more things just like this.  The
>> basic idea is -- sometimes the interface is enough!
>>
>>
>> Thoughts?
>>
>>
>> -David
>>
>>
>> On Aug 2, 2011, at 11:15 AM, Romain Manni-Bucau wrote:
>>
>> > Today it is a simple implementation managing only finders but if someone
>> > give me some clues to manage correctly transaction i would like to add
>> > persistence methods.
>> >
>> > - Romain
>> >
>> > 2011/8/2 David Blevins <da...@gmail.com>
>> >
>> >> This looks like a cool idea.  It's not worth investigating, but this is
>> >> exactly how CMP home interfaces look.  The developer creates an
>> interface
>> >> with "create" and "find" method and the container makes it work.
>> >>
>> >> Home interfaces were actually pretty useful for CMP, just CMP itself
>> was
>> >> terrible and home interfaces weren't very useful for session beans.  So
>> they
>> >> got the axe in EJB 3.0.
>> >>
>> >> I haven't had a chance to check out the implementation code, but the
>> >> concept lines up very well.
>> >>
>> >>
>> >> -David
>> >>
>> >> On Jul 31, 2011, at 1:31 PM, Romain Manni-Bucau wrote:
>> >>
>> >>> I commited,
>> >>>
>> >>> you can test it with this interface:
>> >>>
>> >>> @Repository public interface Foo {
>> >>>  MyEntity findByXXX(String xxx);
>> >>>  MyEntity findById(long i);
>> >>>  List<MyEntity> findByBar(int bar);
>> >>>  // ...
>> >>> }
>> >>>
>> >>> look the logs you'll have the glbal jndi name ;)
>> >>>
>> >>> - Romain
>> >>>
>> >>> 2011/7/31 Romain Manni-Bucau <rm...@gmail.com>
>> >>>
>> >>>> Up ;)
>> >>>>
>> >>>> Nobody thinks it can be useful?
>> >>>>
>> >>>> /me still waits for some feedbacks before commiting a first
>> version...
>> >>>>
>> >>>> - Romain
>> >>>>
>> >>>> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com>
>> a
>> >>>> écrit :
>> >>>>
>> >>>>> 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 <rm...@gmail.com>
>> >>>>>
>> >>>>>> 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
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>
>> >>>>
>> >>
>> >>
>>
>>
>

Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
Thanks for this feedback.

What you describe was my first thought but then i wanted to avoid standard
annotation while this feature is not in the specs but if  you think it is ok
it is ok for me too on this point.

Where i'm not agree (at least for a first step) is to specify a proxy: the
goal is exactly what you said at the end "sometimes the interface is enough"
and it means too "sometimes implementation - as simple it can be - is too
complex" so i don't want to add something which can be a bit complicated
(proxy doesn't seem so easy to expose them IMHO). We could replce it by a
convention: instead of a proxy the user give a class and the matching is
done from methods name (_ could replace a star for regex). But in the spirit
of give user simple thing i prefer to avoid any handler logic here which
could conflict with interceptors for classical cases.

I'll have a try to implement a first version and if it works i'll remove
@Repository.

- Romain

2011/8/2 David Blevins <da...@gmail.com>

> A home interface impl is essentially a stateless session bean.  We could do
> the same.
>
> We just need to tag the interface as a session bean in the
> AnnotationDeployer and add it to the app.  Then we can support transactions,
> security, interceptors, decorators and more.  It could even be a web service
> or a jax-rs service.  We could allow people to tag their @Repository bean as
> any kind of session bean so @Stateful with EXTENDED persistence context
> would even work. The interface becomes a business local interface and people
> can reference it via @EJB.  The persistence context reference could be added
> to the bean meta-data as a plain <persistence-context> reference and
> injected with a setter (just need to add another interface to supply the
> setter method).
>
> We could do something tricky like use the Repository.class itself as the
> bean class -- or some other well known class.  When we see it we create our
> AnnotationFinder from the interface instead of the bean class.  We normally
> check that people aren't using annotations in interfaces, but in this case
> we would just skip checking @Repository beans and allow it.
>
> Then all the data is passed to the assembler as usual.  Very early in the
> createApplication() method we would generate the proxy class for any
> @Repository bean and set that class as the bean class.
>
> That just leaves "how to construct the proxy" as the last unsolved issue.
>  For that we could do something simple.  This is cool... we could establish
> the convention that if you use an implementation of
> java.lang.reflect.InvocationHandler as your bean class, then we construct it
> with full dependency injection, create the proxy for you and pass it back
> from BeanContext.newInstance().  So I guess we wouldn't need a second
> interface for the EntityManager setter, we just put it right in the
> QueryProxy class.
>
> And really at this point, we actually don't need "Repository" specific
> logic.  We could enable this processing for any interface that wishes to
> supply a handler and be an bean.
>
>    @Stateless
>    @PersistenceContext(unitName = "fooUnit", name = "foo")
>    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
>     public interface Foo {
>        MyEntity findByXXX(String xxx);
>
>        MyEntity findById(long i);
>
>        List<MyEntity> findByBar(int bar);
>        // ...
>    }
>
> We could still offer a @Repository implementation, but as a
> meta-annotation.  So when we scan for all @Proxy beans we would pick it up
> automatically with the right implementation.
>
>    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
>    @Metatype
>    @Target({ElementType.TYPE})
>    @Retention(RetentionPolicy.RUNTIME)
>    public static @interface Repository {
>    }
>
> Now we or anyone else can do a million more things just like this.  The
> basic idea is -- sometimes the interface is enough!
>
>
> Thoughts?
>
>
> -David
>
>
> On Aug 2, 2011, at 11:15 AM, Romain Manni-Bucau wrote:
>
> > Today it is a simple implementation managing only finders but if someone
> > give me some clues to manage correctly transaction i would like to add
> > persistence methods.
> >
> > - Romain
> >
> > 2011/8/2 David Blevins <da...@gmail.com>
> >
> >> This looks like a cool idea.  It's not worth investigating, but this is
> >> exactly how CMP home interfaces look.  The developer creates an
> interface
> >> with "create" and "find" method and the container makes it work.
> >>
> >> Home interfaces were actually pretty useful for CMP, just CMP itself was
> >> terrible and home interfaces weren't very useful for session beans.  So
> they
> >> got the axe in EJB 3.0.
> >>
> >> I haven't had a chance to check out the implementation code, but the
> >> concept lines up very well.
> >>
> >>
> >> -David
> >>
> >> On Jul 31, 2011, at 1:31 PM, Romain Manni-Bucau wrote:
> >>
> >>> I commited,
> >>>
> >>> you can test it with this interface:
> >>>
> >>> @Repository public interface Foo {
> >>>  MyEntity findByXXX(String xxx);
> >>>  MyEntity findById(long i);
> >>>  List<MyEntity> findByBar(int bar);
> >>>  // ...
> >>> }
> >>>
> >>> look the logs you'll have the glbal jndi name ;)
> >>>
> >>> - Romain
> >>>
> >>> 2011/7/31 Romain Manni-Bucau <rm...@gmail.com>
> >>>
> >>>> Up ;)
> >>>>
> >>>> Nobody thinks it can be useful?
> >>>>
> >>>> /me still waits for some feedbacks before commiting a first version...
> >>>>
> >>>> - Romain
> >>>>
> >>>> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com>
> a
> >>>> écrit :
> >>>>
> >>>>> 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 <rm...@gmail.com>
> >>>>>
> >>>>>> 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
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>
> >>>>
> >>
> >>
>
>

Re: openejb & dynamic finders/dao?

Posted by David Blevins <da...@gmail.com>.
A home interface impl is essentially a stateless session bean.  We could do the same.

We just need to tag the interface as a session bean in the AnnotationDeployer and add it to the app.  Then we can support transactions, security, interceptors, decorators and more.  It could even be a web service or a jax-rs service.  We could allow people to tag their @Repository bean as any kind of session bean so @Stateful with EXTENDED persistence context would even work. The interface becomes a business local interface and people can reference it via @EJB.  The persistence context reference could be added to the bean meta-data as a plain <persistence-context> reference and injected with a setter (just need to add another interface to supply the setter method).

We could do something tricky like use the Repository.class itself as the bean class -- or some other well known class.  When we see it we create our AnnotationFinder from the interface instead of the bean class.  We normally check that people aren't using annotations in interfaces, but in this case we would just skip checking @Repository beans and allow it.

Then all the data is passed to the assembler as usual.  Very early in the createApplication() method we would generate the proxy class for any @Repository bean and set that class as the bean class.

That just leaves "how to construct the proxy" as the last unsolved issue.  For that we could do something simple.  This is cool... we could establish the convention that if you use an implementation of java.lang.reflect.InvocationHandler as your bean class, then we construct it with full dependency injection, create the proxy for you and pass it back from BeanContext.newInstance().  So I guess we wouldn't need a second interface for the EntityManager setter, we just put it right in the QueryProxy class.

And really at this point, we actually don't need "Repository" specific logic.  We could enable this processing for any interface that wishes to supply a handler and be an bean.

    @Stateless
    @PersistenceContext(unitName = "fooUnit", name = "foo")
    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
    public interface Foo {
        MyEntity findByXXX(String xxx);

        MyEntity findById(long i);

        List<MyEntity> findByBar(int bar);
        // ...
    }

We could still offer a @Repository implementation, but as a meta-annotation.  So when we scan for all @Proxy beans we would pick it up automatically with the right implementation.

    @Proxy(handler = org.apache.openejb.util.QueryProxy.class)
    @Metatype
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Repository {
    }

Now we or anyone else can do a million more things just like this.  The basic idea is -- sometimes the interface is enough!


Thoughts?


-David


On Aug 2, 2011, at 11:15 AM, Romain Manni-Bucau wrote:

> Today it is a simple implementation managing only finders but if someone
> give me some clues to manage correctly transaction i would like to add
> persistence methods.
> 
> - Romain
> 
> 2011/8/2 David Blevins <da...@gmail.com>
> 
>> This looks like a cool idea.  It's not worth investigating, but this is
>> exactly how CMP home interfaces look.  The developer creates an interface
>> with "create" and "find" method and the container makes it work.
>> 
>> Home interfaces were actually pretty useful for CMP, just CMP itself was
>> terrible and home interfaces weren't very useful for session beans.  So they
>> got the axe in EJB 3.0.
>> 
>> I haven't had a chance to check out the implementation code, but the
>> concept lines up very well.
>> 
>> 
>> -David
>> 
>> On Jul 31, 2011, at 1:31 PM, Romain Manni-Bucau wrote:
>> 
>>> I commited,
>>> 
>>> you can test it with this interface:
>>> 
>>> @Repository public interface Foo {
>>>  MyEntity findByXXX(String xxx);
>>>  MyEntity findById(long i);
>>>  List<MyEntity> findByBar(int bar);
>>>  // ...
>>> }
>>> 
>>> look the logs you'll have the glbal jndi name ;)
>>> 
>>> - Romain
>>> 
>>> 2011/7/31 Romain Manni-Bucau <rm...@gmail.com>
>>> 
>>>> Up ;)
>>>> 
>>>> Nobody thinks it can be useful?
>>>> 
>>>> /me still waits for some feedbacks before commiting a first version...
>>>> 
>>>> - Romain
>>>> 
>>>> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com> a
>>>> écrit :
>>>> 
>>>>> 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 <rm...@gmail.com>
>>>>> 
>>>>>> 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
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>> 
>>>> 
>> 
>> 


Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
Today it is a simple implementation managing only finders but if someone
give me some clues to manage correctly transaction i would like to add
persistence methods.

- Romain

2011/8/2 David Blevins <da...@gmail.com>

> This looks like a cool idea.  It's not worth investigating, but this is
> exactly how CMP home interfaces look.  The developer creates an interface
> with "create" and "find" method and the container makes it work.
>
> Home interfaces were actually pretty useful for CMP, just CMP itself was
> terrible and home interfaces weren't very useful for session beans.  So they
> got the axe in EJB 3.0.
>
> I haven't had a chance to check out the implementation code, but the
> concept lines up very well.
>
>
> -David
>
> On Jul 31, 2011, at 1:31 PM, Romain Manni-Bucau wrote:
>
> > I commited,
> >
> > you can test it with this interface:
> >
> > @Repository public interface Foo {
> >   MyEntity findByXXX(String xxx);
> >   MyEntity findById(long i);
> >   List<MyEntity> findByBar(int bar);
> >   // ...
> > }
> >
> > look the logs you'll have the glbal jndi name ;)
> >
> > - Romain
> >
> > 2011/7/31 Romain Manni-Bucau <rm...@gmail.com>
> >
> >> Up ;)
> >>
> >> Nobody thinks it can be useful?
> >>
> >> /me still waits for some feedbacks before commiting a first version...
> >>
> >> - Romain
> >>
> >> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com> a
> >> écrit :
> >>
> >>> 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 <rm...@gmail.com>
> >>>
> >>>> 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
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
> >>
> >>
>
>

Re: openejb & dynamic finders/dao?

Posted by David Blevins <da...@gmail.com>.
This looks like a cool idea.  It's not worth investigating, but this is exactly how CMP home interfaces look.  The developer creates an interface with "create" and "find" method and the container makes it work.

Home interfaces were actually pretty useful for CMP, just CMP itself was terrible and home interfaces weren't very useful for session beans.  So they got the axe in EJB 3.0.

I haven't had a chance to check out the implementation code, but the concept lines up very well.


-David

On Jul 31, 2011, at 1:31 PM, Romain Manni-Bucau wrote:

> I commited,
> 
> you can test it with this interface:
> 
> @Repository public interface Foo {
>   MyEntity findByXXX(String xxx);
>   MyEntity findById(long i);
>   List<MyEntity> findByBar(int bar);
>   // ...
> }
> 
> look the logs you'll have the glbal jndi name ;)
> 
> - Romain
> 
> 2011/7/31 Romain Manni-Bucau <rm...@gmail.com>
> 
>> Up ;)
>> 
>> Nobody thinks it can be useful?
>> 
>> /me still waits for some feedbacks before commiting a first version...
>> 
>> - Romain
>> 
>> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com> a
>> écrit :
>> 
>>> 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 <rm...@gmail.com>
>>> 
>>>> 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
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>> 
>> 


Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
I commited,

you can test it with this interface:

@Repository public interface Foo {
   MyEntity findByXXX(String xxx);
   MyEntity findById(long i);
   List<MyEntity> findByBar(int bar);
   // ...
}

look the logs you'll have the glbal jndi name ;)

- Romain

2011/7/31 Romain Manni-Bucau <rm...@gmail.com>

> Up ;)
>
> Nobody thinks it can be useful?
>
> /me still waits for some feedbacks before commiting a first version...
>
> - Romain
>
> Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com> a
> écrit :
>
> > 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 <rm...@gmail.com>
> >
> >> 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
> >>
> >>
> >>
> >>
> >>
>
>

Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
Up ;)

Nobody thinks it can be useful?

/me still waits for some feedbacks before commiting a first version...

- Romain

Le 29 juil. 2011 22:37, "Romain Manni-Bucau" <rm...@gmail.com> a
écrit :
> 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 <rm...@gmail.com>
>
>> 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
>>
>>
>>
>>
>>

Re: openejb & dynamic finders/dao?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
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 <rm...@gmail.com>

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