1.1 --- a/pom.xml Thu Nov 08 23:28:16 2012 +0400
1.2 +++ b/pom.xml Fri Nov 09 03:00:49 2012 +0400
1.3 @@ -52,6 +52,18 @@
1.4 </execution>
1.5 </executions>
1.6 </plugin>
1.7 + <plugin>
1.8 + <groupId>org.apache.maven.plugins</groupId>
1.9 + <artifactId>maven-jar-plugin</artifactId>
1.10 + <version>${maven-jar-plugin.version}</version>
1.11 + <executions>
1.12 + <execution>
1.13 + <goals>
1.14 + <goal>test-jar</goal>
1.15 + </goals>
1.16 + </execution>
1.17 + </executions>
1.18 + </plugin>
1.19 </plugins>
1.20 </build>
1.21 <dependencies>
1.22 @@ -77,5 +89,10 @@
1.23 <version>${derby.version}</version>
1.24 <scope>test</scope>
1.25 </dependency>
1.26 + <dependency>
1.27 + <groupId>org.codehaus.jackson</groupId>
1.28 + <artifactId>jackson-jaxrs</artifactId>
1.29 + <version>${jackson-jaxrs.version}</version>
1.30 + </dependency>
1.31 </dependencies>
1.32 </project>
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/src/main/java/ru/indvdum/jpa/collections/EntityList.groovy Fri Nov 09 03:00:49 2012 +0400
2.3 @@ -0,0 +1,35 @@
2.4 +package ru.indvdum.jpa.collections
2.5 +
2.6 +/**
2.7 + * @author indvdum (gotoindvdum@gmail.com)
2.8 + * @since 12.01.2012 17:26:13
2.9 + *
2.10 + * @param <E>
2.11 + */
2.12 +class EntityList<E> extends TriggeredList<E> {
2.13 +
2.14 + def ofObject
2.15 + def getter
2.16 + def setter
2.17 +
2.18 + public EntityList() {
2.19 + super()
2.20 + }
2.21 +
2.22 + public EntityList(Object ofObject, String getter, String setter) {
2.23 + this.ofObject = ofObject
2.24 + this.getter = getter
2.25 + this.setter = setter
2.26 + }
2.27 +
2.28 + @Override
2.29 + protected void afterChange() {
2.30 + if(ofObject == null || getter == null || setter == null)
2.31 + return
2.32 + each {
2.33 + if(it."${getter}"() != ofObject) {
2.34 + it."${setter}"(ofObject)
2.35 + }
2.36 + }
2.37 + }
2.38 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/src/main/java/ru/indvdum/jpa/collections/TriggeredList.groovy Fri Nov 09 03:00:49 2012 +0400
3.3 @@ -0,0 +1,93 @@
3.4 +package ru.indvdum.jpa.collections
3.5 +
3.6 +import java.util.Collection
3.7 +import java.util.Iterator
3.8 +import java.util.List
3.9 +import java.util.ListIterator
3.10 +
3.11 +/**
3.12 + * E realization of List interface with support of change notifications
3.13 + *
3.14 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
3.15 + * @since 12.01.2012 13:57:48
3.16 + *
3.17 + * @param <E>
3.18 + */
3.19 +abstract class TriggeredList<E> extends ArrayList<E> {
3.20 +
3.21 + /**
3.22 + * Invoked after changes in list
3.23 + */
3.24 + abstract protected void afterChange();
3.25 +
3.26 + // changing methods
3.27 +
3.28 + @Override
3.29 + public boolean add(E e) {
3.30 + boolean result = super.add(e);
3.31 + afterChange();
3.32 + return result;
3.33 + }
3.34 +
3.35 + @Override
3.36 + public boolean remove(Object o) {
3.37 + boolean result = super.remove(o);
3.38 + afterChange();
3.39 + return result;
3.40 + }
3.41 +
3.42 + @Override
3.43 + public boolean addAll(Collection<? extends E> c) {
3.44 + boolean result = super.addAll(c);
3.45 + afterChange();
3.46 + return result;
3.47 + }
3.48 +
3.49 + @Override
3.50 + public boolean addAll(int index, Collection<? extends E> c) {
3.51 + boolean result = super.addAll(index, c);
3.52 + afterChange();
3.53 + return result;
3.54 + }
3.55 +
3.56 + @Override
3.57 + public boolean removeAll(Collection<?> c) {
3.58 + boolean result = super.removeAll(c);
3.59 + afterChange();
3.60 + return result;
3.61 + }
3.62 +
3.63 + @Override
3.64 + public boolean retainAll(Collection<?> c) {
3.65 + boolean result = super.retainAll(c);
3.66 + afterChange();
3.67 + return result;
3.68 + }
3.69 +
3.70 + @Override
3.71 + public void clear() {
3.72 + super.clear();
3.73 + afterChange();
3.74 + }
3.75 +
3.76 + @Override
3.77 + public E set(int index, E element) {
3.78 + E result = super.set(index, element);
3.79 + afterChange();
3.80 + return result;
3.81 + }
3.82 +
3.83 + @Override
3.84 + public void add(int index, E element) {
3.85 + super.add(index, element);
3.86 + afterChange();
3.87 + }
3.88 +
3.89 + @Override
3.90 + public E remove(int index) {
3.91 + E result = super.remove(index);
3.92 + afterChange();
3.93 + return result;
3.94 + }
3.95 +
3.96 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/src/main/java/ru/indvdum/jpa/dao/JPADataAccessObject.groovy Fri Nov 09 03:00:49 2012 +0400
4.3 @@ -0,0 +1,201 @@
4.4 +package ru.indvdum.jpa.dao
4.5 import java.sql.Connection
4.6 +import java.sql.SQLException
4.7 +import java.util.ResourceBundle;
4.8 import java.util.Map.Entry
4.9 +
4.10 +import javax.persistence.EntityManager
4.11 +import javax.persistence.EntityManagerFactory
4.12 +import javax.persistence.EntityTransaction
4.13 +import javax.persistence.NoResultException
4.14 +import javax.persistence.Persistence
4.15 +import javax.persistence.Query
4.16 +import javax.persistence.criteria.CriteriaBuilder
4.17 +import javax.persistence.criteria.CriteriaQuery
4.18 +import javax.persistence.criteria.Predicate
4.19 +import javax.persistence.criteria.Root
4.20 +import javax.sql.DataSource
4.21 +
4.22 +import org.apache.openjpa.conf.OpenJPAConfiguration
4.23 +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI
4.24 +import org.apache.openjpa.persistence.OpenJPAPersistence
4.25 +import org.slf4j.Logger
4.26 +import org.slf4j.LoggerFactory
4.27 +
4.28 import ru.indvdum.jpa.props.Props;
4.29
4.30 +/**
4.31 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
4.32 + * @since 08.11.2012 23:35:04
4.33 + *
4.34 + */
4.35 +public class JPADataAccessObject {
4.36 +
4.37 protected static String persistenceUnitName = null;
4.38 + protected static EntityManagerFactory emf = Persistence.createEntityManagerFactory(getPersistenceUnitName(), PropertySelector.select());
4.39 + protected EntityManager em = emf.createEntityManager();
4.40 + final static Logger log = LoggerFactory.getLogger(JPADataAccessObject.class.getSimpleName());
4.41 +
4.42 + JPADataAccessObject() {
4.43 + em.isOpen();
4.44 + }
4.45
4.46 protected static String getPersistenceUnitName() {
4.47 if (persistenceUnitName != null)
4.48 return persistenceUnitName;
4.49
4.50 if (persistenceUnitName == null)
4.51 persistenceUnitName = System.getProperty(Props.PERSISTANCE_UNIT_NAME_PROPERTY);
4.52 if (persistenceUnitName == null)
4.53 persistenceUnitName = ResourceBundle.getBundle(Props.JPADAO_PROPERTY_FILE).getString(Props.PERSISTANCE_UNIT_NAME_PROPERTY);
4.54 if (persistenceUnitName == null)
4.55 persistenceUnitName = "database";
4.56 return persistenceUnitName;
4.57 }
4.58 +
4.59 + public static Connection getSQLConnection() throws SQLException {
4.60 + OpenJPAEntityManagerFactorySPI openjpaemf = (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.cast(emf);
4.61 + OpenJPAConfiguration conf = openjpaemf.getConfiguration();
4.62 + DataSource ds = (DataSource) conf.getConnectionFactory();
4.63 + return ds.getConnection();
4.64 + }
4.65 +
4.66 + void close() {
4.67 + em.close()
4.68 + em = null
4.69 + }
4.70 +
4.71 + public <T> T mergeAndRefresh(T object) {
4.72 + def merged = em.merge(object)
4.73 + em.refresh(merged)
4.74 +
4.75 + merged
4.76 + }
4.77 +
4.78 + public <T> T merge(T object) {
4.79 + em.merge(object)
4.80 + }
4.81 +
4.82 + void refresh(Object ... refreshObjects) {
4.83 + for(object in refreshObjects) {
4.84 + em.refresh(object)
4.85 + }
4.86 + }
4.87 +
4.88 + boolean persist(Object ... persistanceQueue) {
4.89 + persistAndRemove(persistanceQueue, new Object[0])
4.90 + }
4.91 +
4.92 + boolean persist(Collection entities) {
4.93 + persistAndRemove(entities.toArray(), new Object[0])
4.94 + }
4.95 +
4.96 + boolean remove(Object ... removeQueue) {
4.97 + persistAndRemove(new Object[0], removeQueue)
4.98 + }
4.99 +
4.100 + boolean remove(Collection entities) {
4.101 + persistAndRemove(new Object[0], entities.toArray())
4.102 + }
4.103 +
4.104 + synchronized boolean persistAndRemove(Object[] persistanceQueue, Object[] removeQueue) {
4.105 + EntityTransaction tx = null
4.106 +
4.107 + try {
4.108 + tx = em.getTransaction()
4.109 + tx.begin()
4.110 +
4.111 + if(persistanceQueue != null) {
4.112 + for(object in persistanceQueue) {
4.113 + if(object != null) {
4.114 + em.persist(object)
4.115 + }
4.116 + }
4.117 + }
4.118 +
4.119 + if(removeQueue != null) {
4.120 + for(object in removeQueue) {
4.121 + if(object != null) {
4.122 + em.remove(object)
4.123 + }
4.124 + }
4.125 + }
4.126 +
4.127 + tx.commit()
4.128 + }
4.129 + catch(Throwable t) {
4.130 + log.error("Error while synchronizing with Database: ", t);
4.131 +
4.132 + if(tx != null && tx.isActive()) {
4.133 + tx.rollback()
4.134 + }
4.135 +
4.136 + return false
4.137 + }
4.138 +
4.139 + return true
4.140 + }
4.141 +
4.142 + public <T> T find(Class<T> entityClass, Object primaryKey) {
4.143 + return em.find(entityClass, primaryKey);
4.144 + }
4.145 +
4.146 + public <T> List<T> list(Class<T> entityClass, Map<String, Object> equalProperties, Map<String, Object> notEqualProperties) {
4.147 + CriteriaBuilder cb = em.getCriteriaBuilder();
4.148 + CriteriaQuery<T> query = cb.createQuery(entityClass);
4.149 + Root<T> root = query.from(entityClass);
4.150 + Collection<Predicate> predicates = new HashSet<Predicate>();
4.151 + if(equalProperties != null) {
4.152 + for(Entry<String, Object> entry: equalProperties.entrySet()){
4.153 + String property = entry.getKey();
4.154 + Object value = entry.getValue();
4.155 + predicates.add(cb.equal(root.get(property), value));
4.156 + }
4.157 + }
4.158 + if(notEqualProperties != null) {
4.159 + for(Entry<String, Object> entry: equalProperties.entrySet()){
4.160 + String property = entry.getKey();
4.161 + Object value = entry.getValue();
4.162 + predicates.add(cb.notEqual(root.get(property), value));
4.163 + }
4.164 + }
4.165 + query.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
4.166 + return em.createQuery(query).getResultList();
4.167 + }
4.168 +
4.169 + public <T> T find(Class<T> entityClass, Map<String, Object> equalProperties, Map<String, Object> notEqualProperties) {
4.170 + CriteriaBuilder cb = em.getCriteriaBuilder();
4.171 + CriteriaQuery<T> query = cb.createQuery(entityClass);
4.172 + Root<T> root = query.from(entityClass);
4.173 + Collection<Predicate> predicates = new HashSet<Predicate>();
4.174 + if(equalProperties != null) {
4.175 + for(Entry<String, Object> entry: equalProperties.entrySet()){
4.176 + String property = entry.getKey();
4.177 + Object value = entry.getValue();
4.178 + predicates.add(cb.equal(root.get(property), value));
4.179 + }
4.180 + }
4.181 + if(notEqualProperties != null) {
4.182 + for(Entry<String, Object> entry: equalProperties.entrySet()){
4.183 + String property = entry.getKey();
4.184 + Object value = entry.getValue();
4.185 + predicates.add(cb.notEqual(root.get(property), value));
4.186 + }
4.187 + }
4.188 + query.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
4.189 + T result = null;
4.190 + try {
4.191 + result = em.createQuery(query).getSingleResult();
4.192 + } catch (NoResultException e) {
4.193 + log.error("Error while searching object in Database: ", e);
4.194 + }
4.195 + return result;
4.196 + }
4.197 +
4.198 + public <T> List<T> list(Class<T> entityClass) {
4.199 + CriteriaQuery<T> query = em.getCriteriaBuilder().createQuery(entityClass);
4.200 + query.from(entityClass);
4.201 + return new ArrayList(em.createQuery(query).getResultList());
4.202 + }
4.203 +
4.204 + public boolean contains(Collection entities) {
4.205 + boolean res = true;
4.206 + entities.each {res &= contains(it)}
4.207 + return res
4.208 + }
4.209 +
4.210 + public boolean contains(Object entity) {
4.211 + return em.contains(entity);
4.212 + }
4.213 +
4.214 + private Object getSingleResult(Query query) {
4.215 + try {
4.216 + return query.getSingleResult()
4.217 + }
4.218 + catch (NoResultException e) {
4.219 + return null
4.220 + }
4.221 + }
4.222 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/src/main/java/ru/indvdum/jpa/dao/PropertySelector.groovy Fri Nov 09 03:00:49 2012 +0400
5.3 @@ -0,0 +1,55 @@
5.4 +package ru.indvdum.jpa.dao
5.5 +
5.6 +/**
5.7 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
5.8 + * @since 03.11.2011 13:53:32
5.9 + *
5.10 + */
5.11 +class PropertySelector {
5.12 + public static final String SHOWSQL = "custom.showsql"
5.13 + public static final String SYNCHRONIZEDB = "custom.synchronize"
5.14 + public static final String SCHEMANAME = "custom.schemaname"
5.15 + public static final String DBDICTIONARY = "custom.dbdictionary"
5.16 + public static final String RUNTIMEENHANCEMENT = "custom.runtimeenhancement"
5.17 +
5.18 + private static Properties properties = new Properties()
5.19 +
5.20 + private static boolean isProperty(String propertyName) {
5.21 + return Boolean.valueOf(properties.getProperty(propertyName)).booleanValue()
5.22 + }
5.23 +
5.24 + private static String getSystemProperty(String propertyName) {
5.25 + properties.getProperty(propertyName)
5.26 + }
5.27 +
5.28 + static void setSystemProperty(String propertyName, String propertyValue) {
5.29 + properties.setProperty(propertyName, propertyValue)
5.30 + }
5.31 +
5.32 + static Map select() {
5.33 + Map<String, String> emfProperties = [:]
5.34 +
5.35 + if(isProperty(SHOWSQL)) {
5.36 + emfProperties["openjpa.Log"] = "DefaultLevel=INFO, Tool=INFO, SQL=TRACE"
5.37 + emfProperties["openjpa.ConnectionFactoryProperties"] = "PrintParameters=true, PrettyPrint=true"
5.38 + }
5.39 +
5.40 + if(isProperty(SYNCHRONIZEDB)) {
5.41 + emfProperties["openjpa.jdbc.SynchronizeMappings"] = "buildSchema(ForeignKeys=true)"
5.42 + }
5.43 +
5.44 + if(getSystemProperty(SCHEMANAME) != null) {
5.45 + emfProperties["openjpa.jdbc.Schema"] = getSystemProperty(SCHEMANAME)
5.46 + }
5.47 +
5.48 + if(getSystemProperty(DBDICTIONARY)) {
5.49 + emfProperties["openjpa.jdbc.DBDictionary"] = getSystemProperty(DBDICTIONARY)
5.50 + }
5.51 +
5.52 + if(getSystemProperty(RUNTIMEENHANCEMENT)) {
5.53 + emfProperties["openjpa.RuntimeUnenhancedClasses"] = getSystemProperty(RUNTIMEENHANCEMENT)
5.54 + }
5.55 +
5.56 + return emfProperties
5.57 + }
5.58 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/src/main/java/ru/indvdum/jpa/entities/AbstractEntity.groovy Fri Nov 09 03:00:49 2012 +0400
6.3 @@ -0,0 +1,132 @@
6.4 +package ru.indvdum.jpa.entities
6.5 +
6.6 +import java.lang.reflect.Modifier
6.7 +
6.8 +import javax.persistence.EmbeddedId
6.9 +import javax.persistence.Entity
6.10 +import javax.persistence.Id
6.11 +import javax.persistence.Transient
6.12 +
6.13 +import org.apache.openjpa.util.OpenJPAId
6.14 +import org.codehaus.jackson.map.ObjectMapper
6.15 +
6.16 +import ru.indvdum.jpa.dao.JPADataAccessObject;
6.17 +
6.18 +/**
6.19 + * @author indvdum (gotoindvdum@gmail.com)
6.20 + * @since 25.12.2011 13:26:57
6.21 + *
6.22 + */
6.23 +abstract class AbstractEntity implements Serializable {
6.24 +
6.25 + @Transient
6.26 + protected ObjectMapper mapper = new ObjectMapper()
6.27 +
6.28 + @Override
6.29 + public String toString() {
6.30 + return getClass().getSimpleName() + ' ' + toJSON()
6.31 + }
6.32 +
6.33 + /**
6.34 + * JSON-like representation of an object
6.35 + *
6.36 + * @return
6.37 + */
6.38 + public String toJSON() {
6.39 + return mapper.writeValueAsString(this)
6.40 + }
6.41 +
6.42 + /*
6.43 + * HashCode, based on root class name of entity class hierarchy and id value
6.44 + *
6.45 + * (non-Javadoc)
6.46 + * @see java.lang.Object#hashCode()
6.47 + */
6.48 + @Override
6.49 + public int hashCode() {
6.50 + Class clazz = getClass()
6.51 + while (
6.52 + clazz.superclass != null
6.53 + && !Modifier.isAbstract(clazz.superclass.modifiers)
6.54 + && clazz.superclass.annotations.find {it instanceof Entity} != null
6.55 + )
6.56 + clazz = clazz.superclass
6.57 + String res = clazz.getName() + ': ' + getIdentifierValue()
6.58 + return res.hashCode()
6.59 + }
6.60 +
6.61 + /*
6.62 + * Comparison, based on entities id's
6.63 + *
6.64 + * (non-Javadoc)
6.65 + * @see java.lang.Object#equals(java.lang.Object)
6.66 + */
6.67 + @Override
6.68 + public boolean equals(Object obj) {
6.69 + if(obj == null)
6.70 + return false
6.71 + if(!(obj.class.isAssignableFrom(this.class) || this.class.isAssignableFrom(obj.class)))
6.72 + return false
6.73 + def id = getIdentifierValue()
6.74 + def objId = (obj as AbstractEntity).getIdentifierValue()
6.75 + return id == objId || id != null && id != this && id.equals(objId)
6.76 + }
6.77 +
6.78 + /**
6.79 + * @return The identifier value of an entity.
6.80 + */
6.81 + @Transient
6.82 + public Object getIdentifierValue() {
6.83 + // an attempt to get id by EntityManagerFactory
6.84 + Object identifier = JPADataAccessObject.emf.getPersistenceUnitUtil().getIdentifier(this)
6.85 + if(identifier instanceof OpenJPAId)
6.86 + return ((OpenJPAId)identifier).getIdObject()
6.87 +
6.88 + def result
6.89 +
6.90 + // an attempt to find id by annotated fields
6.91 + Class clazz = getClass()
6.92 + def self = this
6.93 + Collection fields = clazz.declaredFields
6.94 + while(clazz.superclass != null) {
6.95 + clazz = clazz.superclass
6.96 + fields.addAll(clazz.declaredFields)
6.97 + }
6.98 + fields.grep {
6.99 + !it.synthetic &&
6.100 + !Modifier.isStatic(it.modifiers) &&
6.101 + !Modifier.isTransient(it.modifiers)
6.102 + }.grep {
6.103 + it.annotations.find {
6.104 + it instanceof Id || it instanceof EmbeddedId
6.105 + } != null
6.106 + }.each {
6.107 + boolean isAccessible = it.accessible
6.108 + it.accessible = true
6.109 + result = it.get(self)
6.110 + it.accessible = isAccessible
6.111 + }
6.112 + if(result != null && !(result instanceof Reference && (result as Reference).value == null))
6.113 + return result
6.114 +
6.115 + // an attempt to represent id as a aggregate of all fields
6.116 + // TODO check, why toJSON method don't work
6.117 +// return toJSON()
6.118 + result = '{'
6.119 + fields.grep {
6.120 + !it.synthetic &&
6.121 + !Modifier.isStatic(it.modifiers) &&
6.122 + !Modifier.isTransient(it.modifiers)
6.123 + }.grep {
6.124 + it.annotations.find {
6.125 + it instanceof Transient
6.126 + } == null
6.127 + }.each {
6.128 + boolean isAccessible = it.accessible
6.129 + it.accessible = true
6.130 + result += '"' + it.name + '": "' + it.get(self) + '", '
6.131 + it.accessible = isAccessible
6.132 + }
6.133 + return result
6.134 + }
6.135 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/src/main/java/ru/indvdum/jpa/jndi/SimpleContext.java Fri Nov 09 03:00:49 2012 +0400
7.3 @@ -0,0 +1,173 @@
7.4 +package ru.indvdum.jpa.jndi;
7.5 +
7.6 +import java.util.HashMap;
7.7 +import java.util.Hashtable;
7.8 +import java.util.Map;
7.9 +
7.10 +import javax.naming.Binding;
7.11 +import javax.naming.Context;
7.12 +import javax.naming.Name;
7.13 +import javax.naming.NameClassPair;
7.14 +import javax.naming.NameParser;
7.15 +import javax.naming.NamingEnumeration;
7.16 +import javax.naming.NamingException;
7.17 +
7.18 +/**
7.19 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
7.20 + * @since 03.11.2011 16:37:15
7.21 + *
7.22 + */
7.23 +public class SimpleContext implements Context {
7.24 + private Map<String, Object> _bind_map = new HashMap<String, Object>();
7.25 +
7.26 + public SimpleContext(Hashtable<?, ?> environment) {
7.27 + }
7.28 +
7.29 + @Override
7.30 + public Object addToEnvironment(String propName, Object propVal) throws NamingException {
7.31 + throw new UnsupportedOperationException();
7.32 + }
7.33 +
7.34 + @Override
7.35 + public void bind(Name name, Object obj) throws NamingException {
7.36 + bind(String.valueOf(name), obj);
7.37 + }
7.38 +
7.39 + @Override
7.40 + public void bind(String name, Object obj) throws NamingException {
7.41 + if (_bind_map.get(name) == null) {
7.42 + _bind_map.put(name, obj);
7.43 + } else {
7.44 + throw new NamingException("Already bond.");
7.45 + }
7.46 + }
7.47 +
7.48 + @Override
7.49 + public void close() throws NamingException {
7.50 + }
7.51 +
7.52 + @Override
7.53 + public Name composeName(Name name, Name prefix) throws NamingException {
7.54 + throw new UnsupportedOperationException();
7.55 + }
7.56 +
7.57 + @Override
7.58 + public String composeName(String name, String prefix) throws NamingException {
7.59 + throw new UnsupportedOperationException();
7.60 + }
7.61 +
7.62 + @Override
7.63 + public Context createSubcontext(Name name) throws NamingException {
7.64 + throw new UnsupportedOperationException();
7.65 + }
7.66 +
7.67 + @Override
7.68 + public Context createSubcontext(String name) throws NamingException {
7.69 + throw new UnsupportedOperationException();
7.70 + }
7.71 +
7.72 + @Override
7.73 + public void destroySubcontext(Name name) throws NamingException {
7.74 + throw new UnsupportedOperationException();
7.75 + }
7.76 +
7.77 + @Override
7.78 + public void destroySubcontext(String name) throws NamingException {
7.79 + throw new UnsupportedOperationException();
7.80 + }
7.81 +
7.82 + @Override
7.83 + public Hashtable<?, ?> getEnvironment() throws NamingException {
7.84 + throw new UnsupportedOperationException();
7.85 + }
7.86 +
7.87 + @Override
7.88 + public String getNameInNamespace() throws NamingException {
7.89 + throw new UnsupportedOperationException();
7.90 + }
7.91 +
7.92 + @Override
7.93 + public NameParser getNameParser(Name name) throws NamingException {
7.94 + throw new UnsupportedOperationException();
7.95 + }
7.96 +
7.97 + @Override
7.98 + public NameParser getNameParser(String name) throws NamingException {
7.99 + throw new UnsupportedOperationException();
7.100 + }
7.101 +
7.102 + @Override
7.103 + public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
7.104 + throw new UnsupportedOperationException();
7.105 + }
7.106 +
7.107 + @Override
7.108 + public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
7.109 + throw new UnsupportedOperationException();
7.110 + }
7.111 +
7.112 + @Override
7.113 + public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
7.114 + throw new UnsupportedOperationException();
7.115 + }
7.116 +
7.117 + @Override
7.118 + public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
7.119 + throw new UnsupportedOperationException();
7.120 + }
7.121 +
7.122 + @Override
7.123 + public Object lookup(Name name) throws NamingException {
7.124 + return lookup(String.valueOf(name));
7.125 + }
7.126 +
7.127 + @Override
7.128 + public Object lookup(String name) throws NamingException {
7.129 + return _bind_map.get(name);
7.130 + }
7.131 +
7.132 + @Override
7.133 + public Object lookupLink(Name name) throws NamingException {
7.134 + throw new UnsupportedOperationException();
7.135 + }
7.136 +
7.137 + @Override
7.138 + public Object lookupLink(String name) throws NamingException {
7.139 + throw new UnsupportedOperationException();
7.140 + }
7.141 +
7.142 + @Override
7.143 + public void rebind(Name name, Object obj) throws NamingException {
7.144 + rebind(String.valueOf(name), obj);
7.145 + }
7.146 +
7.147 + @Override
7.148 + public void rebind(String name, Object obj) throws NamingException {
7.149 + _bind_map.put(name, obj);
7.150 + }
7.151 +
7.152 + @Override
7.153 + public Object removeFromEnvironment(String propName) throws NamingException {
7.154 + throw new UnsupportedOperationException();
7.155 + }
7.156 +
7.157 + @Override
7.158 + public void rename(Name oldName, Name newName) throws NamingException {
7.159 + throw new UnsupportedOperationException();
7.160 + }
7.161 +
7.162 + @Override
7.163 + public void rename(String oldName, String newName) throws NamingException {
7.164 + throw new UnsupportedOperationException();
7.165 + }
7.166 +
7.167 + @Override
7.168 + public void unbind(Name name) throws NamingException {
7.169 + unbind(String.valueOf(name));
7.170 + }
7.171 +
7.172 + @Override
7.173 + public void unbind(String name) throws NamingException {
7.174 + _bind_map.remove(name);
7.175 + }
7.176 +}
7.177 \ No newline at end of file
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/src/main/java/ru/indvdum/jpa/jndi/SimpleContextFactory.java Fri Nov 09 03:00:49 2012 +0400
8.3 @@ -0,0 +1,28 @@
8.4 +package ru.indvdum.jpa.jndi;
8.5 +
8.6 +import java.util.Hashtable;
8.7 +
8.8 +import javax.naming.Context;
8.9 +import javax.naming.NamingException;
8.10 +import javax.naming.spi.InitialContextFactory;
8.11 +
8.12 +/**
8.13 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
8.14 + * @since 03.11.2011 16:37:21
8.15 + *
8.16 + */
8.17 +public class SimpleContextFactory implements InitialContextFactory {
8.18 +
8.19 + private static Context simpleContext = null;
8.20 +
8.21 + @Override
8.22 + public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
8.23 + if (simpleContext == null) {
8.24 + synchronized (this) {
8.25 + simpleContext = new SimpleContext(environment);
8.26 + }
8.27 + }
8.28 +
8.29 + return simpleContext;
8.30 + }
8.31 +}
8.32 \ No newline at end of file
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/src/main/java/ru/indvdum/jpa/props/Props.java Fri Nov 09 03:00:49 2012 +0400
9.3 @@ -0,0 +1,14 @@
9.4 +package ru.indvdum.jpa.props;
9.5 +
9.6 +/**
9.7 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
9.8 + * @since 09.11.2012 1:14:24
9.9 + *
9.10 + */
9.11 +public class Props {
9.12 +
9.13 + public static final String PERSISTANCE_UNIT_NAME_PROPERTY = "jpadao.persistenceUnitName";
9.14 + public static final String DATA_SOURCE_PROPERTY = "jpadao.datasource";
9.15 + public static final String JPADAO_PROPERTY_FILE = "jpadao";
9.16 +
9.17 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/src/main/resources/jndi.properties Fri Nov 09 03:00:49 2012 +0400
10.3 @@ -0,0 +1,1 @@
10.4 +java.naming.factory.initial=ru.indvdum.jpa.jndi.SimpleContextFactory
10.5 \ No newline at end of file
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/src/test/java/ru/indvdum/jpa/tests/AbstractJPAEntityTest.groovy Fri Nov 09 03:00:49 2012 +0400
11.3 @@ -0,0 +1,326 @@
11.4 +package ru.indvdum.jpa.tests
11.5 +;
11.6 +
11.7 +import static org.junit.Assert.*
11.8 +
11.9 +import java.lang.reflect.Field
11.10 +import java.lang.reflect.Method
11.11 +import java.lang.reflect.Modifier
11.12 +
11.13 +import javax.persistence.EmbeddedId
11.14 +import javax.persistence.Entity
11.15 +import javax.persistence.GeneratedValue
11.16 +import javax.persistence.Id
11.17 +import javax.persistence.IdClass
11.18 +import javax.persistence.Transient
11.19 +
11.20 +import org.junit.Test
11.21 +
11.22 +import ru.indvdum.jpa.dao.JPADataAccessObject
11.23 +import ru.indvdum.jpa.entities.AbstractEntity;
11.24 +
11.25 +
11.26 +/**
11.27 + * JUnit test case for testing of a creating, listing, updating and removing
11.28 + * operations with database of the JPA entities.
11.29 + *
11.30 + * @author indvdum (gotoindvdum@gmail.com)
11.31 + * @since 23.12.2011 22:54:26
11.32 + *
11.33 + */
11.34 +abstract class AbstractJPAEntityTest extends AbstractJPATest {
11.35 +
11.36 + protected def uniqueValue = 1
11.37 + protected JPADataAccessObject dao = null
11.38 + protected Set toRemove = new HashSet()
11.39 +
11.40 + @Test
11.41 + public void testEntity() {
11.42 + dao = createDAO()
11.43 + try {
11.44 + testEntity(getEntityClass())
11.45 + } finally {
11.46 + dao.close()
11.47 + }
11.48 + }
11.49 +
11.50 + /**
11.51 + * @return Your implementation of JPADataAccessObject
11.52 + */
11.53 + protected abstract JPADataAccessObject createDAO();
11.54 +
11.55 + /**
11.56 + * Test creating, listing, updating, and removing of an {@code entityClass} object
11.57 + *
11.58 + * @param entityClass
11.59 + */
11.60 + protected void testEntity(Class entityClass) {
11.61 +
11.62 + assert AbstractEntity.class.isAssignableFrom(entityClass)
11.63 +
11.64 + Map<String, Object> fieldsValues
11.65 + def dbEntity
11.66 +
11.67 + // creating
11.68 + def entity = createEntity(entityClass)
11.69 + testCreatedEntity(entity)
11.70 +
11.71 + // listing
11.72 + toRemove.each {
11.73 + assert dao.list(it.class).each { it2 ->
11.74 + assert it.class.isAssignableFrom(it2.class)
11.75 + }.size() == toRemove.findAll { it2 ->
11.76 + it.class.isAssignableFrom(it2.class)
11.77 + }.size()
11.78 + }
11.79 +
11.80 + // updating
11.81 + fieldsValues = updateFields(entity)
11.82 + assert dao.persist(entity)
11.83 + assert dao.contains(entity)
11.84 + dbEntity = dao.find(entityClass, (entity as AbstractEntity).getIdentifierValue())
11.85 + assert dbEntity != null
11.86 + assert checkEntityFieldValues(dbEntity, fieldsValues)
11.87 + testUpdatedEntity(entity)
11.88 +
11.89 + // removing
11.90 + assert dao.remove(toRemove)
11.91 + assert !dao.contains(toRemove)
11.92 + testRemovedEntity(entity)
11.93 + }
11.94 +
11.95 + /**
11.96 + * Create and persist to database an {@code entityClass} object
11.97 + *
11.98 + * @param entityClass
11.99 + * @return created entity object
11.100 + */
11.101 + protected Object createEntity(Class entityClass) {
11.102 + assertNotNull entityClass.annotations.find {it instanceof Entity}
11.103 + def entity = entityClass.newInstance()
11.104 + assert entity.class == entityClass
11.105 + toRemove.add(entity)
11.106 +
11.107 + Map<String, Object> fieldsValues = updateFields(entity)
11.108 + assert dao.persist(entity)
11.109 + assert dao.contains(entity)
11.110 + def dbEntity = dao.find(entityClass, (entity as AbstractEntity).getIdentifierValue())
11.111 + assert dbEntity != null
11.112 + assert checkEntityFieldValues(dbEntity, fieldsValues)
11.113 +
11.114 + return entity
11.115 + }
11.116 +
11.117 + /**
11.118 + * Update all fields of an {@code entity} object
11.119 + * @param entity
11.120 + * @return generated field values
11.121 + */
11.122 + protected Map<String, Object> updateFields(Object entity) {
11.123 + Map<String, Object> fieldsValues = new HashMap<String, Object>()
11.124 + getFields(entity).grep {
11.125 + it.annotations.find {
11.126 + it instanceof Transient || it instanceof GeneratedValue
11.127 + } == null
11.128 + }.each {
11.129 + def newValue = generateFieldValue(entity, it)
11.130 + setFieldValueBySetter(entity, it, newValue)
11.131 + fieldsValues.put(it.name, newValue)
11.132 + }
11.133 + return fieldsValues
11.134 + }
11.135 +
11.136 + /**
11.137 + * Collections and arrays will not be processed
11.138 + *
11.139 + * @param entity
11.140 + * @param field
11.141 + * @return generated field value
11.142 + */
11.143 + protected Object generateFieldValue(Object entity, Field field) {
11.144 + def type = field.getType()
11.145 + def newValue
11.146 + if(type.toString() == 'boolean' || type == Boolean.class) {
11.147 + newValue = (boolean) (uniqueValue++ % 2i == 0i)
11.148 + } else if(type.toString() == 'byte' || type == Byte) {
11.149 + newValue = (byte) (uniqueValue++ % Byte.MAX_VALUE + 1i)
11.150 + } else if(type.toString() == 'char' || type == Character) {
11.151 + newValue = (char) (uniqueValue++ % (int) Character.MAX_VALUE + 1i)
11.152 + } else if(type.toString() == 'short' || type == Short) {
11.153 + newValue = (short) (uniqueValue++ % Short.MAX_VALUE + 1i)
11.154 + } else if(type.toString() == 'int' || type == Integer) {
11.155 + newValue = (int) (uniqueValue++ % Integer.MAX_VALUE + 1i)
11.156 + } else if(type.toString() == 'long' || type == Long) {
11.157 + newValue = (long) uniqueValue++ % Long.MAX_VALUE + 1L
11.158 + } else if(type.toString() == 'float' || type == Float) {
11.159 + newValue = (float) uniqueValue++ % Float.MAX_VALUE + 1f
11.160 + } else if(type.toString() == 'double' || type == Double) {
11.161 + newValue = (double) uniqueValue++ % Double.MAX_VALUE + 1d
11.162 + } else if(type == String.class) {
11.163 + newValue = (String) "test${uniqueValue++}"
11.164 + } else if(type instanceof Class && (type as Class).annotations.find {it instanceof Entity} != null) { // modifying of a primary keys is deprecated
11.165 + // an attempt to use already created entities
11.166 + def currentValue = getFieldValue(entity, field)
11.167 + newValue = toRemove.find {it.class == type && it != currentValue}
11.168 + if(newValue == null)
11.169 + newValue = createEntity(type as Class)
11.170 + } else if(
11.171 + type instanceof Class
11.172 + && !(
11.173 + field.clazz.annotations.find {it instanceof IdClass} != null
11.174 + && field.declaredAnnotations.find {it instanceof Id} != null
11.175 + )
11.176 + && field.declaredAnnotations.find {it instanceof EmbeddedId} == null
11.177 + && !Collection.class.isAssignableFrom(type)
11.178 + && !(type as Class).isArray()
11.179 + ) { // modifying of a primary keys is deprecated
11.180 + newValue = (type as Class).newInstance()
11.181 + } else {
11.182 + newValue = getFieldValue(entity, field)
11.183 + }
11.184 + return newValue
11.185 + }
11.186 +
11.187 + /**
11.188 + * @param entity
11.189 + * @param rightValues
11.190 + * @return {@code true}, if all entity fields is equals to {@code rightValues}
11.191 + */
11.192 + protected boolean checkEntityFieldValues(Object entity, Map<String, Object> rightValues) {
11.193 + def result = true
11.194 + def fields = getFields(entity)
11.195 + rightValues.each { key, value ->
11.196 + fields.find { it.name == key }.each {
11.197 + if(!getFieldValue(entity, it).equals(value)) {
11.198 + result = false
11.199 + }
11.200 + }
11.201 + }
11.202 + return result
11.203 + }
11.204 +
11.205 + /**
11.206 + * Set a {@code field} to a {@code value} of an entity {@code object}
11.207 + *
11.208 + * @param object
11.209 + * @param field
11.210 + * @param value
11.211 + */
11.212 + protected void setFieldValue(Object object, Field field, Object value) {
11.213 + boolean isAccessible = field.accessible
11.214 + field.accessible = true
11.215 + field.set(object, value)
11.216 + field.accessible = isAccessible
11.217 + }
11.218 +
11.219 + /**
11.220 + * Trying set a {@code field} to a {@code value} of an entity {@code object}
11.221 + * by using a setter method. If setter not found, field will be set directly
11.222 + * throw {@code setFieldValue} method.
11.223 + *
11.224 + * @param object
11.225 + * @param field
11.226 + * @param value
11.227 + */
11.228 + protected void setFieldValueBySetter(Object object, Field field, Object value) {
11.229 + boolean isSetted = false
11.230 + getMethods(object).grep {
11.231 + (
11.232 + Modifier.isPublic(it.modifiers)
11.233 + && it.parameterTypes.size() == 1
11.234 + && (
11.235 + value == null
11.236 + || it.parameterTypes[0].isAssignableFrom(value.class)
11.237 + )
11.238 + && it.name =~ /^set(?i:${field.name.charAt(0)})${field.name.replaceFirst("^.{1}", "")}/
11.239 + )
11.240 + }.each {
11.241 + object."${it.name}"(value)
11.242 + isSetted = true
11.243 + }
11.244 + if(isSetted)
11.245 + return
11.246 + setFieldValue(object, field, value)
11.247 + }
11.248 +
11.249 + /**
11.250 + * @param object
11.251 + * @param field
11.252 + * @return the field value of an object
11.253 + */
11.254 + protected Object getFieldValue(Object object, Field field) {
11.255 + boolean isAccessible = field.accessible
11.256 + field.accessible = true
11.257 + def value = field.get(object)
11.258 + field.accessible = isAccessible
11.259 + return value
11.260 + }
11.261 +
11.262 + /**
11.263 + * @param obj
11.264 + * @return all object fields, including inherited
11.265 + */
11.266 + protected Collection<Field> getFields(obj) {
11.267 + Class clazz = obj.getClass()
11.268 + Collection<Field> fields = clazz.declaredFields
11.269 + while(clazz.superclass != null) {
11.270 + clazz = clazz.superclass
11.271 + fields.addAll(clazz.declaredFields)
11.272 + }
11.273 + fields.grep {
11.274 + !it.synthetic &&
11.275 + !Modifier.isStatic(it.modifiers) &&
11.276 + !Modifier.isTransient(it.modifiers)
11.277 + }
11.278 + }
11.279 +
11.280 + /**
11.281 + * @param obj
11.282 + * @return all object methods, including inherited
11.283 + */
11.284 + protected Collection<Method> getMethods(obj) {
11.285 + Class clazz = obj.getClass()
11.286 + Collection<Method> methods = clazz.declaredMethods
11.287 + while(clazz.superclass != null) {
11.288 + clazz = clazz.superclass
11.289 + methods.addAll(clazz.declaredMethods)
11.290 + }
11.291 + methods.grep {
11.292 + !it.synthetic &&
11.293 + !Modifier.isStatic(it.modifiers) &&
11.294 + !Modifier.isTransient(it.modifiers)
11.295 + }
11.296 + }
11.297 +
11.298 + /**
11.299 + * For implement in successors
11.300 + *
11.301 + * @param entity
11.302 + */
11.303 + protected void testCreatedEntity(Object entity) {
11.304 +
11.305 + }
11.306 +
11.307 + /**
11.308 + * For implement in successors
11.309 + *
11.310 + * @param entity
11.311 + */
11.312 + protected void testUpdatedEntity(Object entity) {
11.313 +
11.314 + }
11.315 +
11.316 + /**
11.317 + * For implement in successors
11.318 + *
11.319 + * @param entity
11.320 + */
11.321 + protected void testRemovedEntity(Object entity) {
11.322 +
11.323 + }
11.324 +
11.325 + /**
11.326 + * @return an {@link AbstractEntity} successor
11.327 + */
11.328 + abstract protected Class getEntityClass()
11.329 +}
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/src/test/java/ru/indvdum/jpa/tests/AbstractJPATest.java Fri Nov 09 03:00:49 2012 +0400
12.3 @@ -0,0 +1,71 @@
12.4 +package ru.indvdum.jpa.tests;
12.5 +
12.6 +import java.util.ResourceBundle;
12.7 +
12.8 +import javax.naming.InitialContext;
12.9 +import javax.naming.NamingException;
12.10 +
12.11 +import org.apache.commons.dbcp.BasicDataSource;
12.12 +import org.junit.BeforeClass;
12.13 +
12.14 +import ru.indvdum.jpa.dao.PropertySelector;
12.15 +import ru.indvdum.jpa.props.Props;
12.16 +
12.17 +/**
12.18 + * @author indvdum (gotoindvdum[at]gmail[dot]com)
12.19 + * @since 22.12.2011 16:44:26
12.20 + *
12.21 + */
12.22 +public abstract class AbstractJPATest {
12.23 +
12.24 + @BeforeClass
12.25 + public static void setUpClass() throws Exception {
12.26 + if ("true".equalsIgnoreCase(System.getProperty("JPA.realDatabaseConnection"))) {
12.27 + initRealDatabase();
12.28 + } else {
12.29 + initDerby();
12.30 + }
12.31 + }
12.32 +
12.33 + protected static String dataSource = null;
12.34 +
12.35 + protected static String getDataSourceName() {
12.36 + if (dataSource != null)
12.37 + return dataSource;
12.38 +
12.39 + if (dataSource == null)
12.40 + dataSource = System.getProperty(Props.DATA_SOURCE_PROPERTY);
12.41 + if (dataSource == null)
12.42 + dataSource = ResourceBundle.getBundle(Props.JPADAO_PROPERTY_FILE).getString(Props.DATA_SOURCE_PROPERTY);
12.43 + if (dataSource == null)
12.44 + dataSource = "jdbc/database";
12.45 + return dataSource;
12.46 + }
12.47 +
12.48 + private static void initDerby() throws NamingException {
12.49 + System.setProperty("derby.stream.error.field", "java.lang.System.err");
12.50 +
12.51 + InitialContext context = new InitialContext();
12.52 + BasicDataSource datasource = (BasicDataSource) context.lookup(getDataSourceName());
12.53 +
12.54 + if (datasource == null) {
12.55 + datasource = new BasicDataSource();
12.56 + datasource.setUrl("jdbc:derby:memory:myDb;create=true");
12.57 + datasource.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
12.58 + datasource.setUsername("");
12.59 + datasource.setPassword("");
12.60 + datasource.setMaxActive(2);
12.61 +
12.62 + context.bind(getDataSourceName(), datasource);
12.63 + }
12.64 +
12.65 + PropertySelector.setSystemProperty(PropertySelector.RUNTIMEENHANCEMENT, "supported");
12.66 + PropertySelector.setSystemProperty(PropertySelector.SHOWSQL, "true");
12.67 + PropertySelector.setSystemProperty(PropertySelector.SYNCHRONIZEDB, "true");
12.68 + }
12.69 +
12.70 + private static void initRealDatabase() throws NamingException {
12.71 + // TODO: need realize
12.72 + }
12.73 +
12.74 +}