Title: Dynamic DAO Implementation
Many aspects of Data Access Objects (DAOs) are very repetitive and boiler plate. As a fun and experimental feature, TomEE supports dynamically implementing an interface that is seen to have standard DAO-style methods.
The interface has to be annotated with @PersistenceContext to define which EntityManager to use.
Methods should respect these conventions:
Dynamic finder can have as much as you want field constraints. For String like is used and for other type equals is used.
package org.superbiz.dynamic; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; @Entity @NamedQueries({ @NamedQuery(name = "dynamic-ejb-impl-test.query", query = "SELECT u FROM User AS u WHERE u.name LIKE :name"), @NamedQuery(name = "dynamic-ejb-impl-test.all", query = "SELECT u FROM User AS u") }) 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; } }
package org.superbiz.dynamic; import javax.ejb.Stateless; import javax.persistence.PersistenceContext; import java.util.Collection; import java.util.Map; @Stateless @PersistenceContext(name = "dynamic") public interface UserDao { User findById(long id); Collection<User> findByName(String name); Collection<User> findByNameAndAge(String name, int age); Collection<User> findAll(); Collection<User> findAll(int first, int max); Collection<User> namedQuery(String name, Map<String, ?> params, int first, int max); Collection<User> namedQuery(String name, int first, int max, Map<String, ?> params); Collection<User> namedQuery(String name, Map<String, ?> params); Collection<User> namedQuery(String name); Collection<User> query(String value, Map<String, ?> params); void save(User u); void delete(User u); User update(User u); }
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" 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="dynamic" transaction-type="JTA"> <jta-data-source>jdbc/dynamicDB</jta-data-source> <class>org.superbiz.dynamic.User</class> <properties> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/> </properties> </persistence-unit> </persistence>
package org.superbiz.dynamic; import junit.framework.Assert; import org.junit.BeforeClass; import org.junit.Test; import javax.ejb.EJBException; import javax.ejb.Stateless; import javax.ejb.embeddable.EJBContainer; import javax.naming.Context; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; public class DynamicUserDaoTest { private static UserDao dao; private static Util util; @BeforeClass public static void init() throws Exception { final Properties p = new Properties(); p.put("jdbc/dynamicDB", "new://Resource?type=DataSource"); p.put("jdbc/dynamicDB.JdbcDriver", "org.hsqldb.jdbcDriver"); p.put("jdbc/dynamicDB.JdbcUrl", "jdbc:hsqldb:mem:moviedb"); p.put("jdbc/dynamicDB.UserName", "sa"); p.put("jdbc/dynamicDB.Password", ""); final Context context = EJBContainer.createEJBContainer(p).getContext(); dao = (UserDao) context.lookup("java:global/dynamic-dao-implementation/UserDao"); util = (Util) context.lookup("java:global/dynamic-dao-implementation/Util"); util.init(); // init database } @Test public void simple() { User user = dao.findById(1); assertNotNull(user); assertEquals(1, user.getId()); } @Test public void findAll() { Collection<User> users = dao.findAll(); assertEquals(10, users.size()); } @Test public void pagination() { Collection<User> users = dao.findAll(0, 5); assertEquals(5, users.size()); users = dao.findAll(6, 1); assertEquals(1, users.size()); assertEquals(7, users.iterator().next().getId()); } @Test public void persist() { User u = new User(); dao.save(u); assertNotNull(u.getId()); util.remove(u); } @Test public void remove() { User u = new User(); dao.save(u); assertNotNull(u.getId()); dao.delete(u); try { dao.findById(u.getId()); Assert.fail(); } catch (EJBException ee) { assertTrue(ee.getCause() instanceof NoResultException); } } @Test public void merge() { User u = new User(); u.setAge(1); dao.save(u); assertEquals(1, u.getAge()); assertNotNull(u.getId()); u.setAge(2); dao.update(u); assertEquals(2, u.getAge()); dao.delete(u); } @Test public void oneCriteria() { Collection<User> users = dao.findByName("foo"); assertEquals(4, users.size()); for (User user : users) { assertEquals("foo", user.getName()); } } @Test public void twoCriteria() { Collection<User> users = dao.findByNameAndAge("bar-1", 1); assertEquals(1, users.size()); User user = users.iterator().next(); assertEquals("bar-1", user.getName()); assertEquals(1, user.getAge()); } @Test public void query() { Map<String, Object> params = new HashMap<String, Object>(); params.put("name", "foo"); Collection<User> users = dao.namedQuery("dynamic-ejb-impl-test.query", params, 0, 100); assertEquals(4, users.size()); users = dao.namedQuery("dynamic-ejb-impl-test.query", params); assertEquals(4, users.size()); users = dao.namedQuery("dynamic-ejb-impl-test.query", params, 0, 2); assertEquals(2, users.size()); users = dao.namedQuery("dynamic-ejb-impl-test.query", 0, 2, params); assertEquals(2, users.size()); users = dao.namedQuery("dynamic-ejb-impl-test.all"); assertEquals(10, users.size()); params.remove("name"); params.put("age", 1); users = dao.query("SELECT u FROM User AS u WHERE u.age = :age", params); assertEquals(3, users.size()); } @Stateless public static class Util { @PersistenceContext private EntityManager em; public void remove(User o) { em.remove(em.find(User.class, o.getId())); } public void init() { for (int i = 0; i < 10; i++) { User u = new User(); u.setAge(i % 4); if (i % 3 == 0) { u.setName("foo"); } else { u.setName("bar-" + i); } em.persist(u); } } } }
------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.superbiz.dynamic.DynamicUserDaoTest Apache OpenEJB 4.0.0-beta-1 build: 20111002-04:06 http://tomee.apache.org/ INFO - openejb.home = /Users/dblevins/examples/dynamic-dao-implementation INFO - openejb.base = /Users/dblevins/examples/dynamic-dao-implementation INFO - Using 'javax.ejb.embeddable.EJBContainer=true' INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service) INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager) INFO - Configuring Service(id=jdbc/dynamicDB, type=Resource, provider-id=Default JDBC Database) INFO - Found EjbModule in classpath: /Users/dblevins/examples/dynamic-dao-implementation/target/classes INFO - Found EjbModule in classpath: /Users/dblevins/examples/dynamic-dao-implementation/target/test-classes INFO - Beginning load: /Users/dblevins/examples/dynamic-dao-implementation/target/classes INFO - Beginning load: /Users/dblevins/examples/dynamic-dao-implementation/target/test-classes INFO - Configuring enterprise application: /Users/dblevins/examples/dynamic-dao-implementation INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container) INFO - Auto-creating a container for bean UserDao: Container(type=STATELESS, id=Default Stateless Container) INFO - Configuring Service(id=Default Managed Container, type=Container, provider-id=Default Managed Container) INFO - Auto-creating a container for bean org.superbiz.dynamic.DynamicUserDaoTest: Container(type=MANAGED, id=Default Managed Container) INFO - Configuring PersistenceUnit(name=dynamic) INFO - Auto-creating a Resource with id 'jdbc/dynamicDBNonJta' of type 'DataSource for 'dynamic'. INFO - Configuring Service(id=jdbc/dynamicDBNonJta, type=Resource, provider-id=jdbc/dynamicDB) INFO - Adjusting PersistenceUnit dynamic <non-jta-data-source> to Resource ID 'jdbc/dynamicDBNonJta' from 'null' INFO - Enterprise application "/Users/dblevins/examples/dynamic-dao-implementation" loaded. INFO - Assembling app: /Users/dblevins/examples/dynamic-dao-implementation INFO - PersistenceUnit(name=dynamic, provider=org.apache.openjpa.persistence.PersistenceProviderImpl) - provider time 417ms INFO - Jndi(name="java:global/dynamic-dao-implementation/UserDao!org.superbiz.dynamic.UserDao") INFO - Jndi(name="java:global/dynamic-dao-implementation/UserDao") INFO - Jndi(name="java:global/dynamic-dao-implementation/Util!org.superbiz.dynamic.DynamicUserDaoTest$Util") INFO - Jndi(name="java:global/dynamic-dao-implementation/Util") INFO - Jndi(name="java:global/EjbModule346613126/org.superbiz.dynamic.DynamicUserDaoTest!org.superbiz.dynamic.DynamicUserDaoTest") INFO - Jndi(name="java:global/EjbModule346613126/org.superbiz.dynamic.DynamicUserDaoTest") INFO - Created Ejb(deployment-id=UserDao, ejb-name=UserDao, container=Default Stateless Container) INFO - Created Ejb(deployment-id=Util, ejb-name=Util, container=Default Stateless Container) INFO - Created Ejb(deployment-id=org.superbiz.dynamic.DynamicUserDaoTest, ejb-name=org.superbiz.dynamic.DynamicUserDaoTest, container=Default Managed Container) INFO - Started Ejb(deployment-id=UserDao, ejb-name=UserDao, container=Default Stateless Container) INFO - Started Ejb(deployment-id=Util, ejb-name=Util, container=Default Stateless Container) INFO - Started Ejb(deployment-id=org.superbiz.dynamic.DynamicUserDaoTest, ejb-name=org.superbiz.dynamic.DynamicUserDaoTest, container=Default Managed Container) INFO - Deployed Application(path=/Users/dblevins/examples/dynamic-dao-implementation) WARN - Meta class "org.superbiz.dynamic.User_" for entity class org.superbiz.dynamic.User can not be registered with following exception "java.security.PrivilegedActionException: java.lang.ClassNotFoundException: org.superbiz.dynamic.User_" WARN - Query "SELECT u FROM User AS u WHERE u.name LIKE :name" is removed from cache excluded permanently. Query "SELECT u FROM User AS u WHERE u.name LIKE :name" is not cached because it uses pagination.. Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.471 sec Results : Tests run: 9, Failures: 0, Errors: 0, Skipped: 0