Author: fdesbois Date: 2010-01-04 12:21:29 +0100 (Mon, 04 Jan 2010) New Revision: 1743 Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java Log: - Improve TopiaQuery using Generic and DAO for context - Add methods to create a TopiaQuery in TopiaDAO - Replace usage of Criteria by TopiaQuery in TopiaDAOImpl -> Increase execution time for findBy queries Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java 2010-01-04 09:28:49 UTC (rev 1742) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java 2010-01-04 11:21:29 UTC (rev 1743) @@ -35,22 +35,159 @@ import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaContext; import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaDAO; import org.nuiton.topia.persistence.TopiaEntity; /** - * Query HQL managment to simplify utilisation of {@link TopiaContext#find(java.lang.String, java.lang.Object[]) }. + * Query HQL managment to simplify usage of {@link TopiaContext#find(java.lang.String, java.lang.Object[]) }. * * TODO-FD20091224 Complete documentation of this class + JUnit Tests * + * <pre> + * This class is used to construct a HQL query and then execute it from a TopiaContext. The TopiaQuery is linked to a TopiaEntity which + * is the main element manipulated in the query. There is two parts in using this class : + * - construction of the query, using add, addFrom, addOrder, addSelect, addGroup, ... + * - execution of the query, using executeToEntityList, executeToEntity, executeToInteger, ... + * + * Construction + * ============ + * + * This class make easier the way to construct a HQL query. + * + * Example 1 : + * ----------- + * + * SQL : + * "SELECT * FROM PersonImpl WHERE firstName LIKE 'M%' AND year > 1980" + * + * HQL using {@link org.nuiton.topia.TopiaContext#find(java.lang.String, java.lang.Object[]) } : + * TopiaContext context = rootContext.beginTransaction(); + * context.find("FROM " + Person.class.getName() + " WHERE firstName LIKE :firstName AND year > :year", + * "firstName", "M%", year, 1980); + * + * TopiaQuery : + * TopiaQuery query = TopiaQuery.createQuery(Person.class).add(Person.FIRST_NAME, Op.LIKE, "M%").add(Person.YEAR, Op.GT, 1980); + * + * But the real advantage is when you have some parameters to test before adding them to the query. With the older method, it was tidious to construct + * and add parameters to finally use the find method from TopiaContext. + * + * Example 2 : + * ----------- + * + * HQL using {@link org.nuiton.topia.TopiaContext#find(java.lang.String, java.lang.Object[]) } : + * TopiaContext context = rootContext.beginTransaction(); + * + * String query = "FROM " + Person.class.getName(); + * List<Object> params = new ArrayList<Object>(); + * boolean filtered = false; + * // company parameter can be null + * if (company != null) { + * query += " WHERE company = :company"; + * params.add("company"); + * params.add(company); + * filtered = true; + * } + * + * // contact paramater can be null + * if (contact != null) { + * query += filtered ? " AND " : " WHERE " + * query += " contact = :contact"; + * params.add("contact"); + * params.add(contact); + * filtered = true; + * } + * + * context.find(query, params.toArray()); + * + * Here we have only two non obligatory params, but imagine if we must have almost 6 or 7 parameters like this ! + * + * TopiaQuery : + * TopiaQuery query = TopiaQuery.createQuery(Person.class); + * + * if (company != null) { + * query.add(Person.COMPANY, company); + * } + * + * if (contact != null) { + * query.add(Person.CONTACT, contact); + * } + * + * Many ways to create the same query : + * ------------------------------------ + * + * You can use multiple different manners to create a query, it depends on the complexicity. More complex is the query, more easier is to construct it. + * + * HQL : "FROM PersonImpl AS P WHERE (P.company IS NULL OR P.company = :company) AND P.firstName LIKE :firstName" + * + * Using TopiaQuery and an Alias : + * query = TopiaQuery.createQuery(Person.class, "P"); + * 1- query.add("(P.company IS NULL OR P.company = :company") AND P.firstName LIKE :firstName").addParam("company", company).addParam("firstName",firstName + "%"); + * 2- query.add("P.company IS NULL OR P.company = :company").add("P.firstName LIKE :firstName").addParam("company", company).addParam("firstName",firstName + "%"); + * 3- query.add("P.company IS NULL OR P.company = :company").add("P.firstName", Op.LIKE, firstName + "%").addParam("company", company); + * + * You can use TopiaQuery to create a subquery in an other TopiaQuery, you have to use the method {@link #fullQuery() } to get the full query in HQL and give + * it as a string in the other TopiaQuery. + * + * Execution + * ========= + * + * After construction, you can execute the query in different ways. + * + * Default method : + * ---------------- + * + * - execute : as the same result as {@link org.nuiton.topia.TopiaContext#find(java.lang.String, java.lang.Object[]) } + * + * Depends on entity type ; + * ------------------------ + * + * - executeToEntity : only one result, the first one + * - executeToEntityList : all results returned in a List + * - executeToEntityMap : all results returned in a Map with key defined by user or topiaId by default + * + * For aggregate : + * --------------- + * + * These methods have in argument the SELECT to execute the query. The previous SELECT (if defined) will not be deleted, but temporarly not used. + * + * - executeToInteger : for example for "SUM", "COUNT" + * - executeToString : for example for "MAX" + * - executeCount : directly a "count(*)" + * - executeToObject : for other type of possible result (Long, Boolean, Double, ...) + * + * Property loading + * ================ + * + * When using Hibernate, some times, Entities linked to the main one will be lazy initialized, but you want them directly when the + * query will be executed to avoid problems when closing context. You can use the method {@link #addLoad(java.lang.String[]) } to + * tell the TopiaQuery to load some properties when executing the query. After that, you don't need to call them for loading them in Hibernate. + * + * The syntax is the same as a property in HQL query using delegation : "person.company" where person and company are entities. + * + * Note : loading only available on collection or entities but not property on a collection of entities which must be made manually. + * + * For a Contact which is linked to a person (entity) and the person linked to company (entity) you can add to a TopiaQuery<Contact> : + * query.addLoad("person.company") + * + * For a list of addresses (entity) in the contact you can do : + * query.addLoad("addresses") + * + * But it's not possible to do for example with meeting (entity) linked to the contact and responsible (entity) linked to a meeting : + * query.addLoad("meetings.responsible") + * + * </pre> + * * Created: 21 déc. 2009 * + * @param <E> The main entity for the query * @author fdesbois * @version $Revision$ + * @since 2.3.0 * * Mise a jour: $Date$ * par : $Author$ */ -public class TopiaQuery { +public class TopiaQuery<E extends TopiaEntity> { private static final Log log = LogFactory.getLog(TopiaQuery.class); @@ -81,8 +218,10 @@ protected List<String> propertiesToLoad; - protected Class<? extends TopiaEntity> mainEntityClass; + protected Class<E> mainEntityClass; + protected TopiaDAO<E> dao; + /** * Enum to simmplify using operation in query */ @@ -124,21 +263,60 @@ * * @param entityClass Class for an entity Query */ - public TopiaQuery(Class<? extends TopiaEntity> entityClass) { - this(entityClass.getName()); - this.mainEntityClass = entityClass; + protected TopiaQuery(Class<E> entityClass) { + parentheses = true; + mainEntityClass = entityClass; + this.from = " FROM " + mainEntityClass.getName(); } /** + * Constructor of TopiaQuery from a DAO + * + * @param dao DAO linked to the entity to threat + */ + protected TopiaQuery(TopiaDAO<E> dao) { + this(dao.getEntityClass()); + this.dao = dao; + } + + /** * Constructor of TopiaQuery with String form initialization. * - * @param from From part for the Query + * @param entityClass + * @param alias of the main entity in the query */ - public TopiaQuery(String from) { - this.from = " FROM " + from; - parentheses = true; + protected TopiaQuery(Class<E> entityClass, String alias) { + this(entityClass); + this.from += " " + alias; } + /** + * Constructor of TopiaQuery from a DAO with an Alias + * + * @param dao DAO linked to the entity to threat + * @param alias of the main entity in the query + */ + protected TopiaQuery(TopiaDAO<E> dao, String alias) { + this(dao.getEntityClass(), alias); + this.dao = dao; + } + + public static <T extends TopiaEntity> TopiaQuery<T> createQuery(Class<T> entityClass) { + return new TopiaQuery<T>(entityClass); + } + + public static <T extends TopiaEntity> TopiaQuery<T> createQuery(TopiaDAO<T> dao) { + return new TopiaQuery<T>(dao); + } + + public static <T extends TopiaEntity> TopiaQuery<T> createQuery(Class<T> entityClass, String alias) { + return new TopiaQuery<T>(entityClass, alias); + } + + public static <T extends TopiaEntity> TopiaQuery<T> createQuery(TopiaDAO<T> dao, String alias) { + return new TopiaQuery<T>(dao, alias); + } + @Override public String toString() { return fullQuery(); @@ -174,7 +352,7 @@ * @param e value of the param * @return the TopiaQuery */ - public TopiaQuery addParam(String id, Object e) { + public TopiaQuery<E> addParam(String id, Object e) { getParams().add(id); getParams().add(e); return this; @@ -202,7 +380,7 @@ * @param properties * @return */ - public TopiaQuery addLoad(String... properties) { + public TopiaQuery<E> addLoad(String... properties) { getPropertiesToLoad().addAll(Arrays.asList(properties)); return this; } @@ -221,7 +399,7 @@ * @param where element to add * @return the TopiaQuery */ - public TopiaQuery add(String where) { + public TopiaQuery<E> add(String where) { if (this.where == null) { this.where = " WHERE "; } else { @@ -247,7 +425,7 @@ * @param paramValue the value of the parameter (an other entity, a String, ...) * @return the TopiaQuery */ - public TopiaQuery add(String paramName, Op constraint, Object paramValue) { + public TopiaQuery<E> add(String paramName, Op constraint, Object paramValue) { int dot = paramName.lastIndexOf("."); String valueName = paramName; if (dot != -1) { @@ -263,7 +441,7 @@ * @param paramName name of the parameter in the query * @return the TopiaQuery */ - public TopiaQuery addNotNull(String paramName) { + public TopiaQuery<E> addNotNull(String paramName) { return add(paramName + " " + Op.NOT_NULL); } @@ -277,17 +455,32 @@ * @return the TopiaQuery * @see #add(java.lang.String, fr.ifremer.suiviobsmer.TopiaQuery.Op, java.lang.Object) */ - public TopiaQuery add(String paramName, Object paramValue) { + public TopiaQuery<E> add(String paramName, Object paramValue) { return add(paramName, Op.EQ, paramValue); } /** + * Add a map of properties to the where clause of the query. Each property will be added to the query with + * Op.EQ operation, the key in the map is the property name, and the value is the value of the parameter in the + * query. + * + * @param properties + * @return + */ + public TopiaQuery<E> add(Map<String, Object> properties) { + for (String key : properties.keySet()) { + add(key, properties.get(key)); + } + return this; + } + + /** * Add an element to the from in the query. Used to add some other data in the query or for join. * * @param str the element to add * @return the TopiaQuery */ - public TopiaQuery addFrom(String str) { + public TopiaQuery<E> addFrom(String str) { from += ", " + str; return this; } @@ -298,7 +491,7 @@ * @param select element to add * @return the TopiaQuery */ - public TopiaQuery addSelect(String select) { + public TopiaQuery<E> addSelect(String select) { if (this.select == null) { this.select = "SELECT "; } else { @@ -314,7 +507,7 @@ * @param order element to add * @return the TopiaQuery */ - public TopiaQuery addOrder(String order) { + public TopiaQuery<E> addOrder(String order) { if (orderBy == null) { orderBy = " ORDER BY "; } else { @@ -324,7 +517,7 @@ return this; } - public TopiaQuery addOrderDesc(String order) { + public TopiaQuery<E> addOrderDesc(String order) { return addOrder(order + " DESC"); } @@ -334,7 +527,7 @@ * @param group element to add * @return the TopiaQuery */ - public TopiaQuery addGroup(String group) { + public TopiaQuery<E> addGroup(String group) { if (groupBy == null) { groupBy = " GROUP BY "; } else { @@ -351,7 +544,7 @@ * @param end last index to get from the results * @return the TopiaQuery */ - public TopiaQuery setLimit(int start, int end) { + public TopiaQuery<E> setLimit(int start, int end) { this.startIndex = start; this.endIndex = end; return this; @@ -363,7 +556,7 @@ * @param max the number of elements wanted * @return the TopiaQuery */ - public TopiaQuery setMaxResults(int max) { + public TopiaQuery<E> setMaxResults(int max) { return setLimit(0,max-1); } @@ -387,28 +580,38 @@ } /** + * DAO must be defined to use this method. + * + * @return a List of results + * @throws TopiaException + * @see #execute(org.nuiton.topia.TopiaContext) + */ + public List execute() throws TopiaException { + validateDAO(); + return execute(dao.getContext()); + } + + /** * Execute the query and get a List of entity. Some properties will be loaded if they are * prealably set using ${@link #addLoad(java.lang.String[]) }. * - * @param <T> the type of TopiaEntity to return * @param transaction the TopiaContext to use for execution - * @param entityClass the class of the TopiaEntity used for return type * @return a List of TopiaEntity corresponding to the entityClass in argument * @throws TopiaException * @throws ClassCastException */ - public <T extends TopiaEntity> List<T> executeToEntityList(TopiaContext transaction, Class<T> entityClass) + public List<E> executeToEntityList(TopiaContext transaction) throws TopiaException, ClassCastException { List res = execute(transaction); if (log.isTraceEnabled()) { log.trace("Properties to load : " + getPropertiesToLoad()); } - List<T> results = new ArrayList<T>(); + List<E> results = new ArrayList<E>(); for (Object o : res) { - if (o != null && !entityClass.isAssignableFrom(o.getClass())) { - throw new ClassCastException("Invalid cast for " + entityClass.getName()); + if (o != null && !mainEntityClass.isAssignableFrom(o.getClass())) { + throw new ClassCastException("Invalid cast for " + mainEntityClass.getName()); } - T entity = (T)o; + E entity = (E)o; if (!getPropertiesToLoad().isEmpty()) { loadProperties(entity); } @@ -418,112 +621,36 @@ } /** - * {@link #executeToEntity(org.nuiton.topia.TopiaContext, java.lang.Class) } with default entityClass set from constructor. + * DAO must be defined to use this method. * - * @param <T> - * @param transaction - * @return the TopiaEntity from entityClass set from ${@link #TopiaQuery(java.lang.Class) } constructor + * @return a List of TopiaEntity corresponding to the entityClass in argument * @throws TopiaException * @throws ClassCastException + * @see #executeToEntityList(org.nuiton.topia.TopiaContext) */ - public <T extends TopiaEntity> List<T> executeToEntityList(TopiaContext transaction) throws TopiaException, ClassCastException { - if (this.mainEntityClass == null) { - throw new NullPointerException("Main entity class from constructor can't be null to use executeToEntityList method without entityClass"); - } - return (List<T>)executeToEntityList(transaction, mainEntityClass); + public List<E> executeToEntityList() + throws TopiaException, ClassCastException { + validateDAO(); + return executeToEntityList(dao.getContext()); } /** - * Load all properties for the entity. - * - * @param <T> type of the entity extends TopiaEntity - * @param entity used to load properties - * @throws TopiaException - */ - protected <T extends TopiaEntity> void loadProperties(T entity) throws TopiaException { - for (String prop : getPropertiesToLoad()) { - if (log.isTraceEnabled()) { - log.trace("load property " + prop + " ..."); - } - List<String> str = Arrays.asList(prop.split("\\.")); - Iterator<String> it = str.iterator(); - TopiaEntity currEntity = entity; - while (it.hasNext()) { - String s = it.next(); - if (log.isTraceEnabled()) { - log.trace("Current entity : " + currEntity.getClass().getSimpleName()); - log.trace("Current loading : " + s); - } - if (it.hasNext()) { - currEntity = loadEntityProperty(currEntity, s); - } else { - loadProperty(currEntity, s); - } - } - } - } - - /** - * Load a property of type TopiaEntity from an other entity. - * - * @param <T> type of the entity extends TopiaEntity - * @param entity used to load the property - * @param property name of the property in the entity - * @return a TopiaEntity corresponding to the property loaded - * @throws TopiaException - */ - protected <T extends TopiaEntity> TopiaEntity loadEntityProperty(T entity, String property) throws TopiaException { - return (TopiaEntity)loadProperty(entity, property); - } - - /** - * Load a property from an entity. - * - * @param <T> type of the entity extends TopiaEntity - * @param entity used to load the property - * @param property name of the property in the entity - * @return an Object corresponding to the property loaded - * @throws TopiaException - */ - protected <T extends TopiaEntity> Object loadProperty(T entity, String property) throws TopiaException { - try { - Object res = PropertyUtils.getProperty(entity, property); - if (log.isTraceEnabled()) { - log.trace("load property '" + property + "' for '" + entity.getClass().getSimpleName() + "'"); - } - if (Collection.class.isAssignableFrom(res.getClass())) { - Collection list = (Collection) res; - list.size(); - } - return res; - } catch (IllegalAccessException eee) { - throw new TopiaException("Illegal access on property " + property + " from entity " + entity.getClass().getName(), eee); - } catch (InvocationTargetException eee) { - throw new TopiaException("Invocation error on entity " + entity.getClass().getName() + " for property " + property, eee); - } catch (NoSuchMethodException eee) { - throw new TopiaException("Getter method does not exist for property " + property + " from entity " + entity.getClass().getName(), eee); - } - } - - /** * Execute the query and get a Map of entity with key type in argument. Some properties will be loaded if they are * prealably set using ${@link #addLoad(java.lang.String[]) }. * * @param <K> the type of the map key - * @param <T> the type of entity, value of the map * @param transaction the TopiaContext to use for execution - * @param entityClass the class of the TopiaEntity used for return * @param keyName the property name of the key in the entity * @param keyClass the key class for the result map * @return a Map with the key type defined and the entity in value * @throws TopiaException * @throws ClassCastException */ - public <K, T extends TopiaEntity> Map<K, T> executeToEntityMap(TopiaContext transaction, Class<T> entityClass, String keyName, Class<K> keyClass) + public <K> Map<K, E> executeToEntityMap(TopiaContext transaction, String keyName, Class<K> keyClass) throws TopiaException, ClassCastException { - Map<K, T> results = new HashMap<K, T>(); - for (T elmt : executeToEntityList(transaction, entityClass)) { + Map<K, E> results = new HashMap<K, E>(); + for (E elmt : executeToEntityList(transaction)) { Object value = loadProperty(elmt, keyName); if (value != null && !keyClass.isAssignableFrom(value.getClass())) { throw new ClassCastException("Invalid cast for " + keyClass.getName()); @@ -534,92 +661,78 @@ } /** - * {@link #executeToEntityMap(org.nuiton.topia.TopiaContext, java.lang.Class, java.lang.String, java.lang.Class) } - * with default entityClass set from constructor. + * DAO must be defined to use this method. * * @param <K> - * @param <T> - * @param transaction * @param keyName * @param keyClass - * @return the TopiaEntity from entityClass set from ${@link #TopiaQuery(java.lang.Class) } constructor + * @return a Map with the key type defined and the entity in value * @throws TopiaException * @throws ClassCastException + * @see #executeToEntityMap(org.nuiton.topia.TopiaContext, java.lang.String, java.lang.Class) */ - public <K, T extends TopiaEntity> Map<K, T> executeToEntityMap(TopiaContext transaction, String keyName, Class<K> keyClass) + public <K> Map<K, E> executeToEntityMap(String keyName, Class<K> keyClass) throws TopiaException, ClassCastException { - if (this.mainEntityClass == null) { - throw new NullPointerException("Main entity class from constructor can't be null to use executeToEntityMap method without entityClass"); - } - return (Map<K, T>)executeToEntityMap(transaction, mainEntityClass, keyName, keyClass); + validateDAO(); + return executeToEntityMap(dao.getContext(), keyName, keyClass); } /** * Execute the query and get a Map of entity with topiaId in key. Some properties will be loaded if they are * prealably set using ${@link #addLoad(java.lang.String[]) }. * - * @param <T> the type of entity, value of the map * @param transaction the TopiaContext to use for execution - * @param entityClass the class of the TopiaEntity used for return * @return a Map with the key type defined and the entity in value * @throws TopiaException * @throws ClassCastException */ - public <T extends TopiaEntity> Map<String, T> executeToEntityMap(TopiaContext transaction, Class<T> entityClass) + public Map<String, E> executeToEntityMap(TopiaContext transaction) throws TopiaException, ClassCastException { - return executeToEntityMap(transaction, entityClass, TopiaEntity.TOPIA_ID, String.class); + return executeToEntityMap(transaction, TopiaEntity.TOPIA_ID, String.class); } /** - * {@link #executeToEntityMap(org.nuiton.topia.TopiaContext, java.lang.Class) } with default entityClass set from constructor. + * DAO must be defined to use this method. * - * @param <T> - * @param transaction - * @return the TopiaEntity from entityClass set from ${@link #TopiaQuery(java.lang.Class) } constructor + * @return a Map with the key type defined and the entity in value * @throws TopiaException * @throws ClassCastException + * @see #executeToEntityMap(org.nuiton.topia.TopiaContext) */ - public <T extends TopiaEntity> Map<String, T> executeToEntityMap(TopiaContext transaction) + public Map<String, E> executeToEntityMap() throws TopiaException, ClassCastException { - if (this.mainEntityClass == null) { - throw new NullPointerException("Main entity class from constructor can't be null to use executeToEntityMap method without entityClass"); - } - return (Map<String, T>)executeToEntityMap(transaction, mainEntityClass); + validateDAO(); + return executeToEntityMap(dao.getContext()); } /** * Execute the query and get the first result entity. Some properties will be loaded if they are * prealably set using ${@link #addLoad(java.lang.String[]) }. * - * @param <T> the type of TopiaEntity to return * @param transaction the TopiaContext to use for execution - * @param entityClass the class of the TopiaEntity used for return type * @return a TopiaEntity corresponding to the entityClass in argument * @throws TopiaException * @throws ClassCastException */ - public <T extends TopiaEntity> T executeToEntity(TopiaContext transaction, Class<T> entityClass) + public E executeToEntity(TopiaContext transaction) throws TopiaException, ClassCastException { setMaxResults(1); - List<T> results = executeToEntityList(transaction, entityClass); + List<E> results = executeToEntityList(transaction); return !results.isEmpty() ? results.get(0) : null; } /** - * {@link #executeToEntity(org.nuiton.topia.TopiaContext, java.lang.Class) } with default entityClass set from constructor. + * DAO must be defined to use this method. * - * @param <T> - * @param transaction - * @return the TopiaEntity from entityClass set from ${@link #TopiaQuery(java.lang.Class) } constructor + * @return a TopiaEntity corresponding to the entityClass in argument * @throws TopiaException * @throws ClassCastException + * @see #executeToEntity(org.nuiton.topia.TopiaContext) */ - public <T extends TopiaEntity> T executeToEntity(TopiaContext transaction) + public E executeToEntity() throws TopiaException, ClassCastException { - if (this.mainEntityClass == null) { - throw new NullPointerException("Main entity class from constructor can't be null to use executeToEntity method without entityClass"); - } - return (T)executeToEntity(transaction, mainEntityClass); + validateDAO(); + return executeToEntity(dao.getContext()); } /** @@ -637,6 +750,19 @@ } /** + * DAO must be defined to use this method. + * + * @param select + * @return an Integer + * @throws TopiaException + * @see #executeToInteger(org.nuiton.topia.TopiaContext, java.lang.String) + */ + public int executeToInteger(String select) throws TopiaException { + validateDAO(); + return executeToInteger(dao.getContext(), select); + } + + /** * Execute the query and get a String for result. Used for query with MAX, ... * The select is overriden to get only the right value for return. * @@ -651,6 +777,19 @@ } /** + * DAO must be defined to use this method. + * + * @param select + * @return + * @throws TopiaException + * @see #executeToString(org.nuiton.topia.TopiaContext, java.lang.String) + */ + public String executeToString(String select) throws TopiaException { + validateDAO(); + return executeToString(dao.getContext(), select); + } + + /** * Execute the query and get an Object for result. * The select is overriden to get only the right value for return. * @@ -675,6 +814,19 @@ } /** + * DAO must be defined to use this method. + * + * @param select + * @return an Object + * @throws TopiaException + * @see #executeToObject(org.nuiton.topia.TopiaContext, java.lang.String) + */ + public Object executeToObject(String select) throws TopiaException { + validateDAO(); + return executeToObject(dao.getContext(), select); + } + + /** * Execute a simple count on the query, i.e. the number of results get from the query. * * @param transaction the TopiaContext to use for execution @@ -685,4 +837,95 @@ return executeToInteger(transaction, "COUNT(*)"); } + /** + * DAO must be defined to use this method. + * + * @return an int corresponding to the number of result in the query + * @throws TopiaException + * @see #executeCount(org.nuiton.topia.TopiaContext) + */ + public int executeCount() throws TopiaException { + validateDAO(); + return executeCount(dao.getContext()); + } + + protected boolean validateDAO() throws TopiaException { + if (dao == null) { + throw new TopiaException("DAO not defined in TopiaQuery, can't execute it without TopiaContext"); + } + return true; + } + + /** + * Load all properties for the entity. + * + * @param <T> type of the entity extends TopiaEntity + * @param entity used to load properties + * @throws TopiaException + */ + protected <T extends TopiaEntity> void loadProperties(T entity) throws TopiaException { + for (String prop : getPropertiesToLoad()) { + if (log.isTraceEnabled()) { + log.trace("load property " + prop + " ..."); + } + List<String> str = Arrays.asList(prop.split("\\.")); + Iterator<String> it = str.iterator(); + TopiaEntity currEntity = entity; + while (it.hasNext()) { + String s = it.next(); + if (log.isTraceEnabled()) { + log.trace("Current entity : " + currEntity.getClass().getSimpleName()); + log.trace("Current loading : " + s); + } + if (it.hasNext()) { + currEntity = loadEntityProperty(currEntity, s); + } else { + loadProperty(currEntity, s); + } + } + } + } + + /** + * Load a property of type TopiaEntity from an other entity. + * + * @param <T> type of the entity extends TopiaEntity + * @param entity used to load the property + * @param property name of the property in the entity + * @return a TopiaEntity corresponding to the property loaded + * @throws TopiaException + */ + protected <T extends TopiaEntity> TopiaEntity loadEntityProperty(T entity, String property) throws TopiaException { + return (TopiaEntity)loadProperty(entity, property); + } + + /** + * Load a property from an entity. + * + * @param <T> type of the entity extends TopiaEntity + * @param entity used to load the property + * @param property name of the property in the entity + * @return an Object corresponding to the property loaded + * @throws TopiaException + */ + protected <T extends TopiaEntity> Object loadProperty(T entity, String property) throws TopiaException { + try { + Object res = PropertyUtils.getProperty(entity, property); + if (log.isTraceEnabled()) { + log.trace("load property '" + property + "' for '" + entity.getClass().getSimpleName() + "'"); + } + if (Collection.class.isAssignableFrom(res.getClass())) { + Collection list = (Collection) res; + list.size(); + } + return res; + } catch (IllegalAccessException eee) { + throw new TopiaException("Illegal access on property " + property + " from entity " + entity.getClass().getName(), eee); + } catch (InvocationTargetException eee) { + throw new TopiaException("Invocation error on entity " + entity.getClass().getName() + " for property " + property, eee); + } catch (NoSuchMethodException eee) { + throw new TopiaException("Getter method does not exist for property " + property + " from entity " + entity.getClass().getName(), eee); + } + } + } Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java 2010-01-04 09:28:49 UTC (rev 1742) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java 2010-01-04 11:21:29 UTC (rev 1743) @@ -39,6 +39,7 @@ import org.nuiton.topia.event.TopiaEntityListener; import org.nuiton.topia.event.TopiaEntityVetoable; import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaQuery; /** * @author poussin @@ -144,6 +145,23 @@ long size() throws TopiaException; /** + * Crée une requete basé sur l'entité lié au DAO. + * Résultat attendu : "FROM Entity" + * + * @return une nouvelle TopiaQuery vide. (uniquement avec le From sur le type d'entité) + */ + TopiaQuery<Entity> createQuery(); + + /** + * Crée une requete basé sur l'entité lié au DAO et lui assigne un alias valable dans la requête. + * Résultat attendu : "FROM Entity AS entityAlias" + * + * @param entityAlias alias permettant de manipuler l'entité dans la requête + * @return une nouvelle TopiaQuery + */ + TopiaQuery<Entity> createQuery(String entityAlias); + + /** * Recherche la classe en utilisant la cle naturelle, chaque champs de la * cle naturelle est une entre de la map passe en argument. * Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java 2010-01-04 09:28:49 UTC (rev 1742) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java 2010-01-04 11:21:29 UTC (rev 1743) @@ -53,7 +53,6 @@ import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.criterion.Criterion; -import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.hibernate.metadata.ClassMetadata; import org.nuiton.topia.framework.TopiaQuery; @@ -67,22 +66,22 @@ * est ainsi possible d'accèder au meta information hibernate sur les classes * lorque l'on en a besoin. * - * @param <Entity> le type de l'entite + * @param <E> le type de l'entite * @author poussin * */ -public class TopiaDAOImpl<Entity extends TopiaEntity> implements - TopiaDAO<Entity> { // TopiaDAOImpl +public class TopiaDAOImpl<E extends TopiaEntity> implements + TopiaDAO<E> { // TopiaDAOImpl /** to use log facility, just put in your code: log.info(\"...\"); */ private static Log log = LogFactory.getLog(TopiaDAOImpl.class); - protected Class<Entity> entityClass = null; + protected Class<E> entityClass = null; protected TopiaContextImplementor context = null; @Override - public Class<Entity> getEntityClass() { + public Class<E> getEntityClass() { throw new UnsupportedOperationException( "This method must be overided in generated DAO"); } @@ -93,7 +92,7 @@ * @return l'id de l'entity ou null si pas trouvé * @throws TopiaException Si une erreur survient durant la recherche */ - protected Serializable getId(Entity e) throws TopiaException { + protected Serializable getId(E e) throws TopiaException { ClassMetadata meta = getClassMetadata(); String idPropName = meta.getIdentifierPropertyName(); @@ -139,7 +138,7 @@ * @param entityClass */ @Override - public void init(TopiaContextImplementor context, Class<Entity> entityClass) + public void init(TopiaContextImplementor context, Class<E> entityClass) throws TopiaException { log.debug("init dao for " + entityClass.getName()); this.context = context; @@ -152,15 +151,15 @@ } @SuppressWarnings("unchecked") - protected Entity instanciateNew() throws TopiaException { - Entity result = null; + protected E instanciateNew() throws TopiaException { + E result = null; if (log.isDebugEnabled()) { log.debug("entityClass = " + entityClass); } String classname = entityClass.getName(); try { // on commence par essayer d'instancier le Impl - result = ((Class<Entity>) Class.forName(classname + "Impl")) + result = ((Class<E>) Class.forName(classname + "Impl")) .newInstance(); if (log.isDebugEnabled()) { log.debug("Utilisation de la classe " + classname + "Impl" @@ -243,7 +242,7 @@ } @Override - public Entity create(Object... properties) throws TopiaException { + public E create(Object... properties) throws TopiaException { Map<String, Object> map = new HashMap<String, Object>(); Object propertyName = null; Object value = null; @@ -264,19 +263,19 @@ + propertyName.getClass().getName()); } - Entity result = create(map); + E result = create(map); return result; } @Override - public Entity findByPrimaryKey(Map<String, Object> keys) + public E findByPrimaryKey(Map<String, Object> keys) throws TopiaException { try { // we used hibernate meta information for all persistence type // it's more easy than create different for all persistence ClassMetadata meta = getClassMetadata(); if (meta.hasNaturalIdentifier()) { - Entity result = findByProperties(keys); + E result = findByProperties(keys); return result; } } catch (HibernateException eee) { @@ -305,7 +304,7 @@ } @Override - public Entity findByPrimaryKey(Object... k) throws TopiaException { + public E findByPrimaryKey(Object... k) throws TopiaException { // TODO pour une meilleur gestion des problemes a la compilation // mettre un premier couple (propName, value) en argument ca evitera // de pouvoir appeler cette methode sans argument @@ -320,7 +319,7 @@ keys.put(pnames[ikey], k[ikey]); } - Entity result = findByProperties(keys); + E result = findByProperties(keys); return result; } } catch (HibernateException eee) { @@ -332,7 +331,7 @@ } @Override - public Entity findByProperties(String propertyName, Object value, + public E findByProperties(String propertyName, Object value, Object... others) throws TopiaException { Map<String, Object> properties = new HashMap<String, Object>(); properties.put(propertyName, value); @@ -353,12 +352,12 @@ + " La dernière propriété était: " + name, eee); } } - Entity result = findByProperties(properties); + E result = findByProperties(properties); return result; } @Override - public List<Entity> findAllByProperties(String propertyName, Object value, + public List<E> findAllByProperties(String propertyName, Object value, Object... others) throws TopiaException { Map<String, Object> properties = new HashMap<String, Object>(); properties.put(propertyName, value); @@ -379,15 +378,15 @@ + " La dernière propriété était: " + name, eee); } } - List<Entity> result = findAllByProperties(properties); + List<E> result = findAllByProperties(properties); return result; } @Override - public Entity findContainsProperties(Map<String, Collection> properties) + public E findContainsProperties(Map<String, Collection> properties) throws TopiaException { - List<Entity> results = findAllContainsProperties(properties); - Entity result = null; + List<E> results = findAllContainsProperties(properties); + E result = null; if (results.size() > 0) { result = results.get(0); } @@ -395,7 +394,7 @@ } @Override - public Entity findContainsProperties(String propertyName, + public E findContainsProperties(String propertyName, Collection values, Object... others) throws TopiaException { Map<String, Collection> properties = new HashMap<String, Collection>(); properties.put(propertyName, values); @@ -416,7 +415,7 @@ + " La dernière propriété était: " + name, eee); } } - Entity result = findContainsProperties(properties); + E result = findContainsProperties(properties); return result; } @@ -436,10 +435,10 @@ * @throws org.nuiton.topia.TopiaException if any pb */ @Override - public List<Entity> findAllContainsProperties(Map<String, Collection> properties) throws TopiaException { - List<Entity> all = findAll(); - List<Entity> result = new ArrayList<Entity>(); - for (Entity e : all) { + public List<E> findAllContainsProperties(Map<String, Collection> properties) throws TopiaException { + List<E> all = findAll(); + List<E> result = new ArrayList<E>(); + for (E e : all) { boolean ok = true; try { for (Entry<String, Collection> kv : properties.entrySet()) { @@ -481,7 +480,7 @@ } @Override - public List<Entity> findAllContainsProperties(String propertyName, + public List<E> findAllContainsProperties(String propertyName, Collection values, Object... others) throws TopiaException { Map<String, Collection> properties = new HashMap<String, Collection>(); properties.put(propertyName, values); @@ -502,25 +501,25 @@ + " La dernière propriété était: " + name, eee); } } - List<Entity> result = findAllContainsProperties(properties); + List<E> result = findAllContainsProperties(properties); return result; } @Override - public List<Entity> findAllByProperty(String propertyName, Object value) + public List<E> findAllByProperty(String propertyName, Object value) throws TopiaException { Map<String, Object> properties = new HashMap<String, Object>(); properties.put(propertyName, value); - List<Entity> result = findAllByProperties(properties); + List<E> result = findAllByProperties(properties); return result; } @Override - public Entity findByProperty(String propertyName, Object value) + public E findByProperty(String propertyName, Object value) throws TopiaException { Map<String, Object> properties = new HashMap<String, Object>(); properties.put(propertyName, value); - Entity result = findByProperties(properties); + E result = findByProperties(properties); return result; } @@ -629,11 +628,11 @@ * ou a appeler vous aussi ces deux methodes. */ @Override - public Entity create(Map<String, Object> properties) throws TopiaException { - Entity result = instanciateNew(); + public E create(Map<String, Object> properties) throws TopiaException { + E result = instanciateNew(); // TODO reflechir s'il ne faudrait pas creer l'id avant l'event precedent - // reflechir toujours dans un context on les Entity pourrait ne pas + // reflechir toujours dans un context on les E pourrait ne pas // etre des TopiaEntity if (result instanceof TopiaEntity) { String topiaId = TopiaId.create(entityClass); @@ -668,7 +667,7 @@ } @Override - public Entity update(Entity e) throws TopiaException { + public E update(E e) throws TopiaException { try { getSession().saveOrUpdate(e); getContext().getFiresSupport().warnOnUpdateEntity(e); @@ -679,7 +678,7 @@ } @Override - public void delete(Entity e) throws TopiaException { + public void delete(E e) throws TopiaException { try { getSession().delete(e); getContext().getFiresSupport().warnOnDeleteEntity(e); @@ -689,46 +688,63 @@ } @Override - public Entity findByTopiaId(String k) throws TopiaException { - return query(Restrictions.idEq(k)); + public E findByTopiaId(String k) throws TopiaException { + return createQuery().add(TopiaEntity.TOPIA_ID, k).executeToEntity(); + //return query(Restrictions.idEq(k)); } @Override - public List<Entity> findAll() throws TopiaException { - try { - Criteria criteria = createCriteria(FlushMode.AUTO); - List<Entity> result = (List<Entity>) criteria.list(); - result = getContext().getFiresSupport().fireEntitiesLoad(context, - result); - return result; - } catch (HibernateException eee) { - throw new TopiaException(eee); - } + public List<E> findAll() throws TopiaException { +// try { +// Criteria criteria = createCriteria(FlushMode.AUTO); +// List<E> result = (List<E>) criteria.list(); +// result = getContext().getFiresSupport().fireEntitiesLoad(context, +// result); +// return result; +// } catch (HibernateException eee) { +// throw new TopiaException(eee); +// } + return createQuery().executeToEntityList(); } @Override public List<String> findAllIds() throws TopiaException { - List<String> find = context.find("select src.topiaId from " + entityClass.getSimpleName() + "Impl src"); - return find; + //List<String> find = context.find("select src.topiaId from " + entityClass.getSimpleName() + "Impl src"); + //return find; + return createQuery().addSelect(TopiaEntity.TOPIA_ID).execute(context); } + @Override + public TopiaQuery<E> createQuery() { + return TopiaQuery.createQuery(this); + } + @Override + public TopiaQuery<E> createQuery(String entityAlias) { + return TopiaQuery.createQuery(this, entityAlias); + } @Override - public List<Entity> findAllWithOrder(String... propertyNames) + public List<E> findAllWithOrder(String... propertyNames) throws TopiaException { - try { - Criteria criteria = createCriteria(FlushMode.AUTO); - for (String propertyName : propertyNames) { - criteria.addOrder(Order.asc(propertyName)); - } - List<Entity> result = (List<Entity>) criteria.list(); - result = getContext().getFiresSupport().fireEntitiesLoad(context, - result); - return result; - } catch (HibernateException eee) { - throw new TopiaException(eee); +// try { +// Criteria criteria = createCriteria(FlushMode.AUTO); +// for (String propertyName : propertyNames) { +// criteria.addOrder(Order.asc(propertyName)); +// } +// List<E> result = (List<E>) criteria.list(); +// result = getContext().getFiresSupport().fireEntitiesLoad(context, +// result); +// return result; +// } catch (HibernateException eee) { +// throw new TopiaException(eee); +// } + + TopiaQuery<E> query = createQuery(); + for (String propertyName : propertyNames) { + query.addOrder(propertyName); } + return query.executeToEntityList(); } /** @@ -742,69 +758,71 @@ //int result = findAll().size(); // List result = this.getContext().find("SELECT count(*) FROM " + getEntityClass().getName() + "Impl"); // return (Long)result.get(0); - return new TopiaQuery(getEntityClass()).executeCount(context); + return createQuery().executeCount(context); } @Override - public Entity findByProperties(Map<String, Object> properties) + public E findByProperties(Map<String, Object> properties) throws TopiaException { - return query(Restrictions.allEq(properties)); + //return query(Restrictions.allEq(properties)); + return createQuery().add(properties).executeToEntity(); } @Override - public List<Entity> findAllByProperties(Map<String, Object> properties) + public List<E> findAllByProperties(Map<String, Object> properties) throws TopiaException { - return queryAll(Restrictions.allEq(properties)); + return createQuery().add(properties).executeToEntityList(); + //return queryAll(Restrictions.allEq(properties)); } - private List<Entity> queryAll(Criterion criterion) throws TopiaException { - try { - Criteria criteria = createCriteria(FlushMode.AUTO); - criteria.add(criterion); - List<Entity> result = (List<Entity>) criteria.list(); - result = getContext().getFiresSupport().fireEntitiesLoad(context, - result); - return result; - } catch (HibernateException eee) { - throw new TopiaException(eee); - } - } +// private List<E> queryAll(Criterion criterion) throws TopiaException { +// try { +// Criteria criteria = createCriteria(FlushMode.AUTO); +// criteria.add(criterion); +// List<E> result = (List<E>) criteria.list(); +// result = getContext().getFiresSupport().fireEntitiesLoad(context, +// result); +// return result; +// } catch (HibernateException eee) { +// throw new TopiaException(eee); +// } +// } +// +// private E query(Criterion criterion) throws TopiaException { +// try { +// Criteria criteria = createCriteria(FlushMode.AUTO); +// criteria.add(criterion); +// criteria.setMaxResults(1); +// List<E> result = (List<E>) criteria.list(); +// int sizeBefore = (result != null ? result.size() : 0); +// result = getContext().getFiresSupport().fireEntitiesLoad(context, +// result); +// int sizeAfter = (result != null ? result.size() : 0); +// if (sizeAfter < sizeBefore) { +// if (log.isDebugEnabled()) { +// log.debug((sizeBefore - sizeAfter) +// + " element(s) removed. Filter entity: " +// + entityClass.getName() + " - criterion: " +// + criterion); +// } +// } +// if (result != null && result.size() > 0) { +// E elem = result.get(0); +// return elem; +// } +// return null; +// } catch (HibernateException eee) { +// throw new TopiaException(eee); +// } +// } - private Entity query(Criterion criterion) throws TopiaException { - try { - Criteria criteria = createCriteria(FlushMode.AUTO); - criteria.add(criterion); - criteria.setMaxResults(1); - List<Entity> result = (List<Entity>) criteria.list(); - int sizeBefore = (result != null ? result.size() : 0); - result = getContext().getFiresSupport().fireEntitiesLoad(context, - result); - int sizeAfter = (result != null ? result.size() : 0); - if (sizeAfter < sizeBefore) { - if (log.isDebugEnabled()) { - log.debug((sizeBefore - sizeAfter) - + " element(s) removed. Filter entity: " - + entityClass.getName() + " - criterion: " - + criterion); - } - } - if (result != null && result.size() > 0) { - Entity elem = result.get(0); - return elem; - } - return null; - } catch (HibernateException eee) { - throw new TopiaException(eee); - } - } - //FIXME : Commenté car impossible de trouver le bon Criterion // ATTENTION ancienne methode du TopiaDAOAbstract deja presente dans le fichier // /* (non-Javadoc) // * @see org.nuiton.topia.persistence.TopiaDAO#findContainsProperties(java.util.Map) // */ - // public Entity findContainsProperties(Map<String, Collection> properties) throws TopiaException { + // public E findContainsProperties(Map<String, Collection> properties) throws TopiaException { // try { // Criteria criteria = createCriteria(FlushMode.AUTO); // for (Entry<String, Collection> entry : properties.entrySet()) { @@ -813,9 +831,9 @@ // } // } // criteria.setMaxResults(1); - // List<Entity> results = (List<Entity>)criteria.list(); + // List<E> results = (List<E>)criteria.list(); // if (results.size() > 0) { - // return (Entity)results.get(0); + // return (E)results.get(0); // } else { // return null; // } @@ -827,7 +845,7 @@ // /* (non-Javadoc) // * @see org.nuiton.topia.persistence.TopiaDAO#findAllContainsProperties(java.util.Map) // */ - // public List<Entity> findAllContainsProperties(Map<String, Collection> properties) throws TopiaException { + // public List<E> findAllContainsProperties(Map<String, Collection> properties) throws TopiaException { // try { // Criteria criteria = createCriteria(FlushMode.AUTO); // for (Entry<String, Collection> entry : properties.entrySet()) { @@ -835,7 +853,7 @@ // criteria.add(Restrictions.eq(entry.getKey(), value)); // } // } - // List<Entity> result = (List<Entity>)criteria.list(); + // List<E> result = (List<E>)criteria.list(); // return result; // } catch (HibernateException eee) { // throw new TopiaException(eee);
participants (1)
-
fdesbois@users.nuiton.org