=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTable.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTable.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTable.java 2015-10-02 13:58:22 +0000 @@ -0,0 +1,117 @@ +package org.hisp.dhis.resourcetable; + +/* + * Copyright (c) 2004-2015, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.util.List; +import java.util.Optional; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hisp.dhis.common.CodeGenerator; + +/** + * @author Lars Helge Overland + */ +public abstract class ResourceTable +{ + protected static final Log log = LogFactory.getLog( ResourceTable.class ); + + protected static final String TEMP_TABLE_SUFFIX = "_temp"; + + protected String tableName; + + protected List objects; + + protected String columnQuote; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + protected ResourceTable() + { + } + + public ResourceTable( String tableName, List objects, String columnQuote ) + { + this.tableName = tableName; + this.objects = objects; + this.columnQuote = columnQuote; + } + + // ------------------------------------------------------------------------- + // Public methods + // ------------------------------------------------------------------------- + + public final String getTableName() + { + return tableName; + } + + public final String getTempTableName() + { + return tableName + TEMP_TABLE_SUFFIX; + } + + public final String getSwapTablesStatement() + { + final String sql = + "drop table " + getTableName() + ";" + + "alter table " + getTempTableName() + " rename to " + getTableName() + ";"; + + return sql; + } + + // ------------------------------------------------------------------------- + // Protected methods + // ------------------------------------------------------------------------- + + protected String getRandomSuffix() + { + return CodeGenerator.generateCode( 5 ); + } + + // ------------------------------------------------------------------------- + // Abstract methods + // ------------------------------------------------------------------------- + + public abstract String getCreateTempTableStatement(); + + public abstract Optional getPopulateTempTableStatement(); + + public abstract Optional> getPopulateTempTableContent(); + + /** + * Get a SQL index create statement. Note that the index name must have a + * random component to avoid uniqueness conflicts. + * + * @return an optional SQL statement. + */ + public abstract Optional getCreateIndexStatement(); +} === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTableStore.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTableStore.java 2015-09-16 14:49:50 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/resourcetable/ResourceTableStore.java 2015-10-02 13:58:22 +0000 @@ -54,6 +54,13 @@ String TABLE_NAME_DATA_APPROVAL_MIN_LEVEL = "_dataapprovalminlevel"; /** + * Generates the given resource table. + * + * @param resourceTable the resource table. + */ + void generateResourceTable( ResourceTable resourceTable ); + + /** * Performs a batch update. * * @param columns the number of columns in the table to update. === modified file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java' --- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java 2015-09-23 12:23:52 +0000 +++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java 2015-10-02 13:58:22 +0000 @@ -29,19 +29,15 @@ */ import static org.hisp.dhis.dataapproval.DataApprovalLevelService.APPROVAL_LEVEL_HIGHEST; -import static org.hisp.dhis.resourcetable.ResourceTableStore.TABLE_NAME_CATEGORY_OPTION_COMBO_NAME; import static org.hisp.dhis.resourcetable.ResourceTableStore.TABLE_NAME_DATA_ELEMENT_STRUCTURE; import static org.hisp.dhis.resourcetable.ResourceTableStore.TABLE_NAME_DATE_PERIOD_STRUCTURE; -import static org.hisp.dhis.resourcetable.ResourceTableStore.TABLE_NAME_ORGANISATION_UNIT_STRUCTURE; import static org.hisp.dhis.resourcetable.ResourceTableStore.TABLE_NAME_PERIOD_STRUCTURE; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; @@ -62,7 +58,7 @@ import org.hisp.dhis.dataelement.DataElementGroupSet; import org.hisp.dhis.dataset.DataSet; import org.hisp.dhis.indicator.IndicatorGroupSet; -import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.jdbc.StatementBuilder; import org.hisp.dhis.organisationunit.OrganisationUnitGroupSet; import org.hisp.dhis.organisationunit.OrganisationUnitLevel; import org.hisp.dhis.organisationunit.OrganisationUnitService; @@ -72,6 +68,8 @@ import org.hisp.dhis.period.PeriodService; import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.resourcetable.statement.CreateCategoryOptionGroupSetTableStatement; +import org.hisp.dhis.resourcetable.table.CategoryOptionComboNameResourceTable; +import org.hisp.dhis.resourcetable.table.OrganisationUnitStructureResourceTable; import org.hisp.dhis.sqlview.SqlView; import org.hisp.dhis.sqlview.SqlViewService; import org.springframework.transaction.annotation.Transactional; @@ -136,100 +134,33 @@ { this.dataApprovalLevelService = dataApprovalLevelService; } - + + private StatementBuilder statementBuilder; + + public void setStatementBuilder( StatementBuilder statementBuilder ) + { + this.statementBuilder = statementBuilder; + } + // ------------------------------------------------------------------------- - // OrganisationUnitStructure + // ResourceTableService implementation // ------------------------------------------------------------------------- @Override @Transactional public void generateOrganisationUnitStructures() { - int maxLevel = organisationUnitService.getMaxOfOrganisationUnitLevels(); - - log.info( "Using " + maxLevel + " organisation unit levels for org unit structure table" ); - - resourceTableStore.createOrganisationUnitStructure( maxLevel ); - - List batchArgs = new ArrayList<>(); - - for ( int i = 0; i < maxLevel; i++ ) - { - int level = i + 1; - - Collection units = organisationUnitService.getOrganisationUnitsAtLevel( level ); - - for ( OrganisationUnit unit : units ) - { - List values = new ArrayList<>(); - - values.add( unit.getId() ); - values.add( unit.getUid() ); - values.add( level ); - - Map identifiers = new HashMap<>(); - Map uids = new HashMap<>(); - - for ( int j = level; j > 0; j-- ) - { - identifiers.put( j, unit.getId() ); - uids.put( j, unit.getUid() ); - - unit = unit.getParent(); - } - - for ( int k = 1; k <= maxLevel; k++ ) - { - values.add( identifiers.get( k ) != null ? identifiers.get( k ) : null ); - values.add( uids.get( k ) ); - } - - batchArgs.add( values.toArray() ); - } - } - - resourceTableStore.batchUpdate( (maxLevel * 2) + 3, TABLE_NAME_ORGANISATION_UNIT_STRUCTURE, batchArgs ); - - log.info( "Organisation unit structure table generated" ); + resourceTableStore.generateResourceTable( new OrganisationUnitStructureResourceTable( + "_orgunitstructure", null, statementBuilder.getColumnQuote(), + organisationUnitService, organisationUnitService.getMaxOfOrganisationUnitLevels() ) ); } - - // ------------------------------------------------------------------------- - // DataElementCategoryOptionComboName - // ------------------------------------------------------------------------- - + @Override @Transactional public void generateCategoryOptionComboNames() { - resourceTableStore.createDataElementCategoryOptionComboName(); - - Collection combos = categoryService.getAllDataElementCategoryCombos(); - - List batchArgs = new ArrayList<>(); - - for ( DataElementCategoryCombo combo : combos ) - { - if ( !combo.isValid() ) - { - log.warn( "Ignoring category combo, not valid: " + combo ); - continue; - } - - for ( DataElementCategoryOptionCombo coc : combo.getSortedOptionCombos() ) - { - List values = new ArrayList<>(); - - values.add( coc.getId() ); - values.add( coc.getName() ); - values.add( coc.isIgnoreApproval() ? APPROVAL_LEVEL_HIGHEST : null ); - - batchArgs.add( values.toArray() ); - } - } - - resourceTableStore.batchUpdate( 3, TABLE_NAME_CATEGORY_OPTION_COMBO_NAME, batchArgs ); - - log.info( "Category option combo name table generated" ); + resourceTableStore.generateResourceTable( new CategoryOptionComboNameResourceTable( + "_categoryoptioncomboname", idObjectManager.getAll( DataElementCategoryCombo.class ), statementBuilder.getColumnQuote() ) ); } @Override === modified file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java' --- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java 2015-09-23 13:58:30 +0000 +++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/jdbc/JdbcResourceTableStore.java 2015-10-02 13:58:22 +0000 @@ -29,10 +29,12 @@ */ import java.util.List; +import java.util.Optional; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hisp.dhis.commons.util.TextUtils; import org.hisp.dhis.dataelement.CategoryOptionGroupSet; import org.hisp.dhis.dataelement.DataElementCategory; import org.hisp.dhis.dataelement.DataElementGroupSet; @@ -47,7 +49,7 @@ import org.hisp.dhis.resourcetable.statement.CreateDataElementGroupSetTableStatement; import org.hisp.dhis.resourcetable.statement.CreateIndicatorGroupSetTableStatement; import org.hisp.dhis.resourcetable.statement.CreateOrganisationUnitGroupSetTableStatement; -import org.hisp.dhis.commons.util.TextUtils; +import org.hisp.dhis.resourcetable.ResourceTable; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; @@ -81,6 +83,65 @@ // ResourceTableStore implementation // ------------------------------------------------------------------------- + public void generateResourceTable( ResourceTable resourceTable ) + { + final String createTableSql = resourceTable.getCreateTempTableStatement(); + final Optional populateTableSql = resourceTable.getPopulateTempTableStatement(); + final Optional> populateTableContent = resourceTable.getPopulateTempTableContent(); + final Optional createIndexSql = resourceTable.getCreateIndexStatement(); + + // --------------------------------------------------------------------- + // Create table + // --------------------------------------------------------------------- + + log.info( "Create table SQL: " + createTableSql ); + + jdbcTemplate.execute( createTableSql ); + + // --------------------------------------------------------------------- + // Populate table + // --------------------------------------------------------------------- + + if ( populateTableSql.isPresent() ) + { + log.info( "Populate table SQL: " + populateTableSql.get() ); + + jdbcTemplate.execute( populateTableSql.get() ); + } + else if ( populateTableContent.isPresent() ) + { + List content = populateTableContent.get(); + + log.info( "Populate table content rows: " + content.size() ); + + if ( content.size() > 0 ) + { + int columns = content.get( 0 ).length; + + batchUpdate( columns, resourceTable.getTempTableName(), content ); + } + } + + // --------------------------------------------------------------------- + // Create index + // --------------------------------------------------------------------- + + if ( createIndexSql.isPresent() ) + { + log.info( "Create index SQL: " + createIndexSql.get() ); + + jdbcTemplate.execute( createIndexSql.get() ); + } + + // --------------------------------------------------------------------- + // Swap tables + // --------------------------------------------------------------------- + + jdbcTemplate.execute( resourceTable.getSwapTablesStatement() ); + + log.info( "Swapped resource table, done: " + resourceTable.getTableName() ); + } + @Override public void batchUpdate( int columns, String tableName, List batchArgs ) { === added directory 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table' === added file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/CategoryOptionComboNameResourceTable.java' --- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/CategoryOptionComboNameResourceTable.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/CategoryOptionComboNameResourceTable.java 2015-10-02 13:58:22 +0000 @@ -0,0 +1,99 @@ +package org.hisp.dhis.resourcetable.table; + +/* + * Copyright (c) 2004-2015, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import static org.hisp.dhis.dataapproval.DataApprovalLevelService.APPROVAL_LEVEL_HIGHEST; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.hisp.dhis.dataelement.DataElementCategoryCombo; +import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; +import org.hisp.dhis.resourcetable.ResourceTable; + +/** + * @author Lars Helge Overland + */ +public class CategoryOptionComboNameResourceTable + extends ResourceTable +{ + public CategoryOptionComboNameResourceTable( String tableName, List objects, String columnQuote ) + { + super( tableName, objects, columnQuote ); + } + + @Override + public String getCreateTempTableStatement() + { + return "create table " + getTempTableName() + + " (categoryoptioncomboid integer not null primary key, " + + "categoryoptioncomboname varchar(255), approvallevel integer)"; + } + + @Override + public Optional getPopulateTempTableStatement() + { + return Optional.empty(); + } + + @Override + public Optional> getPopulateTempTableContent() + { + List batchArgs = new ArrayList<>(); + + for ( DataElementCategoryCombo combo : objects ) + { + if ( !combo.isValid() ) + { + log.warn( "Ignoring category combo, not valid: " + combo ); + continue; + } + + for ( DataElementCategoryOptionCombo coc : combo.getSortedOptionCombos() ) + { + List values = new ArrayList<>(); + + values.add( coc.getId() ); + values.add( coc.getName() ); + values.add( coc.isIgnoreApproval() ? APPROVAL_LEVEL_HIGHEST : null ); + + batchArgs.add( values.toArray() ); + } + } + + return Optional.of( batchArgs ); + } + + @Override + public Optional getCreateIndexStatement() + { + return Optional.empty(); + } +} === added file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/OrganisationUnitStructureResourceTable.java' --- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/OrganisationUnitStructureResourceTable.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/resourcetable/table/OrganisationUnitStructureResourceTable.java 2015-10-02 13:58:22 +0000 @@ -0,0 +1,135 @@ +package org.hisp.dhis.resourcetable.table; + +/* + * Copyright (c) 2004-2015, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.organisationunit.OrganisationUnitService; +import org.hisp.dhis.resourcetable.ResourceTable; + +/** + * @author Lars Helge Overland + */ +public class OrganisationUnitStructureResourceTable + extends ResourceTable +{ + private OrganisationUnitService organisationUnitService; // Nasty + + private int organisationUnitLevels; + + public OrganisationUnitStructureResourceTable( String tableName, List objects, + String columnQuote, OrganisationUnitService organisationUnitService, int organisationUnitLevels ) + { + super( tableName, objects, columnQuote ); + this.organisationUnitService = organisationUnitService; + this.organisationUnitLevels = organisationUnitLevels; + } + + @Override + public String getCreateTempTableStatement() + { + StringBuilder sql = new StringBuilder(); + + sql.append( "create table " ).append( getTempTableName() ). + append( " (organisationunitid integer not null primary key, organisationunituid character(11), level integer" ); + + for ( int k = 1 ; k <= organisationUnitLevels; k++ ) + { + sql.append( ", " ).append( columnQuote ).append( "idlevel" + k ).append( columnQuote ).append (" integer, " ). + append( columnQuote ).append( "uidlevel" + k ).append( columnQuote ).append( " character(11)" ); + } + + return sql.append( ");" ).toString(); + } + + @Override + public Optional getPopulateTempTableStatement() + { + return Optional.empty(); + } + + @Override + public Optional> getPopulateTempTableContent() + { + List batchArgs = new ArrayList<>(); + + for ( int i = 0; i < organisationUnitLevels; i++ ) + { + int level = i + 1; + + Collection units = organisationUnitService.getOrganisationUnitsAtLevel( level ); + + for ( OrganisationUnit unit : units ) + { + List values = new ArrayList<>(); + + values.add( unit.getId() ); + values.add( unit.getUid() ); + values.add( level ); + + Map identifiers = new HashMap<>(); + Map uids = new HashMap<>(); + + for ( int j = level; j > 0; j-- ) + { + identifiers.put( j, unit.getId() ); + uids.put( j, unit.getUid() ); + + unit = unit.getParent(); + } + + for ( int k = 1; k <= organisationUnitLevels; k++ ) + { + values.add( identifiers.get( k ) != null ? identifiers.get( k ) : null ); + values.add( uids.get( k ) ); + } + + batchArgs.add( values.toArray() ); + } + } + + return Optional.of( batchArgs ); + } + + @Override + public Optional getCreateIndexStatement() + { + String name = "in_orgunitstructure_organisationunituid_" + getRandomSuffix(); + + String sql = "create unique index " + name + " on " + getTempTableName() + "(organisationunituid)"; + + return Optional.of( sql ); + } +} === modified file 'dhis-2/dhis-services/dhis-service-administration/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-services/dhis-service-administration/src/main/resources/META-INF/dhis/beans.xml 2015-09-24 07:35:03 +0000 +++ dhis-2/dhis-services/dhis-service-administration/src/main/resources/META-INF/dhis/beans.xml 2015-10-02 13:58:22 +0000 @@ -17,6 +17,7 @@ +