Moving reusable JPA DAO code into separate module
authorindvdum (gotoindvdum[at]gmail[dot]com)
Fri, 09 Nov 2012 03:00:49 +0400
changeset 2f692598b3742
parent 1 452e65d696a3
child 3 b400313e8aca
Moving reusable JPA DAO code into separate module
pom.xml
src/main/java/ru/indvdum/jpa/collections/EntityList.groovy
src/main/java/ru/indvdum/jpa/collections/TriggeredList.groovy
src/main/java/ru/indvdum/jpa/dao/JPADataAccessObject.groovy
src/main/java/ru/indvdum/jpa/dao/PropertySelector.groovy
src/main/java/ru/indvdum/jpa/entities/AbstractEntity.groovy
src/main/java/ru/indvdum/jpa/jndi/SimpleContext.java
src/main/java/ru/indvdum/jpa/jndi/SimpleContextFactory.java
src/main/java/ru/indvdum/jpa/props/Props.java
src/main/resources/jndi.properties
src/test/java/ru/indvdum/jpa/tests/AbstractJPAEntityTest.groovy
src/test/java/ru/indvdum/jpa/tests/AbstractJPATest.java
     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 +}