datatable and code value mappings

This commit is contained in:
Pramod Nuthakki
2013-08-31 14:24:21 +05:30
parent 3c5679544b
commit d1e442aa14
12 changed files with 302 additions and 49 deletions
@@ -0,0 +1,11 @@
CREATE TABLE `x_table_cloumn_code_mappings` (
`column_alias_name` VARCHAR(50) NOT NULL,
`code_id` INT(10) NOT NULL,
PRIMARY KEY (`column_alias_name`),
INDEX `FK_x_code_id` (`code_id`),
CONSTRAINT `FK_x_code_id` FOREIGN KEY (`code_id`) REFERENCES `m_code` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('constraint_approach_for_datatables', 1);
@@ -10,7 +10,6 @@ package org.mifosplatform.infrastructure.codes.data;
*/ */
public class CodeData { public class CodeData {
@SuppressWarnings("unused")
private final Long id; private final Long id;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String name; private final String name;
@@ -26,4 +25,8 @@ public class CodeData {
this.name = name; this.name = name;
this.systemDefined = systemDefined; this.systemDefined = systemDefined;
} }
public Long getCodeId(){
return this.id;
}
} }
@@ -14,4 +14,6 @@ public interface CodeReadPlatformService {
Collection<CodeData> retrieveAllCodes(); Collection<CodeData> retrieveAllCodes();
CodeData retrieveCode(Long codeId); CodeData retrieveCode(Long codeId);
CodeData retriveCode(String codeName);
} }
@@ -71,4 +71,18 @@ public class CodeReadPlatformServiceImpl implements CodeReadPlatformService {
throw new CodeNotFoundException(codeId); throw new CodeNotFoundException(codeId);
} }
} }
@Override
public CodeData retriveCode(String codeName) {
try {
context.authenticatedUser();
final CodeMapper rm = new CodeMapper();
final String sql = "select " + rm.schema() + " where c.code_name = ?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { codeName });
} catch (EmptyResultDataAccessException e) {
throw new CodeNotFoundException(codeName);
}
}
} }
@@ -96,8 +96,13 @@ public class CodeWritePlatformServiceJpaRepositoryImpl implements CodeWritePlatf
final Code code = retrieveCodeBy(codeId); final Code code = retrieveCodeBy(codeId);
if (code.isSystemDefined()) { throw new SystemDefinedCodeCannotBeChangedException(); } if (code.isSystemDefined()) { throw new SystemDefinedCodeCannotBeChangedException(); }
this.codeRepository.delete(code); try {
this.codeRepository.delete(code);
this.codeRepository.flush();
} catch (DataIntegrityViolationException e) {
throw new PlatformDataIntegrityException("error.msg.cund.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + e.getMostSpecificCause());
}
return new CommandProcessingResultBuilder().withEntityId(codeId).build(); return new CommandProcessingResultBuilder().withEntityId(codeId).build();
} }
@@ -13,4 +13,5 @@ public interface ConfigurationDomainService {
boolean isRescheduleRepaymentsOnHolidaysEnabled(); boolean isRescheduleRepaymentsOnHolidaysEnabled();
boolean allowTransactionsOnHolidayEnabled(); boolean allowTransactionsOnHolidayEnabled();
boolean allowTransactionsOnNonWorkingDayEnabled(); boolean allowTransactionsOnNonWorkingDayEnabled();
boolean isConstraintApproachEnabledForDatatables();
} }
@@ -80,4 +80,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return property.isEnabled(); return property.isEnabled();
} }
@Override
public boolean isConstraintApproachEnabledForDatatables() {
final String colunnValue = "constraint_approach_for_datatables";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByName(colunnValue);
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(colunnValue); }
return property.isEnabled();
}
} }
@@ -20,7 +20,7 @@ public class PlatformDataIntegrityException extends RuntimeException {
final Object... defaultUserMessageArgs) { final Object... defaultUserMessageArgs) {
this.globalisationMessageCode = globalisationMessageCode; this.globalisationMessageCode = globalisationMessageCode;
this.defaultUserMessage = defaultUserMessage; this.defaultUserMessage = defaultUserMessage;
this.parameterName = ""; this.parameterName = null;
this.defaultUserMessageArgs = defaultUserMessageArgs; this.defaultUserMessageArgs = defaultUserMessageArgs;
} }
@@ -0,0 +1,11 @@
package org.mifosplatform.infrastructure.core.exception;
public class PlatformServiceUnavailableException extends AbstractPlatformServiceUnavailableException {
public PlatformServiceUnavailableException(String globalisationMessageCode, String defaultUserMessage,
Object... defaultUserMessageArgs) {
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -24,6 +24,7 @@ public final class ResultsetColumnHeaderData {
private final boolean isColumnPrimaryKey; private final boolean isColumnPrimaryKey;
private final List<ResultsetColumnValueData> columnValues; private final List<ResultsetColumnValueData> columnValues;
private final String columnCode;
public static ResultsetColumnHeaderData basic(final String columnName, final String columnType) { public static ResultsetColumnHeaderData basic(final String columnName, final String columnType) {
@@ -31,25 +32,31 @@ public final class ResultsetColumnHeaderData {
final boolean columnNullable = false; final boolean columnNullable = false;
final boolean columnIsPrimaryKey = false; final boolean columnIsPrimaryKey = false;
final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>(); final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues); final String columnCode = null;
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues,
columnCode);
} }
public static ResultsetColumnHeaderData detailed(final String columnName, final String columnType, final Long columnLength, public static ResultsetColumnHeaderData detailed(final String columnName, final String columnType, final Long columnLength,
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues) { final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues,
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues); String columnCode) {
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues,
columnCode);
} }
private ResultsetColumnHeaderData(final String columnName, final String columnType, final Long columnLength, private ResultsetColumnHeaderData(final String columnName, final String columnType, final Long columnLength,
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues) { final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues,
String columnCode) {
this.columnName = columnName; this.columnName = columnName;
this.columnType = columnType; this.columnType = columnType;
this.columnLength = columnLength; this.columnLength = columnLength;
this.isColumnNullable = columnNullable; this.isColumnNullable = columnNullable;
this.isColumnPrimaryKey = columnIsPrimaryKey; this.isColumnPrimaryKey = columnIsPrimaryKey;
this.columnValues = columnValues; this.columnValues = columnValues;
this.columnCode = columnCode;
String displayType = null; String displayType = null;
if (this.columnValues.isEmpty()) { if (this.columnCode == null) {
if (isString()) { if (isString()) {
displayType = "STRING"; displayType = "STRING";
} else if (isAnyInteger()) { } else if (isAnyInteger()) {
@@ -228,4 +235,8 @@ public final class ResultsetColumnHeaderData {
} }
return allowed; return allowed;
} }
public String getColumnCode() {
return this.columnCode;
}
} }
@@ -185,14 +185,34 @@ public class GenericDataServiceImpl implements GenericDataService {
boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey); boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey);
List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>(); List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
String codeName = null;
if ("varchar".equalsIgnoreCase(columnType)) { if ("varchar".equalsIgnoreCase(columnType)) {
columnValues = retreiveColumnValues(columnName, "_cv"); int codePosition = columnName.indexOf("_cv");
if (codePosition > 0) {
codeName = columnName.substring(0, codePosition);
columnValues = retreiveColumnValues(codeName);
}
} else if ("int".equalsIgnoreCase(columnType)) { } else if ("int".equalsIgnoreCase(columnType)) {
columnValues = retreiveColumnValues(columnName, "_cd"); int codePosition = columnName.indexOf("_cd");
if (codePosition > 0) {
codeName = columnName.substring(0, codePosition);
columnValues = retreiveColumnValues(codeName);
}
}
if(codeName == null){
final SqlRowSet rsValues = getDatatableCodeData(datatable, columnName);
Integer codeId = null;
while (rsValues.next()) {
codeId = rsValues.getInt("id");
codeName = rsValues.getString("code_name");
}
columnValues = retreiveColumnValues(codeId);
} }
final ResultsetColumnHeaderData rsch = ResultsetColumnHeaderData.detailed(columnName, columnType, columnLength, columnNullable, final ResultsetColumnHeaderData rsch = ResultsetColumnHeaderData.detailed(columnName, columnType, columnLength, columnNullable,
columnIsPrimaryKey, columnValues); columnIsPrimaryKey, columnValues,codeName);
columnHeaders.add(rsch); columnHeaders.add(rsch);
} }
@@ -204,14 +224,10 @@ public class GenericDataServiceImpl implements GenericDataService {
* Candidate for using caching there to get allowed 'column values' from * Candidate for using caching there to get allowed 'column values' from
* code/codevalue tables * code/codevalue tables
*/ */
private List<ResultsetColumnValueData> retreiveColumnValues(final String columnName, final String code_suffix) { private List<ResultsetColumnValueData> retreiveColumnValues(final String codeName) {
final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>(); final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
int codePosition = columnName.indexOf(code_suffix);
if (codePosition > 0) {
final String codeName = columnName.substring(0, codePosition);
final String sql = "select v.id, v.code_value from m_code m " + " join m_code_value v on v.code_id = m.id " final String sql = "select v.id, v.code_value from m_code m " + " join m_code_value v on v.code_id = m.id "
+ " where m.code_name = '" + codeName + "' order by v.order_position, v.id"; + " where m.code_name = '" + codeName + "' order by v.order_position, v.id";
@@ -224,6 +240,23 @@ public class GenericDataServiceImpl implements GenericDataService {
columnValues.add(new ResultsetColumnValueData(id, codeValue)); columnValues.add(new ResultsetColumnValueData(id, codeValue));
} }
return columnValues;
}
private List<ResultsetColumnValueData> retreiveColumnValues(final Integer codeId) {
final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
if (codeId != null) {
final String sql = "select v.id, v.code_value from m_code_value v where v.code_id =" + codeId
+ " order by v.order_position, v.id";
final SqlRowSet rsValues = this.jdbcTemplate.queryForRowSet(sql);
rsValues.beforeFirst();
while (rsValues.next()) {
final Integer id = rsValues.getInt("id");
final String codeValue = rsValues.getString("code_value");
columnValues.add(new ResultsetColumnValueData(id, codeValue));
}
} }
return columnValues; return columnValues;
@@ -240,4 +273,13 @@ public class GenericDataServiceImpl implements GenericDataService {
throw new DatatableNotFoundException(datatable); throw new DatatableNotFoundException(datatable);
} }
private SqlRowSet getDatatableCodeData(final String datatable, String columnName) {
final String sql = "select mc.id,mc.code_name from m_code mc join x_table_cloumn_code_mappings xcc on xcc.code_id = mc.id where xcc.column_alias_name='"
+ datatable.toLowerCase().replaceAll("\\s", "_") + "_" + columnName + "'";
final SqlRowSet rsValues = this.jdbcTemplate.queryForRowSet(sql);
return rsValues;
}
} }
@@ -18,8 +18,11 @@ import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.SQLGrammarException; import org.hibernate.exception.SQLGrammarException;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.service.CodeReadPlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -27,6 +30,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException;
import org.mifosplatform.infrastructure.core.serialization.DatatableCommandFromApiJsonDeserializer; import org.mifosplatform.infrastructure.core.serialization.DatatableCommandFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper; import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
@@ -44,6 +48,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData; import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
@@ -60,6 +65,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$"; private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
private final static String CODE_VALUES_TABLE = "m_code_value";
private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class);
private final static HashMap<String, String> apiTypeToMySQL = new HashMap<String, String>() { private final static HashMap<String, String> apiTypeToMySQL = new HashMap<String, String>() {
@@ -80,11 +87,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private final JsonParserHelper helper; private final JsonParserHelper helper;
private final GenericDataService genericDataService; private final GenericDataService genericDataService;
private final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final ConfigurationDomainService configurationDomainService;
private final CodeReadPlatformService codeReadPlatformService;
@Autowired @Autowired
public ReadWriteNonCoreDataServiceImpl(final RoutingDataSource dataSource, final PlatformSecurityContext context, public ReadWriteNonCoreDataServiceImpl(final RoutingDataSource dataSource, final PlatformSecurityContext context,
final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService, final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService,
final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer) { final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CodeReadPlatformService codeReadPlatformService,
final ConfigurationDomainService configurationDomainService) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource); this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.context = context; this.context = context;
@@ -92,6 +102,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
this.helper = new JsonParserHelper(); this.helper = new JsonParserHelper();
this.genericDataService = genericDataService; this.genericDataService = genericDataService;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.codeReadPlatformService = codeReadPlatformService;
this.configurationDomainService = configurationDomainService;
} }
@Override @Override
@@ -253,7 +265,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'"; final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
String[] sqlArray = { deleteRolePermissionsSql, deletePermissionsSql, deleteRegisteredDatatableSql }; String[] sqlArray = null;
if (configurationDomainService.isConstraintApproachEnabledForDatatables()) {
final String deleteColumnCodeSql = "delete from x_table_cloumn_code_mappings where column_alias_name like'"
+ datatable.toLowerCase().replaceAll("\\s", "_") + "%'";
sqlArray = new String[4];
sqlArray[3] = deleteColumnCodeSql;
} else {
sqlArray = new String[3];
}
sqlArray[0] = deleteRolePermissionsSql;
sqlArray[1] = deletePermissionsSql;
sqlArray[2] = deleteRegisteredDatatableSql;
this.jdbcTemplate.batchUpdate(sqlArray); this.jdbcTemplate.batchUpdate(sqlArray);
} }
@@ -333,7 +357,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
"Validation errors exist.", dataValidationErrors); } "Validation errors exist.", dataValidationErrors); }
} }
private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder) { private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder, StringBuilder constrainBuilder,
String dataTableNameAlias, Map<String, Long> codeMappings, boolean isConstraintApproach) {
String name = (column.has("name")) ? column.get("name").getAsString() : null; String name = (column.has("name")) ? column.get("name").getAsString() : null;
String type = (column.has("type")) ? column.get("type").getAsString() : null; String type = (column.has("type")) ? column.get("type").getAsString() : null;
@@ -342,7 +367,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String code = (column.has("code")) ? column.get("code").getAsString() : null; String code = (column.has("code")) ? column.get("code").getAsString() : null;
if (StringUtils.isNotBlank(code)) { if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code); if (isConstraintApproach) {
codeMappings.put(dataTableNameAlias + "_" + name, codeReadPlatformService.retriveCode(code).getCodeId());
constrainBuilder.append(", CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(name).append("` ")
.append("FOREIGN KEY (`" + name + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE).append("` (`id`)");
} else {
name = datatableColumnNameToCodeValueName(name, code);
}
} }
String mysqlType = apiTypeToMySQL.get(type); String mysqlType = apiTypeToMySQL.get(type);
@@ -399,10 +430,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
validateDatatableName(datatableName); validateDatatableName(datatableName);
validateAppTable(apptableName); validateAppTable(apptableName);
boolean isConstraintApproach = configurationDomainService.isConstraintApproachEnabledForDatatables();
String fkColumnName = apptableName.substring(2) + "_id"; String fkColumnName = apptableName.substring(2) + "_id";
String fkName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + fkColumnName; final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
String fkName = dataTableNameAlias + "_" + fkColumnName;
StringBuilder sqlBuilder = new StringBuilder(); StringBuilder sqlBuilder = new StringBuilder();
StringBuilder constrainBuilder = new StringBuilder();
Map<String, Long> codeMappings = new HashMap<String, Long>();
sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` (");
if (multiRow) { if (multiRow) {
@@ -413,7 +447,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
for (JsonElement column : columns) { for (JsonElement column : columns) {
parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder); parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder, constrainBuilder, dataTableNameAlias,
codeMappings, isConstraintApproach);
} }
// Remove trailing comma and space // Remove trailing comma and space
@@ -429,10 +464,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
.append("FOREIGN KEY (`" + fkColumnName + "`) ").append("REFERENCES `" + actualAppTableName + "` (`id`)"); .append("FOREIGN KEY (`" + fkColumnName + "`) ").append("REFERENCES `" + actualAppTableName + "` (`id`)");
} }
sqlBuilder.append(constrainBuilder);
sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;");
this.jdbcTemplate.execute(sqlBuilder.toString()); this.jdbcTemplate.execute(sqlBuilder.toString());
registerDatatable(datatableName, apptableName); registerDatatable(datatableName, apptableName);
registerColumnCodeMapping(codeMappings);
} catch (SQLGrammarException e) { } catch (SQLGrammarException e) {
Throwable realCause = e.getCause(); Throwable realCause = e.getCause();
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -455,7 +493,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
private void parseDatatableColumnForUpdate(final JsonObject column, private void parseDatatableColumnForUpdate(final JsonObject column,
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, StringBuilder sqlBuilder) { final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, StringBuilder sqlBuilder, String datatableName,
StringBuilder constrainBuilder, Map<String, Long> codeMappings, List<String> removeMappings, boolean isConstraintApproach) {
String name = (column.has("name")) ? column.get("name").getAsString() : null; String name = (column.has("name")) ? column.get("name").getAsString() : null;
String lengthStr = (column.has("length")) ? column.get("length").getAsString() : null; String lengthStr = (column.has("length")) ? column.get("length").getAsString() : null;
@@ -465,20 +504,53 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String after = (column.has("after")) ? column.get("after").getAsString() : null; String after = (column.has("after")) ? column.get("after").getAsString() : null;
String code = (column.has("code")) ? column.get("code").getAsString() : null; String code = (column.has("code")) ? column.get("code").getAsString() : null;
String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null; String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null;
String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
if (isConstraintApproach) {
if (StringUtils.isBlank(newName)) {
newName = name;
}
if (!StringUtils.equalsIgnoreCase(code, newCode) || !StringUtils.equalsIgnoreCase(name, newName)) {
if (StringUtils.equalsIgnoreCase(code, newCode)) {
int codeId = getCodeIdForColumn(dataTableNameAlias, name);
if (codeId > 0) {
removeMappings.add(dataTableNameAlias + "_" + name);
constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(dataTableNameAlias).append("_").append(name).append("` ");
codeMappings.put(dataTableNameAlias + "_" + newName, (long) codeId);
constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(newName)
.append("` ").append("FOREIGN KEY (`" + newName + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE)
.append("` (`id`)");
}
if (StringUtils.isNotBlank(code)) { } else {
name = datatableColumnNameToCodeValueName(name, code); if (code != null) {
removeMappings.add(dataTableNameAlias + "_" + name);
if (StringUtils.isNotBlank(newCode)) { if (newCode == null) {
newName = datatableColumnNameToCodeValueName(newName, newCode); constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(dataTableNameAlias).append("_").append(name)
} else { .append("` ");
newName = datatableColumnNameToCodeValueName(newName, code); }
}
if (newCode != null) {
codeMappings.put(dataTableNameAlias + "_" + newName, codeReadPlatformService.retriveCode(newCode).getCodeId());
if (code == null) {
constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(newName)
.append("` ").append("FOREIGN KEY (`" + newName + "`) ").append("REFERENCES `")
.append(CODE_VALUES_TABLE).append("` (`id`)");
}
}
}
}
} else {
if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code);
if (StringUtils.isNotBlank(newCode)) {
newName = datatableColumnNameToCodeValueName(newName, newCode);
} else {
newName = datatableColumnNameToCodeValueName(newName, code);
}
} }
} }
if (!mapColumnNameDefinition.containsKey(name)) { throw new PlatformDataIntegrityException( if (!mapColumnNameDefinition.containsKey(name)) { throw new PlatformDataIntegrityException(
"error.msg.datatable.column.missing.update.parse", "Column " + name + " does not exist.", name); } "error.msg.datatable.column.missing.update.parse", "Column " + name + " does not exist.", name); }
String type = mapColumnNameDefinition.get(name).getColumnType(); String type = mapColumnNameDefinition.get(name).getColumnType();
if (length == null && type.toLowerCase().equals("varchar")) { if (length == null && type.toLowerCase().equals("varchar")) {
length = mapColumnNameDefinition.get(name).getColumnLength().intValue(); length = mapColumnNameDefinition.get(name).getColumnLength().intValue();
@@ -505,7 +577,22 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
} }
private void parseDatatableColumnForAdd(final JsonObject column, StringBuilder sqlBuilder) { private int getCodeIdForColumn(String dataTableNameAlias, String name) {
StringBuilder checkColumnCodeMapping = new StringBuilder();
checkColumnCodeMapping.append("select ccm.code_id from x_table_cloumn_code_mappings ccm where ccm.column_alias_name='")
.append(dataTableNameAlias).append("_").append(name).append("'");
int codeId = 0;
try {
codeId = this.jdbcTemplate.queryForInt(checkColumnCodeMapping.toString());
} catch (EmptyResultDataAccessException e) {
logger.info(e.getMessage());
}
return codeId;
}
private void parseDatatableColumnForAdd(final JsonObject column, StringBuilder sqlBuilder, String dataTableNameAlias,
StringBuilder constrainBuilder, Map<String, Long> codeMappings, boolean isConstraintApproach) {
String name = (column.has("name")) ? column.get("name").getAsString() : null; String name = (column.has("name")) ? column.get("name").getAsString() : null;
String type = (column.has("type")) ? column.get("type").getAsString() : null; String type = (column.has("type")) ? column.get("type").getAsString() : null;
@@ -515,7 +602,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String code = (column.has("code")) ? column.get("code").getAsString() : null; String code = (column.has("code")) ? column.get("code").getAsString() : null;
if (StringUtils.isNotBlank(code)) { if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code); if (isConstraintApproach) {
codeMappings.put(dataTableNameAlias + "_" + name, codeReadPlatformService.retriveCode(code).getCodeId());
constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(name).append("` ")
.append("FOREIGN KEY (`" + name + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE).append("` (`id`)");
} else {
name = datatableColumnNameToCodeValueName(name, code);
}
} }
String mysqlType = apiTypeToMySQL.get(type); String mysqlType = apiTypeToMySQL.get(type);
@@ -542,11 +635,47 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
} }
private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) { private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder, String datatableName,
StringBuilder constrainBuilder, List<String> codeMappings) {
String datatableAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
String name = (column.has("name")) ? column.get("name").getAsString() : null; String name = (column.has("name")) ? column.get("name").getAsString() : null;
sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`");
StringBuilder findFKSql = new StringBuilder();
findFKSql.append("SELECT count(*)").append("FROM information_schema.TABLE_CONSTRAINTS i")
.append(" WHERE i.CONSTRAINT_TYPE = 'FOREIGN KEY'").append(" AND i.TABLE_SCHEMA = DATABASE()")
.append(" AND i.TABLE_NAME = '").append(datatableName).append("' AND i.CONSTRAINT_NAME = 'fk_").append(datatableAlias)
.append("_").append(name).append("' ");
int count = this.jdbcTemplate.queryForInt(findFKSql.toString());
if (count > 0) {
codeMappings.add(datatableAlias + "_" + name);
constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(datatableAlias).append("_").append(name).append("` ");
}
}
private void registerColumnCodeMapping(Map<String, Long> codeMappings) {
if (codeMappings != null && !codeMappings.isEmpty()) {
String[] addSqlList = new String[codeMappings.size()];
int i = 0;
for (Map.Entry<String, Long> mapEntry : codeMappings.entrySet()) {
addSqlList[i++] = "insert into x_table_cloumn_code_mappings (column_alias_name, code_id) values ('" + mapEntry.getKey()
+ "'," + mapEntry.getValue() + ");";
}
this.jdbcTemplate.batchUpdate(addSqlList);
}
}
private void deleteColumnCodeMapping(List<String> columnNames) {
if (columnNames != null && !columnNames.isEmpty()) {
String[] deleteSqlList = new String[columnNames.size()];
int i = 0;
for (String columnName : columnNames) {
deleteSqlList[i++] = "DELETE FROM x_table_cloumn_code_mappings WHERE column_alias_name='" + columnName + "';";
}
this.jdbcTemplate.batchUpdate(deleteSqlList);
}
} }
@Transactional @Transactional
@@ -571,6 +700,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader); mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader);
} }
boolean isConstraintApproach = configurationDomainService.isConstraintApproachEnabledForDatatables();
if (!StringUtils.isBlank(apptableName)) { if (!StringUtils.isBlank(apptableName)) {
validateAppTable(apptableName); validateAppTable(apptableName);
@@ -610,9 +741,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (dropColumns != null) { if (dropColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
StringBuilder constrainBuilder = new StringBuilder();
List<String> codeMappings = new ArrayList<String>();
for (JsonElement column : dropColumns) { for (JsonElement column : dropColumns) {
parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder); parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder, datatableName, constrainBuilder, codeMappings);
} }
// Remove the first comma, right after ALTER TABLE `datatable` // Remove the first comma, right after ALTER TABLE `datatable`
@@ -620,15 +752,18 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (indexOfFirstComma != -1) { if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
} }
sqlBuilder.append(constrainBuilder);
this.jdbcTemplate.execute(sqlBuilder.toString()); this.jdbcTemplate.execute(sqlBuilder.toString());
deleteColumnCodeMapping(codeMappings);
} }
if (addColumns != null) { if (addColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
StringBuilder constrainBuilder = new StringBuilder();
Map<String, Long> codeMappings = new HashMap<String, Long>();
for (JsonElement column : addColumns) { for (JsonElement column : addColumns) {
parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder); parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder, datatableName.toLowerCase().replaceAll("\\s", "_"),
constrainBuilder, codeMappings, isConstraintApproach);
} }
// Remove the first comma, right after ALTER TABLE `datatable` // Remove the first comma, right after ALTER TABLE `datatable`
@@ -636,15 +771,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (indexOfFirstComma != -1) { if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
} }
sqlBuilder.append(constrainBuilder);
this.jdbcTemplate.execute(sqlBuilder.toString()); this.jdbcTemplate.execute(sqlBuilder.toString());
registerColumnCodeMapping(codeMappings);
} }
if (changeColumns != null) { if (changeColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
StringBuilder constrainBuilder = new StringBuilder();
Map<String, Long> codeMappings = new HashMap<String, Long>();
List<String> removeMappings = new ArrayList<String>();
for (JsonElement column : changeColumns) { for (JsonElement column : changeColumns) {
parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder); parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder, datatableName,
constrainBuilder, codeMappings, removeMappings, isConstraintApproach);
} }
// Remove the first comma, right after ALTER TABLE `datatable` // Remove the first comma, right after ALTER TABLE `datatable`
@@ -652,8 +791,15 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (indexOfFirstComma != -1) { if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
} }
sqlBuilder.append(constrainBuilder);
this.jdbcTemplate.execute(sqlBuilder.toString()); try {
this.jdbcTemplate.execute(sqlBuilder.toString());
deleteColumnCodeMapping(removeMappings);
registerColumnCodeMapping(codeMappings);
} catch (GenericJDBCException e) {
if (e.getMessage().contains("Error on rename")) { throw new PlatformServiceUnavailableException(
"error.msg.datatable.column.update.not.allowed", "One of the column name modification not allowed"); }
}
} }
} catch (SQLGrammarException e) { } catch (SQLGrammarException e) {
Throwable realCause = e.getCause(); Throwable realCause = e.getCause();