Index: topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java diff -u topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.4 topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.5 --- topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java:1.4 Mon Jul 25 15:19:48 2005 +++ topia/src/java/org/codelutin/topia/persistence/PersistenceStorageJDBC.java Mon Aug 1 17:10:15 2005 @@ -23,10 +23,10 @@ * Created: 20 juillet 2005 15:25:06 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.4 $ + * @version $Revision: 1.5 $ * - * Last update: $Date: 2005/07/25 15:19:48 $ - * by : $Author: bpoussin $ + * Last update: $Date: 2005/08/01 17:10:15 $ + * by : $Author: thimel $ */ package org.codelutin.topia.persistence; @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -58,6 +59,7 @@ import org.apache.commons.dbcp.PoolingDriver; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory; +import org.codelutin.queryparser.QueryHelperException; import org.codelutin.topia.TopiaConst; import org.codelutin.topia.TopiaException; import org.codelutin.topia.TopiaId; @@ -834,7 +836,7 @@ * @return vrai si la methode find est implanté */ public boolean haveFindImplemented(){ - return false; + return true; } /** @@ -844,7 +846,44 @@ * leve l'exception UnsupportedOperationException */ public List find(TopiaTransaction tt, TopiaQuery query) throws TopiaPersistenceException { - throw new UnsupportedOperationException("Find method is not implemented for PersistenceStorageJDBC"); + TopiaJDBCQueryHelper queryHelper = new TopiaJDBCQueryHelper(); + try { + queryHelper.setQuery(query.getQueryString()); + } catch (IOException eee) { + throw new TopiaPersistenceException("Creation de la requete impossible, requete source mal formée", eee); + } + List results = new ArrayList(); + try { + String preparedQuery = queryHelper.getPreparedQuery(tt); + try { + Connection conn = getConnection(); + try{ + PreparedStatement sta = conn.prepareStatement(preparedQuery); + try{ + Iterator args = queryHelper.getArgs().iterator(); + int i = 1; + while (args.hasNext()) { + sta.setObject(i, args.next()); + i++; + } + ResultSet rr = sta.executeQuery(); + while(rr.next()){ + results.add(rr.getString("id")); + } + } finally { + sta.close(); + } + conn.commit(); + } finally { + conn.close(); + } + } catch (SQLException eee) { + throw new TopiaPersistenceException("Erreur durant le find transaction : " + tt.getId(), eee); + } + } catch (QueryHelperException eee) { + throw new TopiaPersistenceException("Execution de la requete impossible, requete source mal formée", eee); + } + return results; } /** Index: topia/src/java/org/codelutin/topia/persistence/TopiaJDBCQueryHelper.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/TopiaJDBCQueryHelper.java:1.1 --- /dev/null Mon Aug 1 17:10:20 2005 +++ topia/src/java/org/codelutin/topia/persistence/TopiaJDBCQueryHelper.java Mon Aug 1 17:10:15 2005 @@ -0,0 +1,443 @@ +/* *##% + * Copyright (C) 2002, 2003 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaJDBCQueryHelper.java + * + * Created: 28 juil. 2005 + * + * @author Arnaud Thimel + * Copyright Code Lutin + * @version $Revision: 1.1 $ + * + * Mise a jour: $Date: 2005/08/01 17:10:15 $ + * par : $Author: thimel $ + */ +package org.codelutin.topia.persistence; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.codelutin.queryparser.Constraint; +import org.codelutin.queryparser.DefaultQueryVisitorUnsupportedOperation; +import org.codelutin.queryparser.FieldPath; +import org.codelutin.queryparser.From; +import org.codelutin.queryparser.OrderBy; +import org.codelutin.queryparser.QMark; +import org.codelutin.queryparser.Query; +import org.codelutin.queryparser.QueryHelper; +import org.codelutin.queryparser.QueryHelperException; +import org.codelutin.queryparser.Select; +import org.codelutin.queryparser.Where; +import org.codelutin.topia.TopiaEntity; + +public class TopiaJDBCQueryHelper extends QueryHelper { + + public static final String IN_START = " AND (id,longdate) IN ("; + public static final String IN_AND_SELECT = "SELECT id, longdate FROM data WHERE "; + + public static final String LEAD_SELECT = "SELECT DISTINCT id FROM data WHERE "; + public static final String END_BEFORE_TYPE = "SELECT id, min(longdate) FROM management WHERE (id,longdate) IN (SELECT id, max(longdate) FROM management WHERE class='"; + public static final String END_MIDDLE_TYPE = "' AND (longdate=? OR (longdate>0 AND longdate<=?)) AND id NOT IN (SELECT id FROM management WHERE (longdate=? OR (longdate>0 AND longdate<=?)) AND isDeleted=true) GROUP BY id UNION SELECT id, longdate FROM management WHERE class='"; + public static final String END_AFTER_TYPE = "' AND longdate=? AND isDeleted=false) GROUP BY id)"; + +// public static final String LEAD_SELECT = "SELECT * FROM data WHERE "; +// public static final String END_BEFORE_TYPE = "SELECT id, min(longdate) FROM management WHERE (id,longdate) IN (SELECT id, max(longdate) FROM management WHERE class='"; +// public static final String END_MIDDLE_TYPE = "' AND (longdate=-900 OR (longdate>0 AND longdate<=900)) AND id NOT IN (SELECT id FROM management WHERE (longdate=-900 OR (longdate>0 AND longdate<=900)) AND isDeleted=true) GROUP BY id UNION SELECT id, longdate FROM management WHERE class='"; +// public static final String END_AFTER_TYPE = "' AND longdate=-900 AND isDeleted=false) GROUP BY id)"; + + protected TopiaJDBCQueryBuilder queryBuilder; + protected TopiaTransaction transaction; + protected long thetime; + protected String request; + + public void setQuery(Query query) { + super.setQuery(query); + queryBuilder = null; + } + + public String getPreparedQuery(TopiaTransaction tt) throws QueryHelperException { + if (getQuery() == null) { + throw new QueryHelperException("Please use setQuery(...) " + + "before execute()"); + } + if (transaction == null) { +// throw new QueryHelperException("Please use setTransaction(...) " + +// "before execute()"); + thetime = System.currentTimeMillis(); + } + + // Préparation de la requete SQL + if (queryBuilder == null) { + queryBuilder = new TopiaJDBCQueryBuilder(); + getQuery().visit(queryBuilder); + + request = LEAD_SELECT; + for (String s : queryBuilder.getANDs()) { + request += s + IN_START + IN_AND_SELECT; + } + request += queryBuilder.getFilter() + IN_START; + request += END_BEFORE_TYPE + queryBuilder.getFrom() + END_MIDDLE_TYPE + queryBuilder.getFrom() + END_AFTER_TYPE; + for (int i = 0; i < queryBuilder.getANDs().size(); i++) { + request += ")"; + } + request += ";"; + + // utiliser la transaction pour ajouter les valeurs des 5 "?". Il faut : + // -transaction.getId(), transaction.getId(), -transaction.getId(), transaction.getId(), -transaction.getId() + // soit : -, +, -, +, - + args.add(-thetime); + args.add(thetime); + args.add(-thetime); + args.add(thetime); + args.add(-thetime); + } + + return request; + } + + @Override + public Collection execute() throws QueryHelperException { + throw new QueryHelperException("NotImplemented, please use getPreparedQuery()"); + } + + public class TopiaJDBCQueryBuilder extends DefaultQueryVisitorUnsupportedOperation { + + private String filter = ""; + private Query query; + private List ANDs; + private String from; + + private boolean inNotClause = false; + + public String getFrom() { + return from; + } + + public String getFilter() { + return filter; + } + + public List getANDs() { + return ANDs; + } + + public void visitQuery(Query query) { + this.query = query; + ANDs = new LinkedList(); + } + + public void visitFrom(From from) { + } + public void visitFromSource(From from, String field, String alias) { + this.from = field; + } + + // OrderBy non supporté => renvoie une exception (DefaultQueryVisitorUnsupportedOperation) + public void visitOrderBy(OrderBy orderBy) { + super.visitOrderBy(orderBy); + } + public void visitOrderByField(OrderBy orderBy, FieldPath field, + boolean asc) { + super.visitOrderByField(orderBy, field, asc); + } + + public void visitSelect(Select select) { + } + public void visitSelectAvg(Select select) { + } + public void visitSelectCount(Select select) { + } + public void visitSelectDistinct(Select select) { + } + public void visitSelectField(Select select, FieldPath field, String alias) { + } + public void visitSelectLimit(Select select, int first, int last) { + } + public void visitSelectMax(Select select) { + } + public void visitSelectMin(Select select) { + } + public void visitSelectSum(Select select) { + } + + //Visite récursivement l'opérande spécifiée + protected void visitOperand(Object op){ + if (op instanceof Constraint) { + ((Constraint)op).visit(this); + } else if(op == null) { + filter += " null"; + } else if(op instanceof String) { + filter += " \""+op.toString()+"\""; + } else if(op instanceof QMark) { + filter += "?"; + } else if(op instanceof FieldPath) { + FieldPath field = (FieldPath)op; + String path = field.getField(); + String source = query.getFrom().getSource(field); + if (source.equals("")) { + source = from; + } + if (from.equals(source)) { + path = query.getFrom().getField(field); + } else { + // Sans doute faire plusieurs requetes pour pouvoir supporter + // plusieurs sources et faire des jointure entre elle. + throw new UnsupportedOperationException("Only one source can be used"); + } + if(path.length() > 0){ + filter += path; + } else { + filter += null; + } + } else if(op instanceof Number) { + filter += op.toString(); + } else if(op instanceof Boolean) { + filter += op.toString(); + } else if(op instanceof Collection) { +// filter += "argCollection" + argCollection.size(); +// argCollection.add(op); + } else { + throw new UnsupportedOperationException("This operand is not supported: "+op.getClass().getName()); + } + + } + + public void visitWhere(Where where) { + //Voir les visitWhere spécifiques... + } + public void visitWhereAnd(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + // OR mais sans changer le inNotClause car on veut que ca se + // répercute sur les arguments + visitWhereAbsoluteOr(constraint, op1, op2); + } else { + visitWhereAbsoluteAnd(constraint, op1, op2); + } + } + /** + * Absolute sous-entend que la méthode ne tient pas compte du NOT + * éventuel (inNoteClause) + */ + protected void visitWhereAbsoluteAnd(Constraint constraint, Object op1, + Object op2) { + visitOperand(op1); + ANDs.add(filter); + filter = "("; + visitOperand(op2); + filter += ")"; + } + public void visitWhereOr(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + // AND mais sans changer le inNotClause car on veut que ca se + // répercute sur les arguments + visitWhereAbsoluteAnd(constraint, op1, op2); + } else { + visitWhereAbsoluteOr(constraint, op1, op2); + } + } + /** + * Absolute sous-entend que la méthode ne tient pas compte du NOT + * éventuel (inNoteClause) + */ + protected void visitWhereAbsoluteOr(Constraint constraint, Object op1, + Object op2) { + filter += "("; + visitOperand(op1); + filter += " OR "; + visitOperand(op2); + filter += ")"; + } + public void visitWhereNot(Constraint constraint, Object op2) { + // FIXME ne réponds pas au requetes du genre + // "WHERE NOT nom='Thimel'" car transformé en + // "WHERE NOT (field='nom' AND value='Thimel')" + // Du coup si l'objet a un autre attribut, alors des résultats seront retournés + + // FIXED grâce à l'utilisation de inNotClause. Maintenant : + // "WHERE NOT nom='Thimel'" transformé en + // "WHERE (field='nom' AND value!='Thimel')" + // Ainsi, on positionne la variable inNotClause à son opposé, et le + // visiteur continue récursivement. Si une méthode voit qu'elle est + // dans un NOT, elle appelle la méthode opposée après avoir mis + // l'attribut à false de manière à ce que la méthode opposée + // effectue son traitement de manière normale. Puis elle + // repositionne l'attibut à true. + +// filter += "("/* + "NOT "*/; + inNotClause = !inNotClause; + visitOperand(op2); + inNotClause = !inNotClause; +// filter += ")"; + } + + public void visitWhereEqual(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereNotEqual(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value='"; + visitOperand(op2); + filter += "')"; + } + } + public void visitWhereNotEqual(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereEqual(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value!='"; + visitOperand(op2); + filter += "')"; + } + } + + public void visitWhereGreater(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereSmallerOrEqual(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value>'"; + visitOperand(op2); + filter += "')"; + } + } + public void visitWhereGreaterOrEqual(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereSmaller(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value>='"; + visitOperand(op2); + filter += "')"; + } + } + public void visitWhereSmaller(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereGreaterOrEqual(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value<'"; + visitOperand(op2); + filter += "')"; + } + } + public void visitWhereSmallerOrEqual(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereGreater(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value<='"; + visitOperand(op2); + filter += "')"; + } + } + + public void visitWhereIn(Constraint constraint, Object op1, Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereNotIn(constraint, op1, op2); + inNotClause = true; + } else { + // Non supporté, renvoie une exception + super.visitWhereIn(constraint, op1, op2); + //TODO +// filter += "("; +// visitOperand(op2); +// filter += ".contains(toObject("; +// visitOperand(op1); +// filter += ")))"; + } + } + public void visitWhereNotIn(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereIn(constraint, op1, op2); + inNotClause = true; + } else { + // Non supporté, renvoie une exception + super.visitWhereNotIn(constraint, op1, op2); + //TODO +// filter += "(!"; +// visitOperand(op2); +// filter += ".contains(toObject("; +// visitOperand(op1); +// filter += ")))"; + } + } + + public void visitWhereLike(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereNotLike(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value LIKE '"; + visitOperand(op2); + filter += "')"; + } + } + public void visitWhereNotLike(Constraint constraint, Object op1, + Object op2) { + if (inNotClause) { + inNotClause = false; + visitWhereLike(constraint, op1, op2); + inNotClause = true; + } else { + filter += "(field='"; + visitOperand(op1); + filter += "' AND value NOT LIKE '"; + visitOperand(op2); + filter += "')"; + } + } + + } + +}