diff --git a/tawus-hibernate/build.gradle b/tawus-hibernate/build.gradle new file mode 100644 index 0000000..39fdeec --- /dev/null +++ b/tawus-hibernate/build.gradle @@ -0,0 +1,18 @@ + +sourceSets.main.java.srcDirs + +dependencies { + compile project(":tawus-core") + compile "org.hibernate:hibernate-core:3.6.0.Final" + compile "org.hibernate:hibernate-validator:4.1.0.Final" + testCompile "hsqldb:hsqldb:1.8.0.7" + testRuntime "cglib:cglib-nodep:2.2" + testCompile "org.spockframework:spock-tapestry:0.4-groovy-1.7" +} + +jar { + manifest { + attributes 'Tapestry-Module-Classes': 'com.googlecode.tawus.hibernate.services.TawusHibernateModule' + } +} + diff --git a/tawus-hibernate/src/main/java/META-INF/MANIFEST.MF b/tawus-hibernate/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5e94951 --- /dev/null +++ b/tawus-hibernate/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/InlineAudit.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/InlineAudit.java new file mode 100644 index 0000000..a91c5a8 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/InlineAudit.java @@ -0,0 +1,42 @@ +package com.googlecode.tawus.hibernate; + +import java.util.Date; + + +public interface InlineAudit { + /** + * Get login class which is stored in tapestry session + * @return login class + */ + Class getLoginClass(); + + /** + * Set creator + * @param currentLogin + */ + void setCreator(T currentLogin); + + /** + * Set creation date + * @param date + */ + void setCreationDate(Date date); + + /** + * Set modification date + * @param date + */ + void setModificationDate(Date date); + + /** + * Set modifier + * @param currentLogin + */ + void setModifier(T currentLogin); + + /** + * Has the record been modified + * @return + */ + boolean isModified(); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/TawusHibernateConstants.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/TawusHibernateConstants.java new file mode 100644 index 0000000..7f20ffe --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/TawusHibernateConstants.java @@ -0,0 +1,7 @@ +package com.googlecode.tawus.hibernate; + +public class TawusHibernateConstants { + + public static final String DEFAULT_FACTORY_ID = "tawus.hibernate.defaultFactoryID"; + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/DefaultOrder.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/DefaultOrder.java new file mode 100644 index 0000000..3ebe16c --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/DefaultOrder.java @@ -0,0 +1,18 @@ +package com.googlecode.tawus.hibernate.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface DefaultOrder { + + boolean ascending() default true; + + String column(); + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/Unique.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/Unique.java new file mode 100644 index 0000000..1614ed9 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/annotations/Unique.java @@ -0,0 +1,14 @@ +package com.googlecode.tawus.hibernate.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Unique { + String [] columns(); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/MandatoryTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/MandatoryTransactionAdvice.java new file mode 100644 index 0000000..7bb83d1 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/MandatoryTransactionAdvice.java @@ -0,0 +1,30 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class MandatoryTransactionAdvice implements + MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public MandatoryTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + if(!sessionManager.isWithinTransaction(factoryID)){ + throw new RuntimeException("Must be within a transaction"); + } + invocation.proceed(); + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NestedTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NestedTransactionAdvice.java new file mode 100644 index 0000000..828db68 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NestedTransactionAdvice.java @@ -0,0 +1,34 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class NestedTransactionAdvice implements MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public NestedTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + + try{ + sessionManager.begin(factoryID); + invocation.proceed(); + sessionManager.commit(factoryID); + }catch(Exception ex){ + sessionManager.rollback(factoryID); + throw new RuntimeException(ex); + } + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NeverTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NeverTransactionAdvice.java new file mode 100644 index 0000000..0921099 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NeverTransactionAdvice.java @@ -0,0 +1,29 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class NeverTransactionAdvice implements MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public NeverTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + if(sessionManager.isWithinTransaction(factoryID)){ + throw new RuntimeException("Cannot proceed within a transaction"); + } + invocation.proceed(); + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NotSupportedTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NotSupportedTransactionAdvice.java new file mode 100644 index 0000000..c742c80 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/NotSupportedTransactionAdvice.java @@ -0,0 +1,36 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; +import org.hibernate.Session; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class NotSupportedTransactionAdvice implements + MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public NotSupportedTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + if(sessionManager.isWithinTransaction()){ + Session oldSession = sessionManager.getSession(factoryID); + Session newSession = sessionManager.createSession(factoryID); + invocation.proceed(); + newSession.close(); + sessionManager.setSession(factoryID, oldSession); + }else{ + invocation.proceed(); + } + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiredTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiredTransactionAdvice.java new file mode 100644 index 0000000..6e98ddb --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiredTransactionAdvice.java @@ -0,0 +1,39 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class RequiredTransactionAdvice implements + MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public RequiredTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + boolean isNew = sessionManager.beginOrContinue(factoryID); + if(!isNew){ + invocation.proceed(); + return; + } + + try{ + invocation.proceed(); + sessionManager.commit(factoryID); + }catch(Exception ex){ + sessionManager.rollback(factoryID); + throw new RuntimeException(ex); + } + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiresNewTransactionAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiresNewTransactionAdvice.java new file mode 100644 index 0000000..47a067e --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/RequiresNewTransactionAdvice.java @@ -0,0 +1,56 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; +import org.hibernate.Session; +import org.hibernate.Transaction; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public class RequiresNewTransactionAdvice implements + MethodAdvice { + + private HibernateSessionManager sessionManager; + private String factoryID; + + public RequiresNewTransactionAdvice(String factoryID, + HibernateSessionManager sessionManager) { + this.sessionManager = sessionManager; + this.factoryID = factoryID; + } + + @Override + public void advise(Invocation invocation) { + if(sessionManager.isWithinTransaction()){ + Session oldSession = sessionManager.getSession(factoryID); + Session newSession = sessionManager.createSession(factoryID); + Transaction tx = null; + try{ + tx = newSession.beginTransaction(); + invocation.proceed(); + tx.commit(); + }catch(Exception ex){ + if(tx != null){ + tx.rollback(); + } + throw new RuntimeException(ex); + }finally { + newSession.close(); + sessionManager.setSession(factoryID, oldSession); + } + }else{ + + try{ + sessionManager.begin(factoryID); + invocation.proceed(); + sessionManager.commit(factoryID); + }catch(Exception ex){ + sessionManager.rollback(factoryID); + throw new RuntimeException(ex); + } + } + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/TransactionIsolationAdvice.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/TransactionIsolationAdvice.java new file mode 100644 index 0000000..15ca09d --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/advices/TransactionIsolationAdvice.java @@ -0,0 +1,46 @@ +/** + * + */ +package com.googlecode.tawus.hibernate.internal.advices; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.apache.tapestry5.ioc.Invocation; +import org.apache.tapestry5.ioc.MethodAdvice; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; + +public final class TransactionIsolationAdvice implements MethodAdvice { + + private String factoryID; + private HibernateSessionManager sessionManager; + private int isolation; + + public TransactionIsolationAdvice(final String factoryID, int isolation, + HibernateSessionManager sessionManager) { + this.factoryID = factoryID; + this.sessionManager = sessionManager; + this.isolation = isolation; + } + + @Override + @SuppressWarnings("deprecation") + public void advise(Invocation invocation) { + Connection connection = sessionManager.getSession(factoryID) + .connection(); + int oldIsolation = setIsolation(connection, isolation); + invocation.proceed(); + setIsolation(connection, oldIsolation); + } + + private int setIsolation(Connection connection, int isolation) { + try { + int oldIsolation = connection.getTransactionIsolation(); + connection.setTransactionIsolation(isolation); + return oldIsolation; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOImpl.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOImpl.java new file mode 100644 index 0000000..2deeb3c --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOImpl.java @@ -0,0 +1,221 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.io.Serializable; +import java.util.List; + +import org.apache.tapestry5.ioc.services.PropertyAccess; +import org.apache.tapestry5.ioc.services.TypeCoercer; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.criterion.Projections; +import org.hibernate.metadata.ClassMetadata; + +import com.googlecode.tawus.SearchCriteria; +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; +import com.googlecode.tawus.hibernate.services.SearchCriteriaConverter; +import com.googlecode.tawus.services.EntityDAO; + +/** + * Hibernate implementation of EnityDAO + */ +public class HibernateEntityDAOImpl implements EntityDAO { + private HibernateSessionManager sessionManager; + + private Class type; + + private TypeCoercer typeCoercer; + + private Class idType; + + private String idPropertyName; + + private PropertyAccess propertyAccess; + + private SearchCriteriaConverter converter; + + /** + * Constructor + * + * @param locator + * Hibernate session factory + * @param type + * entity type + */ + public HibernateEntityDAOImpl(HibernateSessionManager sessionManager, PropertyAccess propertyAccess, + TypeCoercer typeCoercer, SearchCriteriaConverter converter, Class type) { + this.sessionManager = sessionManager; + this.type = type; + this.typeCoercer = typeCoercer; + this.propertyAccess = propertyAccess; + this.converter = converter; + setIdentityAttributes(); + } + + HibernateEntityDAOImpl(HibernateSessionManager sessionManager, PropertyAccess propertyAccess, + TypeCoercer typeCoercer, Class type, Class idType, String idPropertyName) { + this.sessionManager = sessionManager; + this.type = type; + this.typeCoercer = typeCoercer; + this.propertyAccess = propertyAccess; + this.idType = idType; + this.idPropertyName = idPropertyName; + } + + /** + * Setup type and name of the entity id. + */ + private void setIdentityAttributes() { + ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(type); + if (classMetadata == null) { + throw new RuntimeException(type + " not found in hibernate"); + } + this.idType = (Class) classMetadata.getIdentifierType().getReturnedClass(); + this.idPropertyName = classMetadata.getIdentifierPropertyName(); + } + + /** + * Get current session + * + * @return session + */ + protected Session getSession() { + return sessionManager.getSession(type); + } + + /** + * {@inheritDocs} + */ + @Override + @SuppressWarnings("unchecked") + public T find(Serializable id) { + return (T) getSession().get(type, id); + } + + /** + * {@inheritDocs} + */ + @Override + public void save(T entity) { + getSession().save(entity); + } + + /** + * {@inheritDocs} + */ + @Override + public void update(T entity) { + getSession().update(entity); + } + + /** + * {@inheritDocs} + */ + @Override + public void saveOrUpdate(T entity) { + getSession().saveOrUpdate(entity); + } + + @Override + public void merge(T entity) { + getSession().merge(entity); + } + + /** + * {@inheritDoc} + */ + @Override + public int count(SearchCriteria sc) { + Criteria criteria = converter.toCriteria(sc, getSession(), false, false); + criteria.setProjection(Projections.rowCount()); + long size = (Long) criteria.uniqueResult(); + return (int) size; + } + + /** + * {@inheritDoc} + */ + @Override + public int count() { + Criteria criteria = getSession().createCriteria(type); + criteria.setProjection(Projections.rowCount()); + long size = (Long) criteria.uniqueResult(); + return (int) size; + } + + /** + * To flush the current session + */ + public void flush() { + getSession().flush(); + } + + /** + * To clear current session + */ + public void clear() { + getSession().clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(T entity) { + getSession().delete(entity); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public T find(SearchCriteria sc) { + Criteria criteria = converter.toCriteria(sc, getSession(), false, false); + return (T) criteria.setCacheable(true).setCacheRegion(sc.getType().getName()).uniqueResult(); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public List list(SearchCriteria sc) { + Criteria criteria = converter.toCriteria(sc, getSession(), true, true); + return criteria.setCacheable(true).setCacheRegion(sc.getType().getName()).list(); + } + + /** + * {@inheritDoc} + */ + @Override + public Class getType() { + return type; + } + + @Override + @SuppressWarnings("unchecked") + public List list() { + return getSession().createCriteria(type).list(); + } + + @Override + public Serializable getIdentifier(Object entity) { + return (Serializable) propertyAccess.getAdapter(type).getPropertyAdapter(idPropertyName).get( + entity); + } + + @Override + public void setIdentifier(T entity, Object value) { + propertyAccess.getAdapter(type).getPropertyAdapter(idPropertyName).set(entity, value); + } + + @Override + public String idString(T entity) { + return typeCoercer.coerce(getIdentifier(entity), String.class); + } + + @Override + public T get(String id) { + return find((Serializable) typeCoercer.coerce(id, idType)); + } +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOSource.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOSource.java new file mode 100644 index 0000000..07075cf --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOSource.java @@ -0,0 +1,32 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import org.apache.tapestry5.ioc.services.PropertyAccess; +import org.apache.tapestry5.ioc.services.TypeCoercer; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; +import com.googlecode.tawus.hibernate.services.SearchCriteriaConverter; +import com.googlecode.tawus.services.EntityDAO; +import com.googlecode.tawus.services.EntityDAOSource; + +public class HibernateEntityDAOSource implements EntityDAOSource { + private TypeCoercer typeCoercer; + private PropertyAccess propertyAccess; + private SearchCriteriaConverter converter; + private HibernateSessionManager sessionManager; + + public HibernateEntityDAOSource(HibernateSessionManager sessionManager, + PropertyAccess propertyAccess, TypeCoercer typeCoercer, SearchCriteriaConverter converter) { + this.sessionManager = sessionManager; + this.propertyAccess = propertyAccess; + this.typeCoercer = typeCoercer; + this.converter = converter; + } + + @Override + public EntityDAO get(Class entityClass) { + return new HibernateEntityDAOImpl(sessionManager, propertyAccess, typeCoercer, converter, + entityClass); + + } + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSearchCriteriaConverter.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSearchCriteriaConverter.java new file mode 100644 index 0000000..cad35e3 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSearchCriteriaConverter.java @@ -0,0 +1,303 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.hibernate.Criteria; +import org.hibernate.EntityMode; +import org.hibernate.Session; +import org.hibernate.criterion.Conjunction; +import org.hibernate.criterion.CriteriaSpecification; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Disjunction; +import org.hibernate.criterion.Order; +import org.hibernate.criterion.Restrictions; +import org.hibernate.criterion.SimpleExpression; +import org.hibernate.engine.SessionImplementor; +import org.hibernate.metadata.ClassMetadata; +import org.hibernate.type.Type; + +import com.googlecode.tawus.SearchCriteria; +import com.googlecode.tawus.hibernate.annotations.DefaultOrder; +import com.googlecode.tawus.hibernate.services.SearchCriteriaConverter; +import com.googlecode.tawus.search.Condition; +import com.googlecode.tawus.search.ConditionType; +import com.googlecode.tawus.search.ICondition; +import com.googlecode.tawus.search.SearchType; + +public class HibernateSearchCriteriaConverter implements SearchCriteriaConverter { + private static class Context { + private Map valueMap = new HashMap(); + private Map aliasMap = new HashMap(); + + public void setValue(String name, Object value){ + valueMap.put(name, value); + } + + public Map getValueMap(){ + return valueMap; + } + + public void setAlias(String name, String alias){ + aliasMap.put(name, alias); + } + + public Object getValue(String name){ + return valueMap.get(name); + } + + public String getAlias(String name){ + return aliasMap.get(name); + } + } + + public Criteria toCriteria(SearchCriteria sp, Session session, boolean sort, boolean paginate) { + final Criteria criteria = session.createCriteria(sp.getType()); + criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); + + //If paginate is specified, then paginate + if (paginate && sp.getFirstResult() >= 0 && sp.getMaxResults() > 0) { + criteria.setFirstResult(sp.getFirstResult()); + criteria.setMaxResults(sp.getMaxResults()); + } + + Context context = new Context(); + loadPropertyValues(sp.getEntity(), null, session, context); + + ICondition condition = sp.getCondition(); + if (condition == null) { + List conditions = new ArrayList(); + for (String propertyName : context.getValueMap().keySet()) { + conditions.add(Condition.eq(propertyName)); + } + condition = Condition.and(conditions.toArray(new ICondition[] {})); + } + + if (condition != null) { + Criterion c = applyCondition(criteria, condition, sp.getSearchType(), context); + if (c != null) { + criteria.add(c); + } + } + if (sort) { + Map sorting = sp.getOrder(); + if (sorting == null || sorting.size() == 0) { + DefaultOrder annotation = sp.getType().getAnnotation(DefaultOrder.class); + if (annotation != null) { + String colAlias = getPropertyName(criteria, annotation.column(), context); + criteria.addOrder(annotation.ascending() ? Order.asc(colAlias) + : Order.desc(colAlias)); + } + } else { + for (String col : sorting.keySet()) { + boolean order = sorting.get(col); + String colAlias = getPropertyName(criteria, col, context); + criteria.addOrder(order ? Order.asc(colAlias) : Order.desc(colAlias)); + } + } + } + return criteria; + } + + Criterion applyCondition(final Criteria criteria, final ICondition condition, SearchType searchType, + Context context) { + Criterion c = null; + if (condition.isJoin()) { + switch (condition.getType()) { + case AND: + c = Restrictions.conjunction(); + for (Object object : condition.getArgs()) { + Criterion child = applyCondition(criteria, (ICondition) object, searchType, context); + if (child != null) { + ((Conjunction) c).add(child); + } + } + break; + + case OR: + c = Restrictions.disjunction(); + for (Object object : condition.getArgs()) { + Criterion child = applyCondition(criteria, (ICondition) object, searchType, context); + if (child != null) { + ((Disjunction) c).add(child); + } + } + break; + + case NOT: + c = Restrictions.not(applyCondition(criteria, + (ICondition) condition.getArgs().get(0), searchType, context)); + break; + } + } else { + String firstArg = getPropertyName(criteria, (String) condition.getArgs().get(0), context); + int argCount = condition.getArgs().size(); + Object secondArg = null; + if (condition.getType() == ConditionType.NOT_NULL + || condition.getType() == ConditionType.IS_NULL) { + // do nothing + } else if (argCount == 1) { + secondArg = context.getValue((String) condition.getArgs().get(0)); + if (secondArg == null) { + return null; + } + } else { + if (condition.isProperty()) { + secondArg = getPropertyName(criteria, (String) condition.getArgs().get(1), context); + } else { + secondArg = condition.getArgs().get(1); + if (secondArg == null) { + return null; + } + } + } + + switch (condition.getType()) { + case EQ: + if ((argCount == 1) && (searchType == SearchType.Like) + && (secondArg instanceof String)) { + c = Restrictions.like(firstArg, "%" + secondArg + "%"); + } else { + c = argCount == 1 || !condition.isProperty() ? Restrictions.eq(firstArg, + secondArg) : Restrictions.eqProperty(firstArg, (String) secondArg); + } + break; + + case NE: + c = argCount == 1 || !condition.isProperty() ? Restrictions.ne(firstArg, secondArg) + : Restrictions.neProperty(firstArg, (String) secondArg); + break; + + case LE: + c = argCount == 1 || !condition.isProperty() ? Restrictions.le(firstArg, secondArg) + : Restrictions.leProperty(firstArg, (String) secondArg); + break; + + case LT: + c = argCount == 1 || !condition.isProperty() ? Restrictions.lt(firstArg, secondArg) + : Restrictions.ltProperty(firstArg, (String) secondArg); + break; + + case GE: + c = argCount == 1 || !condition.isProperty() ? Restrictions.ge(firstArg, secondArg) + : Restrictions.geProperty(firstArg, (String) secondArg); + break; + + case GT: + c = argCount == 1 || !condition.isProperty() ? Restrictions.gt(firstArg, secondArg) + : Restrictions.gtProperty(firstArg, (String) secondArg); + break; + + case LIKE: + c = Restrictions.like(firstArg, secondArg); + break; + + case NOT_LIKE: + c = Restrictions.not(Restrictions.like(firstArg, secondArg)); + break; + + case IS_NULL: + c = Restrictions.isNull(firstArg); + break; + + case NOT_NULL: + c = Restrictions.isNotNull(firstArg); + break; + } + if (c != null && SimpleExpression.class.isAssignableFrom(c.getClass())) { + if (secondArg instanceof String) { + c = ((SimpleExpression) c).ignoreCase(); + } + } + } + return c; + } + + String getPropertyName(final Criteria criteria, final String expr, Context context) { + if (!expr.contains(".")) { + return expr; + } + final String[] beans = expr.split("\\."); + String beanAlias = ""; + String beanName = ""; + for (int i = 0; i < beans.length - 1; ++i) { + if (beanAlias != "") { + beanAlias += "_" + beans[i]; + beanName += "." + beans[i]; + } else { + beanAlias = beans[i]; + beanName = beans[i]; + } + + if (context.getAlias(beanName) == null) { + context.setAlias(beanName, beanAlias); + criteria.createAlias(beanName, beanAlias, CriteriaSpecification.LEFT_JOIN); + } + } + return beanAlias + "." + beans[beans.length - 1]; + } + + void loadPropertyValues(Object object, String namePrefix, Session session, Context context) { + final ClassMetadata meta = getMetadata(object, session); + assert meta != null; + + final String[] propertyNames = meta.getPropertyNames(); + String idName = meta.getIdentifierPropertyName(); + if (idName != null) { + final Serializable id = meta.getIdentifier(object, (SessionImplementor) session); + if (id != null) { + if (namePrefix != null) { + idName = namePrefix + "." + idName; + } + context.setValue(idName, id); + if(namePrefix != null){ + return; //If it is a nested object and id is given, don't bother to add other properties + //of the nested object + } + } + } + + for (int i = 0; i < propertyNames.length; ++i) { + final Type type = meta.getPropertyType(propertyNames[i]); + final Object value = meta.getPropertyValue(object, propertyNames[i], EntityMode.POJO); + if (value == null) { + continue; + } + if (value instanceof Number && ((Number) value).longValue() == 0) { + continue; + } + + String name; + if (namePrefix == null) { + name = propertyNames[i]; + } else { + name = namePrefix + "." + propertyNames[i]; + } + + if (type.isCollectionType()) { + Collection val = (Collection) value; + if (val.size() == 1) { + Iterator iterator = val.iterator(); + Object obj = iterator.next(); + if (obj != null) { + loadPropertyValues(obj, name, session, context); + } + } + } else if (type.isEntityType()) { + loadPropertyValues(value, name, session, context); + } else { + context.setValue(name, value); + } + } + } + + //For testing purposes set the scope to package + ClassMetadata getMetadata(Object object, Session session) { + return session.getSessionFactory().getClassMetadata(object.getClass()); + } +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSessionManagerImpl.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSessionManagerImpl.java new file mode 100644 index 0000000..2c3efbf --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/HibernateSessionManagerImpl.java @@ -0,0 +1,148 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.ioc.services.ThreadCleanupListener; +import org.hibernate.Session; +import org.hibernate.Transaction; + +import com.googlecode.tawus.hibernate.TawusHibernateConstants; +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; +import com.googlecode.tawus.hibernate.services.SessionFactorySource; + +public class HibernateSessionManagerImpl implements HibernateSessionManager, + ThreadCleanupListener { + + private SessionFactorySource sessionFactorySource; + private String defaultFactoryID; + private Map sessions = new HashMap(); + private Map> transactions = new HashMap>(); + + public HibernateSessionManagerImpl( + @Symbol(TawusHibernateConstants.DEFAULT_FACTORY_ID) String defaultFactoryID, + SessionFactorySource sessionFactorySource) { + this.sessionFactorySource = sessionFactorySource; + this.defaultFactoryID = defaultFactoryID; + } + + public Session getSession(Class entityClass) { + return getSession(sessionFactorySource.getFactoryId(entityClass)); + } + + public Session getSession(String factoryID) { + factoryID = nvl(factoryID); + Session session = sessions.get(factoryID); + if( session == null){ + session = createSession(factoryID); + } + + return session; + } + + public Session getSession() { + return getSession(defaultFactoryID); + } + + public void threadDidCleanup() { + for(final Stack transactionStack: transactions.values()){ + while(!transactionStack.isEmpty()){ + transactionStack.pop().rollback(); + } + } + + for(final Session session: sessions.values()){ + session.close(); + } + } + + public void begin(String factoryID) { + factoryID = nvl(factoryID); + Transaction transaction = getSession(factoryID).beginTransaction(); + Stack transactionStack = transactions.get(factoryID); + if(transactionStack == null){ + transactionStack = new Stack(); + transactions.put(factoryID, transactionStack); + } + transactionStack.push(transaction); + } + + public void begin() { + begin(defaultFactoryID); + } + + public boolean beginOrContinue(String factoryID) { + factoryID = nvl(factoryID); + if(!isWithinTransaction(factoryID)){ + begin(factoryID); + return true; + } + return false; + } + + public boolean beginOrContinue(){ + return beginOrContinue(defaultFactoryID); + } + + public void commit(String factoryID) { + factoryID = nvl(factoryID); + if(!isWithinTransaction(factoryID)){ + throw new RuntimeException("Not within a transaction"); + } + transactions.get(factoryID).pop().commit(); + } + + public void commit(){ + commit(defaultFactoryID); + } + + public boolean isWithinTransaction(String factoryID){ + Stack transactionStack = transactions.get(factoryID); + return transactionStack != null && transactionStack.size() != 0; + } + + public boolean isWithinTransaction() { + return isWithinTransaction(defaultFactoryID); + } + + public void rollback(String factoryID) { + factoryID = nvl(factoryID); + if(isWithinTransaction(factoryID)){ + transactions.get(factoryID).pop().rollback(); + } + } + + private String nvl(String factoryID) { + return (factoryID == null || "".equals(factoryID)) ? defaultFactoryID : factoryID; + } + + public void rollback(){ + rollback(defaultFactoryID); + } + + public Transaction getCurrentTransaction(String factoryID){ + factoryID = nvl(factoryID); + if(!isWithinTransaction(factoryID)){ + throw new RuntimeException("Not within a transaction"); + } + return transactions.get(factoryID).peek(); + } + + public Session createSession(String factoryID) { + factoryID = nvl(factoryID); + final Session session = sessionFactorySource.createSession(factoryID); + sessions.put(factoryID, session); + return session; + } + + public Session createSession() { + return createSession(defaultFactoryID); + } + + public void setSession(String factoryID, Session session) { + sessions.put(nvl(factoryID), session); + } + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionFactorySourceImpl.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionFactorySourceImpl.java new file mode 100644 index 0000000..7c89a4b --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionFactorySourceImpl.java @@ -0,0 +1,136 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.tapestry5.ioc.services.ClassNameLocator; +import org.apache.tapestry5.ioc.services.RegistryShutdownListener; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +import com.googlecode.tawus.hibernate.services.SessionFactoryConfiguration; +import com.googlecode.tawus.hibernate.services.SessionFactorySource; + +/** + * Session Factory Source Implementation. + */ +public class SessionFactorySourceImpl implements SessionFactorySource, + RegistryShutdownListener { + private final Map factoryMap = new HashMap(); + private final Map, String> entityMap = new HashMap, String>(); + private final ClassNameLocator classNameLocator; + + /** + * Constructor + * + * @param configurations + * database configurations + */ + public SessionFactorySourceImpl(final ClassNameLocator classNameLocator, + final List configurations) { + this.classNameLocator = classNameLocator; + for(final SessionFactoryConfiguration configuration : configurations){ + setupDB(configuration); + } + } + + private void setupDB(SessionFactoryConfiguration configuration) { + setupSessionFactory(configuration); + } + + private void setupSessionFactory( + final SessionFactoryConfiguration configuration) { + final Configuration hibernateConfig = new Configuration(); + final List> entities = loadEntityClasses(configuration); + + // Load entity classes + for(final Class entityClass : entities){ + hibernateConfig.addAnnotatedClass(entityClass); + entityMap.put(entityClass, configuration.getFactoryId()); + } + + configuration.configure(hibernateConfig); + + final SessionFactory sf = hibernateConfig.buildSessionFactory(); + if(configuration.getFactoryId() != null){ + factoryMap.put(configuration.getFactoryId(), sf); + } + } + + private List> loadEntityClasses( + final SessionFactoryConfiguration configuration) { + final ClassLoader classLoader = Thread.currentThread() + .getContextClassLoader(); + + final List> entityClasses = new ArrayList>(); + + for(final String packageName : configuration.getPackageNames()){ + for(final String className : classNameLocator + .locateClassNames(packageName)){ + try{ + Class entityClass = null; + entityClass = classLoader.loadClass(className); + if(entityClass.getAnnotation(javax.persistence.Entity.class) != null || + entityClass.getAnnotation(javax.persistence.MappedSuperclass.class) != null){ + entityClasses.add(entityClass); + } + }catch(ClassNotFoundException e){ + throw new RuntimeException(e); + } + } + } + return entityClasses; + } + + public boolean hasSessionFactory(final Class entityClass){ + String factoryId = entityMap.get(entityClass); + if(factoryId == null){ + return false; + } + return hasSessionFactory(factoryId); + } + + public boolean hasSessionFactory(final String factoryId){ + return factoryMap.get(factoryId) != null; + } + + public SessionFactory getSessionFactory(final String factoryId) { + SessionFactory sf = factoryMap.get(factoryId); + if(sf == null){ + throw new RuntimeException("No session factory found for factoryId: " + + factoryId); + } + return sf; + } + + public SessionFactory getSessionFactory(final Class entityClass) { + SessionFactory sf = getSessionFactory(entityMap.get(entityClass)); + if(sf == null){ + throw new RuntimeException("No session factory found for entity: " + + entityClass); + } + return sf; + } + + public void registryDidShutdown() { + for(final SessionFactory sessionFactory : factoryMap.values()){ + sessionFactory.close(); + } + } + + public Session createSession(Class entityClass) { + return createSession(getFactoryId(entityClass)); + } + + public Session createSession(String factoryId) { + final Session session = getSessionFactory(factoryId).openSession(); + return session; + } + + public String getFactoryId(Class entityClass) { + return entityMap.get(entityClass); + } +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionShadowBuilderImpl.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionShadowBuilderImpl.java new file mode 100644 index 0000000..9090e76 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/SessionShadowBuilderImpl.java @@ -0,0 +1,71 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import org.apache.tapestry5.ioc.services.Builtin; +import org.apache.tapestry5.ioc.services.ClassFab; +import org.apache.tapestry5.ioc.services.ClassFactory; +import org.apache.tapestry5.ioc.services.MethodSignature; +import org.apache.tapestry5.ioc.util.BodyBuilder; +import org.hibernate.Session; + +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; +import com.googlecode.tawus.hibernate.services.SessionShadowBuilder; + +public class SessionShadowBuilderImpl implements SessionShadowBuilder { + private final ClassFactory classFactory; + + public SessionShadowBuilderImpl(@Builtin ClassFactory classFactory) { + this.classFactory = classFactory; + } + + public Session build(HibernateSessionManager sm, String sessionFactoryId) { + @SuppressWarnings("rawtypes") + Class sourceClass = sm.getClass(); + ClassFab cf = classFactory.newClass(Session.class); + + cf.addField("_source", Modifier.PRIVATE | Modifier.FINAL, sourceClass); + cf.addConstructor(new Class[] { sourceClass }, null, "_source = $1;"); + + BodyBuilder body = new BodyBuilder(); + body.begin(); + + body.addln("%s result = _source.getSession(\"%s\");", sourceClass.getName(), sessionFactoryId); + + body.addln("if (result == null)"); + body.begin(); + body.addln("throw new NullPointerException(%s.buildMessage(_source, \"getSession(%s)\"));", + getClass().getName(), sessionFactoryId); + body.end(); + body.addln("return result;"); + body.end(); + + MethodSignature sig = new MethodSignature(Session.class, "_delegate", null, null); + cf.addMethod(Modifier.PRIVATE, sig, body.toString()); + + String toString = String.format("", + sessionFactoryId); + + cf.proxyMethodsToDelegate(Session.class, "_delegate()", toString); + + @SuppressWarnings("rawtypes") + Class shadowClass = cf.createClass(); + try { + @SuppressWarnings("rawtypes") + Constructor cc = shadowClass.getConstructors()[0]; + Object instance = cc.newInstance(sm); + return (Session) instance; + } catch (Exception ex) { + // Should not be reachable + throw new RuntimeException(ex); + } + + } + + public static final String buildMessage(Object source, String propertyName) { + return String.format( + "Unable to delegate method invocation to property '%s' of %s, because the property is null.", + propertyName, source); + } +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/TransactionAdvisorImpl.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/TransactionAdvisorImpl.java new file mode 100644 index 0000000..4515746 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/internal/services/TransactionAdvisorImpl.java @@ -0,0 +1,101 @@ +package com.googlecode.tawus.hibernate.internal.services; + +import java.lang.reflect.Method; + +import org.apache.tapestry5.ioc.MethodAdviceReceiver; +import org.apache.tapestry5.ioc.annotations.Symbol; + +import com.googlecode.tawus.Propagation; +import com.googlecode.tawus.annotations.Transactional; +import com.googlecode.tawus.hibernate.TawusHibernateConstants; +import com.googlecode.tawus.hibernate.internal.advices.MandatoryTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.NestedTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.NeverTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.NotSupportedTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.RequiredTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.RequiresNewTransactionAdvice; +import com.googlecode.tawus.hibernate.internal.advices.TransactionIsolationAdvice; +import com.googlecode.tawus.hibernate.services.HibernateSessionManager; +import com.googlecode.tawus.hibernate.services.TransactionAdvisor; + +public class TransactionAdvisorImpl implements TransactionAdvisor { + + private HibernateSessionManager sessionManager; + private String defaultFactoryID; + + public TransactionAdvisorImpl(HibernateSessionManager sessionManager, + @Symbol(TawusHibernateConstants.DEFAULT_FACTORY_ID)String defaultFactoryID) { + this.sessionManager = sessionManager; + this.defaultFactoryID = defaultFactoryID; + } + + public void addTransactionAdvice( + final MethodAdviceReceiver receiver, String factoryID) { + if(factoryID == null){ + factoryID = defaultFactoryID; + } + + for (Method method : receiver.getInterface().getMethods()) { + Transactional transactional = method + .getAnnotation(Transactional.class); + + if (transactional != null) { + adviceMethod(transactional.propagation(), transactional.isolation(), factoryID, method, receiver); + } + } + } + + public void addTransactionAdvice(final MethodAdviceReceiver receiver) { + addTransactionAdvice(receiver, ""); + } + + private void adviceMethod(final Propagation propagation, int isolation, + String factoryID, + Method method, + MethodAdviceReceiver receiver) { + + switch (propagation) { + case REQUIRED: + receiver.adviseMethod(method, new RequiredTransactionAdvice(factoryID, + sessionManager)); + break; + + case MANDATORY: + receiver.adviseMethod(method, new MandatoryTransactionAdvice(factoryID, + sessionManager)); + break; + + case NESTED: + receiver.adviseMethod(method, new NestedTransactionAdvice(factoryID, + sessionManager)); + break; + + case NEVER: + receiver.adviseMethod(method, new NeverTransactionAdvice(factoryID, + sessionManager)); + break; + + case SUPPORTS: + break; + + case REQUIRES_NEW: + receiver.adviseMethod(method, new RequiresNewTransactionAdvice( + factoryID, sessionManager)); + break; + + case NOT_SUPPORTED: + receiver.adviseMethod(method, new NotSupportedTransactionAdvice( + factoryID, sessionManager)); + break; + } + + if (isolation != 0) { + receiver.adviseMethod(method, + new TransactionIsolationAdvice(factoryID, + isolation, sessionManager)); + } + + } + + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/HibernateSessionManager.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/HibernateSessionManager.java new file mode 100644 index 0000000..b5b1785 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/HibernateSessionManager.java @@ -0,0 +1,37 @@ +package com.googlecode.tawus.hibernate.services; + +import org.hibernate.Session; + +public interface HibernateSessionManager { + + public Session getSession(Class entityClass); + + public Session getSession(String factoryID); + + public Session getSession(); + + public void rollback(); + + public void rollback(String factoryID); + + public void commit(); + + public void commit(String factoryID); + + public boolean isWithinTransaction(); + + public boolean isWithinTransaction(String factoryID); + + public void begin(); + + public void begin(String factoryID); + + public boolean beginOrContinue(); + + public boolean beginOrContinue(String factoryID); + + public Session createSession(String factoryID); + + public void setSession(String factoryID, Session oldSession); + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SearchCriteriaConverter.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SearchCriteriaConverter.java new file mode 100644 index 0000000..af32ec0 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SearchCriteriaConverter.java @@ -0,0 +1,10 @@ +package com.googlecode.tawus.hibernate.services; + +import org.hibernate.Criteria; +import org.hibernate.Session; + +import com.googlecode.tawus.SearchCriteria; + +public interface SearchCriteriaConverter { + Criteria toCriteria(SearchCriteria searchCriteria, Session session, boolean sort, boolean paginate); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactoryConfiguration.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactoryConfiguration.java new file mode 100644 index 0000000..17bab50 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactoryConfiguration.java @@ -0,0 +1,30 @@ +package com.googlecode.tawus.hibernate.services; + +import org.hibernate.cfg.Configuration; + +/** + * Database Configuration. Each database to be used has to be configured + * using this configuration. + */ +public interface SessionFactoryConfiguration { + /** + * List of package names containing the model objects + * + * @return list of package names + */ + String[] getPackageNames(); + + /** + * String marker to identify DAOs of this database + * + * @return factory id + */ + String getFactoryId(); + + /** + * Configure hibernate configuration + * + * @param configuration + */ + void configure(Configuration configuration); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactorySource.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactorySource.java new file mode 100644 index 0000000..86b1d2b --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionFactorySource.java @@ -0,0 +1,33 @@ +package com.googlecode.tawus.hibernate.services; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +/** + * Source for session factory + */ +public interface SessionFactorySource { + /** + * Get session factory for a given string marker + * @param factoryID string marker + * @return session factory + */ + public SessionFactory getSessionFactory(String factoryID); + + /** + * Get session factory for a given annotation marker + * @param entityClass marker annotation + * @return session factory + */ + public SessionFactory getSessionFactory(Class entityClass); + + public Session createSession(Class entityClass); + + public Session createSession(String factoryID); + + public String getFactoryId(Class entityClass); + + public boolean hasSessionFactory(String factoryId); + + public boolean hasSessionFactory(Class entityClass); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionShadowBuilder.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionShadowBuilder.java new file mode 100644 index 0000000..ac8d937 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/SessionShadowBuilder.java @@ -0,0 +1,7 @@ +package com.googlecode.tawus.hibernate.services; + +import org.hibernate.Session; + +public interface SessionShadowBuilder { + Session build(HibernateSessionManager sm, String factoryId); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TawusHibernateModule.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TawusHibernateModule.java new file mode 100644 index 0000000..fc9b542 --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TawusHibernateModule.java @@ -0,0 +1,77 @@ +package com.googlecode.tawus.hibernate.services; + +import java.util.List; + +import org.apache.tapestry5.ioc.MappedConfiguration; +import org.apache.tapestry5.ioc.OrderedConfiguration; +import org.apache.tapestry5.ioc.ScopeConstants; +import org.apache.tapestry5.ioc.ServiceBinder; +import org.apache.tapestry5.ioc.annotations.Primary; +import org.apache.tapestry5.ioc.annotations.Scope; +import org.apache.tapestry5.ioc.annotations.ServiceId; +import org.apache.tapestry5.ioc.annotations.SubModule; +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.ioc.services.ClassNameLocator; +import org.apache.tapestry5.ioc.services.PerthreadManager; +import org.apache.tapestry5.ioc.services.PropertyAccess; +import org.apache.tapestry5.ioc.services.RegistryShutdownHub; +import org.hibernate.Session; + +import com.googlecode.tawus.hibernate.TawusHibernateConstants; +import com.googlecode.tawus.hibernate.internal.services.HibernateEntityDAOSource; +import com.googlecode.tawus.hibernate.internal.services.HibernateSearchCriteriaConverter; +import com.googlecode.tawus.hibernate.internal.services.HibernateSessionManagerImpl; +import com.googlecode.tawus.hibernate.internal.services.SessionFactorySourceImpl; +import com.googlecode.tawus.hibernate.internal.services.SessionShadowBuilderImpl; +import com.googlecode.tawus.hibernate.internal.services.TransactionAdvisorImpl; +import com.googlecode.tawus.services.EntityDAOSource; +import com.googlecode.tawus.services.EntityValidator; +import com.googlecode.tawus.services.TawusModule; + +@SubModule(TawusModule.class) +public class TawusHibernateModule { + public static void bind(ServiceBinder binder) { + binder.bind(TransactionAdvisor.class, TransactionAdvisorImpl.class); + binder.bind(SessionShadowBuilder.class, SessionShadowBuilderImpl.class); + binder.bind(SearchCriteriaConverter.class, HibernateSearchCriteriaConverter.class); + } + + public void contributeEntityDAOSource(OrderedConfiguration daoSources) { + daoSources.addInstance("hibernateSource", HibernateEntityDAOSource.class); + } + + public static void contributeFactoryDefaults(MappedConfiguration defaults) { + defaults.add(TawusHibernateConstants.DEFAULT_FACTORY_ID, "default"); + } + + @Scope(value = ScopeConstants.PERTHREAD) + public HibernateSessionManager buildHibernateSessionManager( + @Symbol(TawusHibernateConstants.DEFAULT_FACTORY_ID) String defaultFactoryID, + SessionFactorySource sessionFactorySource, PerthreadManager threadManager) { + HibernateSessionManagerImpl sessionManager = new HibernateSessionManagerImpl( + defaultFactoryID, sessionFactorySource); + threadManager.addThreadCleanupListener(sessionManager); + return sessionManager; + } + + @ServiceId("default") + @Primary + public Session buildDefaultSession(SessionShadowBuilder sessionShadowBuilder, + HibernateSessionManager sessionManager) { + return sessionShadowBuilder.build(sessionManager, "default"); + } + + public SessionFactorySource buildSessionFactorySource(ClassNameLocator classNameLocator, + List configuration, RegistryShutdownHub hub) { + SessionFactorySourceImpl sessionFactorySource = new SessionFactorySourceImpl( + classNameLocator, configuration); + hub.addRegistryShutdownListener(sessionFactorySource); + return sessionFactorySource; + } + + public void contributeEntityValidator(HibernateSessionManager sessionManager, + PropertyAccess propertyAccess, OrderedConfiguration contribution) { + contribution.add("unique", new UniqueEntityValidator(sessionManager, propertyAccess)); + } + +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionAdvisor.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionAdvisor.java new file mode 100644 index 0000000..ace34ee --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionAdvisor.java @@ -0,0 +1,21 @@ +package com.googlecode.tawus.hibernate.services; + +import org.apache.tapestry5.ioc.MethodAdviceReceiver; + +/** + * Transaction Advisor + * @author Taha Hafeez + * + */ +public interface TransactionAdvisor { + /** + * Add Transaction advice to methods with + * {@link com.googlecode.tawus.annotations.Transactional} annotation + * + * @param methodAdviceReceiver + */ + void addTransactionAdvice(MethodAdviceReceiver methodAdviceReceiver); + + void addTransactionAdvice(final MethodAdviceReceiver receiver, + String factoryID); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionalSupportTransformer.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionalSupportTransformer.java new file mode 100644 index 0000000..df7cd6f --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/TransactionalSupportTransformer.java @@ -0,0 +1,19 @@ +package com.googlecode.tawus.hibernate.services; + +/** + * Adds transactional support to a service + * + * @author Taha Hafeez + * + */ +public interface TransactionalSupportTransformer { + /** + * Transforms a class by adding support for + * {@link com.googlecode.tawus.hibernate.annotation.Transactional} + * + * @param + * @param clazz + * @return + */ + public T transform(T clazz); +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/UniqueEntityValidator.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/UniqueEntityValidator.java new file mode 100644 index 0000000..d545aee --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/services/UniqueEntityValidator.java @@ -0,0 +1,61 @@ +package com.googlecode.tawus.hibernate.services; + +import org.apache.tapestry5.ValidationException; +import org.apache.tapestry5.ioc.services.ClassPropertyAdapter; +import org.apache.tapestry5.ioc.services.PropertyAccess; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; + +import com.googlecode.tawus.hibernate.annotations.Unique; +import com.googlecode.tawus.services.EntityValidator; + +public class UniqueEntityValidator implements EntityValidator { + + private HibernateSessionManager sessionManager; + private PropertyAccess propertyAccess; + + public UniqueEntityValidator(HibernateSessionManager sessionManager, + PropertyAccess propertyAccess) { + this.sessionManager = sessionManager; + this.propertyAccess = propertyAccess; + } + + @Override + public void validate(Object entity) throws ValidationException { + if (entity == null) { + return; + } + Unique unique = entity.getClass().getAnnotation(Unique.class); + if (unique == null) { + return; + } + + @SuppressWarnings("rawtypes") + Class clazz = entity.getClass(); + Session session = sessionManager.getSession(clazz); + ClassPropertyAdapter classAdapter = propertyAccess.getAdapter(clazz); + + for (String column : unique.columns()) { + if (column != null) { + Criteria criteria = session.createCriteria(clazz); + for (String c : column.split(",")) { + String col = c.trim(); + + criteria.add(Restrictions.eq(col, classAdapter.get(entity, col))); + } + if (classAdapter.get(entity, "id") != null){ + criteria.add(Restrictions.not(Restrictions.idEq(classAdapter.get(entity, "id")))); + } + criteria.setProjection(Projections.rowCount()); + Long rowCount = (Long) criteria.uniqueResult(); + if (rowCount > 0L) { + throw new ValidationException("Column(s) '" + column + + "' must be unique"); + } + } + } + + } +} diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/MD5SumType.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/MD5SumType.java new file mode 100644 index 0000000..a865cdf --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/MD5SumType.java @@ -0,0 +1,110 @@ +package com.googlecode.tawus.hibernate.utils; + +import java.io.Serializable; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.usertype.UserType; + +public class MD5SumType implements UserType { + @Override + public int [] sqlTypes(){ + return new int []{ Types.VARCHAR }; + } + + @Override + @SuppressWarnings("rawtypes") + public Class returnedClass(){ + return String.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return (x == y) || (x != null && y != null && x.equals(y)); + } + + @Override + @SuppressWarnings("deprecation") + public Object nullSafeGet(final ResultSet rs, final String [] names, final Object owner) throws + HibernateException, SQLException { + final String val = (String)Hibernate.STRING.nullSafeGet(rs, names[0]); + return val == null ? null: val.trim(); + } + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException{ + return original; + } + + @Override + @SuppressWarnings("deprecation") + public void nullSafeSet(final PreparedStatement ps, Object value, int index) + throws HibernateException, SQLException { + String password = null; + if(value != null){ + password = encrypt((String)value); + } + Hibernate.STRING.nullSafeSet(ps, password, index); + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + if(value == null){ + return null; + } + + return new String((String)value); + } + + @Override + public boolean isMutable(){ + return true; + } + + @Override + public Serializable disassemble(Object value) throws HibernateException{ + return (Serializable)value; + } + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException{ + return cached; + } + + @Override + public int hashCode(Object x) throws HibernateException{ + return x.hashCode(); + } + + private String encrypt(String plainText) throws HibernateException { + if(plainText.length() == 32){ + return plainText; + } + + try { + final MessageDigest md = MessageDigest.getInstance("MD5"); + md.reset(); + String clearPassword = plainText; + md.update(clearPassword.getBytes("UTF-8")); + BigInteger hash = new BigInteger(1, md.digest()); + return pad(hash.toString(16),32,'0'); + }catch(Exception ex){ + throw new HibernateException(ex.getMessage(), ex); + } + } + + private String pad(String s, int length, char pad) { + StringBuffer buffer = new StringBuffer(s); + while (buffer.length() < length) { + buffer.insert(0, pad); + } + return buffer.toString(); + } +} + diff --git a/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/NewAndImprovedNamingStrategy.java b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/NewAndImprovedNamingStrategy.java new file mode 100755 index 0000000..6e2b85b --- /dev/null +++ b/tawus-hibernate/src/main/java/com/googlecode/tawus/hibernate/utils/NewAndImprovedNamingStrategy.java @@ -0,0 +1,15 @@ +package com.googlecode.tawus.hibernate.utils; + +import org.hibernate.cfg.ImprovedNamingStrategy; + +@SuppressWarnings("serial") +public class NewAndImprovedNamingStrategy extends ImprovedNamingStrategy { + @Override + public String foreignKeyColumnName(String propertyName, + String propertyEntityName, String propertyTableName, + String referencedColumnName) { + String s = super.foreignKeyColumnName(propertyName, propertyEntityName, + propertyTableName, referencedColumnName); + return s.endsWith("_id") ? s : s + "_id"; + } +} diff --git a/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOTest.groovy b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOTest.groovy new file mode 100644 index 0000000..54aef63 --- /dev/null +++ b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/HibernateEntityDAOTest.groovy @@ -0,0 +1,92 @@ + +package com.googlecode.tawus.hibernate.internal.services + +import org.apache.tapestry5.ioc.services.PropertyAccess +import org.apache.tapestry5.ioc.services.TypeCoercer +import org.hibernate.Session + +import spock.lang.Shared +import spock.lang.Specification + +import com.googlecode.tawus.hibernate.internal.services.HibernateEntityDAOImpl +import com.googlecode.tawus.hibernate.models.User + +class HibernateEntityDAOTest extends Specification { + @Shared dao + @Shared session + @Shared propertyAccess + @Shared typeCoercer + @Shared user + + static class DAO extends HibernateEntityDAOImpl { + def session; + + DAO(Session session, PropertyAccess propertyAccess, TypeCoercer typeCoercer, Class type, Class idType, String idName){ + super(null, propertyAccess, typeCoercer, type, idType, idName); + this.session = session; + } + + @Override + public Session getSession(){ + return session; + } + } + + def setup(){ + session = Mock(Session) + propertyAccess = Mock(PropertyAccess) + typeCoercer = Mock(TypeCoercer) + + dao = new DAO(session, propertyAccess, typeCoercer, User.class, Long.class, "id"); + user = new User() + user.id = 1 + user.userName = "someone" + } + + def "getSession works"(){ + expect: dao.getSession() != null + } + + def "find works"(){ + when: + dao.find(1) == user + then: + 1 * session.get(User.class, 1) >> user + } + + def "save works"(){ + when: dao.save(user) + then: 1 * session.save(user) + } + + def "update works"(){ + when: dao.update(user) + then: 1 * session.update(user) + } + + def "saveOrUpdate works"(){ + when: dao.saveOrUpdate(user) + then: 1 * session.saveOrUpdate(user) + } + + def "merge works"(){ + when:dao.merge(user) + then:1 * session.merge(user) + } + + def "flush() works"(){ + when:dao.flush() + then: 1 * session.flush() + } + + def "remove() works"(){ + when:dao.remove(user) + then: 1 * session.delete(user) + } + + def "clear() works"(){ + when:dao.clear() + then: 1 * session.clear() + } + +} diff --git a/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/SearchCriteriaConverterTest.groovy b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/SearchCriteriaConverterTest.groovy new file mode 100644 index 0000000..09d0415 --- /dev/null +++ b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/internal/services/SearchCriteriaConverterTest.groovy @@ -0,0 +1,107 @@ +package com.googlecode.tawus.hibernate.internal.services + +import org.apache.tapestry5.ioc.RegistryBuilder +import org.apache.tapestry5.ioc.annotations.Inject +import org.apache.tapestry5.ioc.annotations.SubModule +import org.apache.tapestry5.services.TapestryModule +import org.hibernate.Criteria +import org.hibernate.Session + +import spock.lang.Shared +import spock.lang.Specification + +import com.googlecode.tawus.hibernate.internal.services.HibernateSearchCriteriaConverter.Context +import com.googlecode.tawus.hibernate.models.Address +import com.googlecode.tawus.hibernate.models.Gender +import com.googlecode.tawus.hibernate.models.User +import com.googlecode.tawus.hibernate.services.SessionFactorySource +import com.googlecode.tawus.hibernate.services.TestModule + +@SubModule([]) +class SearchCriteriaConverterTest extends Specification { + @Shared converter + @Shared context; + + @Inject + Session session; + + @Inject + SessionFactorySource source + + def beforeRegistryCreated(){ + RegistryBuilder builder = new RegistryBuilder(); + builder.add(TapestryModule.class, TestModule.class); + return builder.build(); + } + + def setup(){ + session = source.createSession("default") + converter = new HibernateSearchCriteriaConverter() + context = new Context(); + } + + def "check loadPropertyValues with empty object"(){ + setup: + User user = new User(); + expect:source.hasSessionFactory(User.class) == true + when: + converter.loadPropertyValues(user, null, session, context); + then: + context.getValueMap().size() == 0 + } + + def "check loadPropertyValues() with filled object"(){ + setup: + User user = new User(); + user.userName = "tawus" + user.email = "tawushafeez@gmail.com" + user.gender = Gender.Male + user.id = 1L + when: + converter.loadPropertyValues(user, null, session, context); + then: + context.getValueMap().size() == 4 + context.getValue("userName").equals(user.userName) + context.getValue("email").equals(user.email) + context.getValue("gender").equals(user.gender) + context.getValue("id").equals(user.id) + } + + def "check loadPropertyValues() with nested objects"(){ + setup: + User user = new User(); + Address address = new Address(); + address.city = "Srinagar" + address.country = "Kashmir" + address.id = 1L + user.address = address + when: + converter.loadPropertyValues(user, null, session, context); + then: + context.getValueMap().size() == 1 + context.getValue("address.id").equals(address.id) + context.getValueMap().containsKey("address.country") == false + context.getValue("address.id").equals(address.id) + } + + def "check getPropertyName for alias creation"(){ + setup: + User user = new User() + Address address = new Address(); + address.city = "Srinagar" + address.country = "Kashmir" + address.id = 1L + user.address = address + Criteria criteria = Mock(Criteria) + when: + def value = converter.getPropertyName(criteria, "address.city", context) + then: + value == "address.city" + + context.getAlias("address") == "address" + } + + def cleanup(){ + session.close(); + } +} diff --git a/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/HibernateEntityDAOIntegrationTest.groovy b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/HibernateEntityDAOIntegrationTest.groovy new file mode 100644 index 0000000..6a0fffb --- /dev/null +++ b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/HibernateEntityDAOIntegrationTest.groovy @@ -0,0 +1,82 @@ +package com.googlecode.tawus.hibernate.services + +import javax.persistence.Entity + +import org.apache.tapestry5.annotations.Service +import org.apache.tapestry5.ioc.RegistryBuilder +import org.apache.tapestry5.ioc.annotations.Inject +import org.apache.tapestry5.ioc.annotations.SubModule +import org.apache.tapestry5.ioc.internal.util.CollectionFactory +import org.apache.tapestry5.services.TapestryModule + +import spock.lang.Specification + +import com.googlecode.tawus.SearchCriteria +import com.googlecode.tawus.hibernate.models.Gender +import com.googlecode.tawus.hibernate.models.User +import com.googlecode.tawus.internal.AbstractEntityLocator +import com.googlecode.tawus.internal.def.EntityModuleDef +import com.googlecode.tawus.services.EntityDAO +import com.googlecode.tawus.services.EntityLocator + +@SubModule([]) +class HibernateEntityDAOIntegrationTest extends Specification { + @Service("UserDAO") + @Inject + private EntityDAO userDAO + + def beforeRegistryCreated(){ + RegistryBuilder builder = new RegistryBuilder(); + EntityLocator entityLocator = new AbstractEntityLocator(CollectionFactory.newSet( + "com.googlecode.tawus.hibernate.models")){ + public boolean isEntity(@SuppressWarnings("rawtypes") Class entityType){ + System.out.println(entityType) + return entityType.getAnnotation(Entity.class) != null; + } + }; + + builder.add(TapestryModule.class, TestModule.class); + builder.add(new EntityModuleDef(entityLocator)) + return builder.build(); + } + + def "test operations"(){ + User user = new User(); + user.userName = "tawus" + user.email = "tawushafeez@gmail.com" + user.id = 1L + user.gender = Gender.Male + + expect: userDAO.list().size() == 0 + when: "Check saving entity" + userDAO.save user + then: + userDAO.list().size() == 1 + userDAO.find(user.id).equals(user) + userDAO.getIdentifier(user) == user.id + + when: + def criteria = new SearchCriteria(User.class) + criteria.getEntity().setUserName("tawus") + then: + userDAO.list(criteria).size() == 1 + userDAO.count(criteria) == 1 + userDAO.list().size() == 1 + userDAO.count() == 1 + + when: + criteria = new SearchCriteria(User.class) + criteria.getEntity().setUserName("taha") + then: + userDAO.list(criteria).size() == 0 + userDAO.count(criteria) == 0 + userDAO.list().size() == 1 + userDAO.count() == 1 + + when: "Check removing entity" + userDAO.remove(user) + then: + userDAO.list().size() == 0 + + } +} diff --git a/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/SessionFactorySourceTest.groovy b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/SessionFactorySourceTest.groovy new file mode 100644 index 0000000..0015ae7 --- /dev/null +++ b/tawus-hibernate/src/test/groovy/com/googlecode/tawus/hibernate/services/SessionFactorySourceTest.groovy @@ -0,0 +1,66 @@ +package com.googlecode.tawus.hibernate.services + +import java.util.Properties + +import org.apache.tapestry5.ioc.services.ClassNameLocator +import org.hibernate.cfg.Configuration + +import spock.lang.Specification + +import com.googlecode.tawus.hibernate.internal.services.SessionFactorySourceImpl +import com.googlecode.tawus.hibernate.models.Gender +import com.googlecode.tawus.hibernate.models.User + + +/** + * This test is for SessionFactorySource + */ +class SessionFactorySourceTest extends Specification { + + def "check configuration is used"(){ + setup: + ClassNameLocator classNameLocator = Mock(ClassNameLocator) + SessionFactoryConfiguration configuration =new SessionFactoryConfiguration(){ + + public String [] getPackageNames(){ + return ["com.googlecode.tawus.hibernate.models"]; + } + + public String getFactoryId(){ + return "default"; + } + + public void configure(Configuration configuration){ + Properties prop = new Properties(); + prop.put("hibernate.dialect", + "org.hibernate.dialect.HSQLDialect"); + prop.put("hibernate.connection.driver_class", + "org.hsqldb.jdbcDriver"); + prop + .put("hibernate.connection.url", + "jdbc:hsqldb:mem:testdb"); + prop.put("hibernate.connection.username", "sa"); + prop.put("hibernate.connection.password", ""); + prop.put("hibernate.connection.pool_size", "1"); + prop.put("hibernate.connection.autocommit", "false"); + prop.put("hibernate.hbm2ddl.auto", "create-drop"); + prop.put("hibernate.show_sql", "true"); + prop.put("hibernate.current_session_context_class", "thread"); + configuration.addProperties(prop); + } + }; + List configurations = new ArrayList() + configurations.add(configuration); + when: + SessionFactorySource source = new SessionFactorySourceImpl(classNameLocator, configurations); + then: + 1 * classNameLocator.locateClassNames("com.googlecode.tawus.hibernate.models") >> + ["com.googlecode.tawus.hibernate.models.User", "com.googlecode.tawus.hibernate.models.Address", + "com.googlecode.tawus.hibernate.models.UserGroup", + "com.googlecode.tawus.hibernate.models.Gender" + ] + source.getFactoryId(User.class).equals("default"); + source.hasSessionFactory(User.class) == true + source.hasSessionFactory(Gender.class) == false + } +} diff --git a/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Address.java b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Address.java new file mode 100644 index 0000000..87c13f7 --- /dev/null +++ b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Address.java @@ -0,0 +1,113 @@ +package com.googlecode.tawus.hibernate.models; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; + + +@Entity +public class Address implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + private Long id; + + @NotNull + private String street; + @NotNull + private String city; + @NotNull + private String state; + @NotNull + private String country; + + @ManyToOne + private User user; + + public Address(){} + + public Address(String street, String city, String state, String country){ + setStreet(street); + setCity(city); + setState(state); + setCountry(country); + } + + public String getStreet(){ + return street; + } + + public void setStreet(String street){ + this.street = street; + } + + public String getCity(){ + return city; + } + + public void setCity(String city){ + this.city = city; + } + + public String getState(){ + return state; + } + + public void setState(String state){ + this.state = state; + } + + public String getCountry(){ + return country; + } + + public void setCountry(String country){ + this.country = country; + } + + public User getUser(){ + return user; + } + + public void setUser(User user){ + this.user = user; + } + + public String toString(){ + return street + ", " + state + ", " + country; + } + + public Long getId(){ + return id; + } + + public void setId(Long id){ + this.id = id; + } + + @Override + public boolean equals(Object o){ + if(o == null || !(o instanceof User)){ + return false; + } + Address a = (Address)o; + if(getId() != null && getId().equals(a.getId())){ + return true; + } + return false; + } + + @Override + public int hashCode(){ + if(getId() == null){ + return super.hashCode(); + } + return getId().hashCode(); + } +} + diff --git a/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Gender.java b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Gender.java new file mode 100644 index 0000000..238cc55 --- /dev/null +++ b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/Gender.java @@ -0,0 +1,6 @@ +package com.googlecode.tawus.hibernate.models; + +public enum Gender { + Male, Female +} + diff --git a/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/User.java b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/User.java new file mode 100644 index 0000000..5a9673a --- /dev/null +++ b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/User.java @@ -0,0 +1,137 @@ +package com.googlecode.tawus.hibernate.models; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.googlecode.tawus.annotations.NonUpdatable; +import com.googlecode.tawus.hibernate.annotations.Unique; + +@Entity +@Unique(columns = "userName") +public class User implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + private Long id; + + @NotNull + @Size(max = 10) + private String userName; + + private String password; + + @NotNull + @Size(max = 64) + private String email; + + private String signature; + + @NotNull + @NonUpdatable + private Gender gender; + + @ManyToOne + private Address address; + + @ManyToOne + private UserGroup userGroup; + + public User(){} + + public Long getId(){ + return id; + } + + public void setId(Long id){ + this.id = id; + } + + public String getUserName(){ + return userName; + } + + public void setUserName(String userName){ + this.userName = userName; + } + + public String getPassword(){ + return password; + } + + public void setPassword(String password){ + this.password = password; + } + + public String getEmail(){ + return email; + } + + public void setEmail(String email){ + this.email = email; + } + + public String getSignature(){ + return signature; + } + + public void setSignature(String signature){ + this.signature = signature; + } + + public Gender getGender(){ + return gender; + } + + public void setGender(Gender gender){ + this.gender = gender; + } + + @Override + public boolean equals(Object o){ + if(o == null || !(o instanceof User)){ + return false; + } + User u = (User)o; + if(getId() != null){ + return getId().equals(u.getId()); + }else { + return super.equals(o); + } + } + + @Override + public int hashCode(){ + if(getId() == null){ + return super.hashCode(); + } + return getId().hashCode(); + } + + public String toString(){ + return userName + "(" + signature + ")"; + } + + public void setAddress(Address address) { + this.address = address; + } + + public Address getAddress() { + return address; + } + + public void setGroup(UserGroup userGroup) { + this.userGroup = userGroup; + } + + public UserGroup getGroup() { + return userGroup; + } +} + diff --git a/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/UserGroup.java b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/UserGroup.java new file mode 100644 index 0000000..2c687a3 --- /dev/null +++ b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/models/UserGroup.java @@ -0,0 +1,58 @@ +package com.googlecode.tawus.hibernate.models; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Entity +public class UserGroup implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + private Long id; + + @NotNull + private String name; + + @NotNull + @OneToMany(mappedBy = "userGroup") + @Size(max=1) + @Valid + private List users = new ArrayList(); + + public void setUsers(List users) { + this.users = users; + } + + public List getUsers() { + return users; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + +} diff --git a/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/services/TestModule.java b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/services/TestModule.java new file mode 100644 index 0000000..274e67e --- /dev/null +++ b/tawus-hibernate/src/test/java/com/googlecode/tawus/hibernate/services/TestModule.java @@ -0,0 +1,59 @@ +package com.googlecode.tawus.hibernate.services; + +import java.util.Properties; + +import org.apache.tapestry5.ioc.MethodAdviceReceiver; +import org.apache.tapestry5.ioc.OrderedConfiguration; +import org.apache.tapestry5.ioc.annotations.Contribute; +import org.apache.tapestry5.ioc.annotations.Match; +import org.apache.tapestry5.ioc.annotations.SubModule; + +import com.googlecode.tawus.hibernate.services.TawusHibernateModule; +import com.googlecode.tawus.hibernate.services.SessionFactoryConfiguration; +import com.googlecode.tawus.hibernate.services.SessionFactorySource; + +@SubModule(TawusHibernateModule.class) +public class TestModule { + + @Contribute(SessionFactorySource.class) + public void providerSessionFactorySource( + OrderedConfiguration configuration) { + configuration.add("default", + new SessionFactoryConfiguration(){ + public void configure( + org.hibernate.cfg.Configuration configuration) { + Properties prop = new Properties(); + prop.put("hibernate.dialect", + "org.hibernate.dialect.HSQLDialect"); + prop.put("hibernate.connection.driver_class", + "org.hsqldb.jdbcDriver"); + prop + .put("hibernate.connection.url", + "jdbc:hsqldb:mem:testdb"); + prop.put("hibernate.connection.username", "sa"); + prop.put("hibernate.connection.password", ""); + prop.put("hibernate.connection.pool_size", "1"); + prop.put("hibernate.connection.autocommit", "false"); + prop.put("hibernate.hbm2ddl.auto", "create-drop"); + prop.put("hibernate.show_sql", "true"); + prop.put("hibernate.current_session_context_class", "thread"); + configuration.addProperties(prop); + } + + public String[] getPackageNames() { + return new String[]{"com.googlecode.tawus.hibernate.models"}; + } + + public String getFactoryId() { + return "default"; + } + }); + } + + @Match( { "*DAO" }) + public static void adviseTransaction(TransactionAdvisor transactionAdvisor, + MethodAdviceReceiver receiver) { + transactionAdvisor.addTransactionAdvice(receiver); + } + +} diff --git a/tawus-hibernate/src/test/webapp/WEB-INF/web.xml b/tawus-hibernate/src/test/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..ad7b2c2 --- /dev/null +++ b/tawus-hibernate/src/test/webapp/WEB-INF/web.xml @@ -0,0 +1,31 @@ + + + + + + + + Integration Test + + + tapestry.app-package + com.googlecode.tawus.hibernate + + + + test + org.apache.tapestry5.TapestryFilter + + + + test + /* + + + + index + + +