Changeset 2000


Ignore:
Timestamp:
06/21/09 23:17:28 (3 years ago)
Author:
mswertz
Message:

feature: added queryrule rewrite mechanism to enable searches on mrefs
Explanation: mref fields are not fields in 'this' table but in a related link table. Thus, searching on these fields means first searching in the related table to find the 'this.id' values to filter on. rewriteMrefRule does exactly this.

Location:
molgenis3_3/src/org/molgenis
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • molgenis3_3/src/org/molgenis/framework/data/jdbc/AbstractJDBCMapper.java

    r1995 r2000  
    1212 
    1313import org.apache.log4j.Logger; 
     14import org.molgenis.framework.Database; 
    1415import org.molgenis.framework.data.DatabaseException; 
    1516import org.molgenis.framework.data.QueryRule; 
     
    9394                catch (Exception sqle) 
    9495                { 
     96                        sqle.printStackTrace(); 
    9597                        database.rollbackPrivateTx(TX_TICKET); 
    9698                        logger.error("ADD failed on " + this.create().getClass().getSimpleName() + ": " + sqle.getMessage()); 
     
    405407         */ 
    406408        public abstract void mapMrefs(List<E> entities) throws DatabaseException; 
     409         
     410        /** 
     411         * Rewrite mref rules: mref fields are actually not in the table but in a link table. 
     412         * To filter on an mref id or mref label one has to first query this table to extract ids for 'this' table. 
     413         * This function provides the functionality therefore.  
     414         *  
     415         * @param db 
     416         * @param user_rule the original rule 
     417         * @return a rewritten rule for mrefs, typically of the form 'id' IN 'list of ids' 
     418         * @throws DatabaseException 
     419         */ 
     420        protected abstract QueryRule rewriteMrefRule(Database db, QueryRule user_rule) throws DatabaseException; 
    407421 
    408422        /** 
     
    473487                try 
    474488                { 
    475                         String sql = createCountSql(rules); 
     489                        String sql = createCountSql(rules) 
     490                                        + getDatabase().createWhereSql((JDBCMapper) this, false, true, 
     491                                                        this.rewriteRules(getDatabase(), rules)); 
    476492                        // + createWhereSql(getMapperFor(klazz), false, true, rules); 
    477493                        ResultSet rs = getDatabase().executeQuery(sql); 
     
    484500                catch (SQLException sqle) 
    485501                { 
    486                         logger.error("count of "+this.create().getClass().getSimpleName()+"failed: " + sqle.getMessage()); 
     502                        logger.error("count of " + this.create().getClass().getSimpleName() + "failed: " + sqle.getMessage()); 
    487503                        throw new DatabaseException(sqle); 
    488504                } 
     
    593609        private ResultSet executeSelect(QueryRule... rules) throws DatabaseException, SQLException 
    594610        { 
    595                 String sql = createFindSql() + getDatabase().createWhereSql((JDBCMapper) this, false, true, rules); 
     611                String sql = createFindSql() 
     612                                + getDatabase().createWhereSql((JDBCMapper) this, false, true, 
     613                                                this.rewriteRules(getDatabase(), rules)); 
    596614 
    597615                // FIXME too complicated 
     
    609627        } 
    610628 
    611         // /** 
    612         // * Helper method for retrieving keys. 
    613         // * 
    614         // * @param entities 
    615         // * @param fromIndex 
    616         // * @param stmt 
    617         // * @throws DatabaseException 
    618         // */ 
    619         // public void getGeneratedKeys(List<E> entities, Statement stmt, 
    620         // int fromIndex) throws DatabaseException 
    621         // { 
    622         // E entity = null; 
    623         // ResultSet rs_keys = null; 
    624         // int i = 0; 
    625         // try 
    626         // { 
    627         // rs_keys = stmt.getGeneratedKeys(); 
    628         // while (rs_keys.next()) 
    629         // { 
    630         // entity = entities.get(fromIndex + i); 
    631         // setAutogeneratedKey(rs_keys.getInt(1), entity); 
    632         // entities.set(fromIndex + i, entity); // put it back again... 
    633         // i++; 
    634         // 
    635         // } 
    636         // } 
    637         // catch (Exception e) 
    638         // { 
    639         // logger.error("executeKeys(): " + e); 
    640         // e.printStackTrace(); 
    641         // throw new DatabaseException(e.getMessage()); 
    642         // } 
    643         // finally 
    644         // { 
    645         // try 
    646         // { 
    647         // rs_keys.close(); 
    648         // } 
    649         // catch (Exception e) 
    650         // { 
    651         // } 
    652         // rs_keys = null; 
    653         // } 
    654         // } 
    655  
     629        /** 
     630         * Mref fields do not really exist in the table but instead in a separate 
     631         * link table. This method should query this link table to rewrite the query 
     632         * rules. 
     633         *  
     634         * @param db 
     635         * @param user_rules 
     636         * @return 
     637         * @throws DatabaseException 
     638         */ 
     639        protected QueryRule[] rewriteRules(Database db, QueryRule... user_rules) throws DatabaseException 
     640        { 
     641                List<QueryRule> rules = this.rewriteRules(db, Arrays.asList(user_rules)); 
     642                return rules.toArray(new QueryRule[rules.size()]); 
     643        } 
     644 
     645        /** 
     646         * Mref fields do not really exist in the table but instead in a separate 
     647         * link table. This method should query this link table to rewrite the query 
     648         * rules. 
     649         *  
     650         * @param db 
     651         * @param user_rules 
     652         * @return 
     653         * @throws DatabaseException 
     654         */ 
     655        protected List<QueryRule> rewriteRules(Database db, List<QueryRule> user_rules) 
     656                        throws DatabaseException 
     657        { 
     658                List<QueryRule> rules = new ArrayList<QueryRule>(); 
     659                for(QueryRule rule: user_rules) 
     660                { 
     661                        if(rule.getOperator().equals(Operator.NESTED)) 
     662                        { 
     663                                QueryRule r = new QueryRule(this.rewriteRules(db, rule.getNestedRules())); 
     664                                r.setOr(rule.isOr()); 
     665                                rules.add(r); 
     666                        } 
     667                        else 
     668                        { 
     669                                QueryRule r = this.rewriteMrefRule(db, rule); 
     670                                r.setOr(rule.isOr()); 
     671                                rules.add(r); 
     672                        } 
     673                } 
     674                return rules; 
     675        } 
    656676} 
  • molgenis3_3/src/org/molgenis/framework/data/jdbc/JDBCConnectionHelper.java

    r1995 r2000  
    3030        /** Flag to indicate whether the database is in a transaction */ 
    3131        boolean inTransaction = false; 
    32          
     32 
    3333        int openconnections = 0; 
    3434 
     
    8080                        connection.setAutoCommit(true); 
    8181                        inTransaction = false; 
    82                         //FIXME in case of hsqldb we need to checkpoint 
    83                         //if(this.source.getDriverClassName().contains("hsql")) this.executeQuery("checkpoint"); 
     82                        // FIXME in case of hsqldb we need to checkpoint 
     83                        // if(this.source.getDriverClassName().contains("hsql")) 
     84                        // this.executeQuery("checkpoint"); 
    8485                        logger.info("commited transaction"); 
    8586                } 
     
    130131                        { 
    131132                                openconnections++; 
    132                                 logger.info(this+"opened database connection, connectioncount="+openconnections);                                
     133                                logger.info(this + "opened database connection, connectioncount=" + openconnections); 
    133134                                connection = source.getConnection(); 
    134135                                connection.setAutoCommit(true); // restore default 
     
    163164                                        if (!connection.isClosed()) connection.close(); 
    164165                                        connection = null; 
    165                                         openconnections--;                                       
    166                                         logger.info(this+"closed connection back to pool, connectioncount="+openconnections); 
     166                                        openconnections--; 
     167                                        logger.info(this + "closed connection back to pool, connectioncount=" + openconnections); 
    167168                                } 
    168169                                catch (Exception sqle) 
    169170                                { 
    170                                         //System.err.println(this+"Cannot close connection: " + sqle.getMessage()); 
    171                                         logger.error(this+"Cannot close connection: " + sqle.getMessage()); 
     171                                        // System.err.println(this+"Cannot close connection: " + 
     172                                        // sqle.getMessage()); 
     173                                        logger.error(this + "Cannot close connection: " + sqle.getMessage()); 
    172174                                } 
    173175                        } 
     
    183185         * @throws DatabaseException 
    184186         */ 
    185         public ResultSet executeQuery(String sql, QueryRule...rules) throws SQLException, DatabaseException 
     187        public ResultSet executeQuery(String sql, QueryRule... rules) throws SQLException, DatabaseException 
    186188        { 
    187189                getConnection(); 
     
    189191                try 
    190192                { 
    191                         stmt = connection.createStatement();//ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); 
     193                        stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); 
    192194                        if (source.getDriverClassName().contains("mysql")) 
    193195                        { 
    194                                 //stmt.setFetchSize(Integer.MIN_VALUE); // trigger streaming of 
     196                                stmt.setFetchSize(Integer.MIN_VALUE); // trigger streaming of 
    195197                        } 
    196198                        String allSql = sql; 
    197                         if(rules.length > 0) allSql += createWhereSql(null, false, true, rules); 
     199                        if (rules.length > 0) allSql += createWhereSql(null, false, true, rules); 
    198200                        ResultSet rs = stmt.executeQuery(allSql); 
    199                         logger.debug("executeQuery: " + allSql ); 
     201                        logger.debug("executeQuery: " + allSql); 
    200202                        return rs; 
    201203                } 
     
    284286                        else if (rule.getOperator() == QueryRule.Operator.IN) 
    285287                        { 
    286                                 if (where_clause.length() > 0) 
    287                                 { 
    288                                         if (rule.isOr()) where_clause.append(" OR "); 
    289                                         else 
    290                                                 where_clause.append(" AND "); 
    291                                 } 
    292  
    293                                 // where_clause.append(tablePrefix + rule.getField() + " IN("); 
    294                                 where_clause.append(rule.getField() + " IN("); 
    295  
    296                                 Object[] values = new Object[0]; 
    297                                 if (rule.getValue() instanceof List) 
    298                                 { 
    299                                         values = ((List<Object>) rule.getValue()).toArray(); 
    300                                 } 
    301                                 else 
    302                                 { 
    303                                         values = (Object[]) rule.getValue(); 
    304                                 } 
    305  
    306                                 for (int i = 0; i < values.length; i++) 
    307                                 { 
    308                                         if (i > 0) where_clause.append(","); 
    309                                         if (mapper != null && omitQuotes(mapper.getFieldType(rule.getField()))) 
     288                                //only add if nonempty condition??? 
     289                                if (rule.getValue() == null || (rule.getValue() instanceof List && ((List) rule.getValue()).size() == 0) 
     290                                                || (rule.getValue() instanceof Object[] && ((Object[]) rule.getValue()).length == 0)) 
     291                                                throw new DatabaseException("empty 'in' clause for rule "+rule); 
     292                                { 
     293                                        if (where_clause.length() > 0) 
    310294                                        { 
    311                                                 // where_clause.append(values[i] 
    312                                                 // .toString()); 
    313                                                 where_clause.append("" + escapeSql(values[i]) + ""); 
     295                                                if (rule.isOr()) where_clause.append(" OR "); 
     296                                                else 
     297                                                        where_clause.append(" AND "); 
     298                                        } 
     299 
     300                                        // where_clause.append(tablePrefix + rule.getField() + 
     301                                        // " IN("); 
     302                                        where_clause.append(rule.getField() + " IN("); 
     303 
     304                                        Object[] values = new Object[0]; 
     305                                        if (rule.getValue() instanceof List) 
     306                                        { 
     307                                                values = ((List<Object>) rule.getValue()).toArray(); 
    314308                                        } 
    315309                                        else 
    316310                                        { 
    317                                                 where_clause.append("'" + escapeSql(values[i]) + "'"); 
     311                                                values = (Object[]) rule.getValue(); 
    318312                                        } 
    319                                 } 
    320                                 where_clause.append(") "); 
    321  
     313 
     314                                        for (int i = 0; i < values.length; i++) 
     315                                        { 
     316                                                if (i > 0) where_clause.append(","); 
     317                                                if (mapper != null && omitQuotes(mapper.getFieldType(rule.getField()))) 
     318                                                { 
     319                                                        // where_clause.append(values[i] 
     320                                                        // .toString()); 
     321                                                        where_clause.append("" + escapeSql(values[i]) + ""); 
     322                                                } 
     323                                                else 
     324                                                { 
     325                                                        where_clause.append("'" + escapeSql(values[i]) + "'"); 
     326                                                } 
     327                                        } 
     328                                        where_clause.append(") "); 
     329                                } 
    322330                        } 
    323331                        else 
     
    372380                                if (new Boolean(false).equals(rule.getValue())) rule.setValue("0"); 
    373381                                Object value = rule.getValue() == null ? "NULL" : escapeSql(rule.getValue()); 
    374                                  
     382 
    375383                                if (!value.equals("NULL") && rule.getOperator() == Operator.LIKE 
    376384                                                && (mapper == null || !omitQuotes(mapper.getFieldType(rule.getField())))) 
     
    388396                                // where_clause.append(tablePrefix + rule.getField() + " " + 
    389397                                // operator + " '" + value + "'"); 
    390                                 if(rule.getOperator().equals(Operator.JOIN)) where_clause.append(rule.getField() + " " + operator + " " + value + ""); 
    391                                 else where_clause.append(rule.getField() + " " + operator + " '" + value + "'"); 
     398                                if (rule.getOperator().equals(Operator.JOIN)) where_clause.append(rule.getField() + " " + operator 
     399                                                + " " + value + ""); 
     400                                else 
     401                                        where_clause.append(rule.getField() + " " + operator + " '" + value + "'"); 
    392402                                // } 
    393403                        } 
     
    407417        private static boolean omitQuotes(Type t) 
    408418        { 
    409                 return t == Type.LONG || t == Type.INT || t == Type.DECIMAL ; 
     419                return t == Type.LONG || t == Type.INT || t == Type.DECIMAL; 
    410420 
    411421        } 
  • molgenis3_3/src/org/molgenis/framework/data/jdbc/JDBCDatabase.java

    r1985 r2000  
    8585        { 
    8686                super(new SimpleDataSourceWrapper(data_src)); 
    87                  
     87 
    8888                this.metadata = metadata; 
    8989 
     
    9696        { 
    9797                super(data_src); 
    98                  
     98 
    9999                this.metadata = metadata; 
    100                  
     100 
    101101                // optional: requires a fileSource 
    102102                if (file_source == null) logger.warn("JDBCDatabase: fileSource is missing"); 
     
    135135 
    136136        } 
    137          
     137 
    138138        public JDBCDatabase(String propertiesFilePath) throws FileNotFoundException, IOException 
    139139        { 
     
    150150 
    151151                File file_source = new File(p.getProperty("db_filepath")); 
    152                 this.fileSource = file_source;           
     152                this.fileSource = file_source; 
    153153        } 
    154154 
     
    261261        public <E extends Entity> void find(Class<E> klazz, CsvWriter writer, QueryRule... rules) throws DatabaseException 
    262262        { 
    263                 getMapperFor(klazz).find(writer,rules);  
     263                getMapperFor(klazz).find(writer, rules); 
    264264        } 
    265265 
     
    281281        public <E extends Entity> int add(List<E> entities) throws DatabaseException, IOException 
    282282        { 
    283                 return getMapperFor(entities).add(entities); 
     283                if (entities.size() > 0) return getMapperFor(entities).add(entities); 
     284                return 0; 
    284285        } 
    285286 
     
    301302        public <E extends Entity> int update(List<E> entities) throws DatabaseException, IOException 
    302303        { 
    303                 return getMapperFor(entities).update(entities); 
     304                if (entities.size() > 0) return getMapperFor(entities).update(entities); 
     305                return 0; 
    304306        } 
    305307 
     
    321323        public <E extends Entity> int remove(List<E> entities) throws DatabaseException, IOException 
    322324        { 
    323                 return getMapperFor(entities).remove(entities); 
     325                if (entities.size() > 0) return getMapperFor(entities).remove(entities); 
     326                return 0; 
    324327        } 
    325328 
     
    405408        { 
    406409                this.mappers.put(klazz.getName(), mapper); 
    407                 logger.debug("added mapper for klazz " + klazz.getName()); 
     410                // logger.debug("added mapper for klazz " + klazz.getName()); 
    408411        } 
    409412 
     
    522525                } 
    523526        } 
    524          
     527 
    525528        @Override 
    526529        public List<Class> getEntityClasses() 
  • molgenis3_3/src/org/molgenis/generators/data/mappers/MapperCommons.subclass_per_table.java.ftl

    r1979 r2000  
    4848                          <#assign xref_label = xref_entity.getAllField(f.getXRefLabelString()) /><#--can be from supertype!!--> 
    4949                          <#if name(xref_field.entity) != name(xref_label.entity)>+" LEFT JOIN ${SqlName(xref_label.entity)} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}" 
    50                           <#else>+" LEFT JOIN ${SqlName(f.getXRefEntity())} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}"</#if></#list> 
    51                           +  ((JDBCConnectionHelper) this.getDatabase()).createWhereSql((JDBCMapper)this, false, true, rules); 
     50                          <#else>+" LEFT JOIN ${SqlName(f.getXRefEntity())} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}"</#if></#list>; 
    5251        }        
    5352 
     
    6160                          <#assign xref_label = xref_entity.getAllField(f.getXRefLabelString()) /><#--can be from supertype!!--> 
    6261                          <#if name(xref_field.entity) != name(xref_label.entity)>+" LEFT JOIN ${SqlName(xref_label.entity)} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}" 
    63                           <#else>+" LEFT JOIN ${SqlName(f.getXRefEntity())} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}"</#if></#list> 
    64                           +  ((JDBCConnectionHelper) this.getDatabase()).createWhereSql((JDBCMapper)this, false, true, rules); 
     62                          <#else>+" LEFT JOIN ${SqlName(f.getXRefEntity())} AS xref${f_index} ON ${SqlName(f.getEntity())}.${SqlName(f)}=xref${f_index}.${SqlName(xref_field)}"</#if></#list>; 
    6563        } 
    6664         
     
    7068                <#list viewFields(entity) as f> 
    7169                <#assign type= f.type> 
    72                 if("${name(f)}".equals(fieldName)) return "${SqlName(f.entity)}.${SqlName(f)}"; 
     70                if("${name(f)}".equalsIgnoreCase(fieldName)) return "${SqlName(f.entity)}.${SqlName(f)}"; 
    7371                </#list>         
    7472                <#list viewFields(entity,"xref") as f>           
     
    7775                <#assign xref_label = xref_entity.getAllField(f.getXRefLabelString()) /><#--can be from supertype!!--> 
    7876                //alias for query on id field of xref entity 
    79                 if("${name(f)}_${name(xref_field)}".equals(fieldName)) return "${SqlName(f.entity)}.${SqlName(f)}"; 
     77                if("${name(f)}_${name(xref_field)}".equalsIgnoreCase(fieldName)) return "${SqlName(f.entity)}.${SqlName(f)}"; 
    8078                //alias for query on label of the xref entity 
    81                 if("${name(f)}_${name(xref_label)}".equals(fieldName)) return "xref${f_index}.${SqlName(xref_label)}"; 
     79                if("${name(f)}_${name(xref_label)}".equalsIgnoreCase(fieldName)) return "xref${f_index}.${SqlName(xref_label)}"; 
    8280                </#list>                                 
    8381                return fieldName; 
     
    9189                <#if type == "user" || type == "xref" || type == "mref">                 
    9290                <#assign type = model.getEntity(f.getXRefEntity()).getField(f.getXRefField()).getType()/> 
    93                 if("${name(f)}".equals(fieldName)) return Type.${type?upper_case}; 
    94                 if("${name(f)}_${name(xref_label)}".equals(fieldName)) return Type.STRING; 
     91                if("${name(f)}".equalsIgnoreCase(fieldName)) return Type.${type?upper_case}; 
     92                if("${name(f)}_${name(xref_label)}".equalsIgnoreCase(fieldName)) return Type.STRING; 
    9593                <#else>          
    96                 if("${name(f)}".equals(fieldName)) return Type.${type?upper_case}; 
     94                if("${name(f)}".equalsIgnoreCase(fieldName)) return Type.${type?upper_case}; 
    9795                </#if> 
    9896                </#list> 
     
    149147</#if></#list> 
    150148        } 
     149         
     150        @Override 
     151        public QueryRule rewriteMrefRule(Database db, QueryRule rule) throws DatabaseException 
     152        { 
     153<#assign else = false> 
     154<#list entity.getAllFields() as field> 
     155        <#if field.type = "mref">        
     156                <#if else>else </#if>if("${field.name}".equalsIgnoreCase(rule.getField())) 
     157                { 
     158                        // replace with id filter based on the many-to-many links in 
     159                        // ${field.getMrefName()} 
     160                        List<${JavaName(field.getMrefName())}> mref_mapping_entities = db.find(${JavaName(field.getMrefName())}.class, new QueryRule( 
     161                                        "${SqlName(field.getXRefEntity())}_${SqlName(field.getXRefField())}", rule.getOperator(), rule.getValue())); 
     162                        if (mref_mapping_entities.size() > 0) 
     163                        { 
     164                                List<Integer> mref_ids = new ArrayList<Integer>(); 
     165                                for (${JavaName(field.getMrefName())} mref : mref_mapping_entities) mref_ids.add(mref.get${JavaName(field.mrefLocalid)}()); 
     166                                return new QueryRule("${SqlName(pkey(entity))}", Operator.IN, mref_ids); 
     167                        }                
     168                        else 
     169                        { 
     170                                // no records to be shown 
     171                                return new QueryRule("id", Operator.EQUALS, Integer.MIN_VALUE); 
     172                        }                        
     173                } 
     174                else if("${field.name}_${field.getXRefLabelString()}".equalsIgnoreCase(rule.getField())) 
     175                { 
     176                        // replace with id filter based on the many-to-many links in 
     177                        // ${field.getMrefName()} 
     178                        List<${JavaName(field.getMrefName())}> mref_mapping_entities = db.find(${JavaName(field.getMrefName())}.class, new QueryRule( 
     179                                        "${SqlName(field.getXRefEntity())}_${SqlName(field.getXRefLabelString())}", rule.getOperator(), rule.getValue())); 
     180                        if (mref_mapping_entities.size() > 0) 
     181                        { 
     182                                List<Integer> mref_ids = new ArrayList<Integer>(); 
     183                                for (${JavaName(field.getMrefName())} mref : mref_mapping_entities) mref_ids.add(mref.get${JavaName(field.mrefLocalid)}()); 
     184                                return new QueryRule("${SqlName(pkey(entity))}", Operator.IN, mref_ids); 
     185                        }                
     186                        else 
     187                        { 
     188                                // no records to be shown 
     189                                return new QueryRule("id", Operator.EQUALS, Integer.MIN_VALUE); 
     190                        } 
     191                } 
     192                <#assign else = true>                            
     193                </#if> 
     194                </#list> 
     195                <#if else>else</#if> 
     196                { 
     197                        return rule; 
     198                } 
     199        } 
     200         
  • molgenis3_3/src/org/molgenis/generators/data/mappers/MultiqueryMapperGen.java.ftl

    r1978 r2000  
    3131 
    3232import org.molgenis.framework.Database; 
     33import org.molgenis.framework.data.QueryRule; 
     34import org.molgenis.framework.data.QueryRule.Operator; 
    3335import org.molgenis.framework.data.DatabaseException; 
    3436import org.molgenis.framework.data.jdbc.JDBCConnectionHelper; 
Note: See TracChangeset for help on using the changeset viewer.