Compare commits

...
Sign in to create a new pull request.

1 commit

10 changed files with 300 additions and 37 deletions

View file

@ -65,6 +65,12 @@
<version>${external_libraries.hazelcast.version}</version> <version>${external_libraries.hazelcast.version}</version>
</dependency> </dependency>
<!-- TEST -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>jar/${project.artifactId}</finalName> <finalName>jar/${project.artifactId}</finalName>

View file

@ -0,0 +1,7 @@
package ru.spcex.clearing.imdg.base;
public interface AutoconfiguredMap {
String getMapName();
String[] getIndexingField();
}

View file

@ -0,0 +1,89 @@
package ru.spcex.clearing.imdg.base;
import org.springframework.jdbc.core.JdbcTemplate;
import ru.spcex.clearing.imdg.utils.DbUtilsHelper;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
/**
* MapStore для шаблонных бизнес-объектов
* @Component
*/
public abstract class TemplateMapStore<T extends SpcexObjectBase> extends ObjectBaseMapStore<T> implements AutoconfiguredMap {
protected final int validateSize = getFields().length;
protected final String insertStatement = DbUtilsHelper.createUpdateOrInsert(getTableName(), getFields(), "id");
public TemplateMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
// @Override
// public String getTableName() {
// return "COMPANY_ROLE_SET";
// }
//
// @Override
// public String[] getFields() {
// return new String[]{"id", "roleid", "companyid"};
// }
// //todo TradingDay флаг
// protected boolean useTradingDay() {
// return false;
// }
/**
*
* @return IMDGDistributedNames.*
*/
public abstract String getMapName();
/**
* Список индексируемых полей, для быстрого поиска
* @return
*/
public String[] getIndexingField() {
return null;
}
/**
* Десериализатор
* @param resultSet
* @return
*/
protected abstract T objectReader(ResultSet resultSet) throws SQLException;
@Override
public Collection<T> load(Collection<Long> keys) {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys);
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap,
(resultSet, i) -> objectReader(resultSet));
}
/**
* Сериализатор
* @param resultSet
* @return Object[] args
*/
protected abstract Object[] objectToField(T resultSet);
@Override
public void store(Map<Long, T> map) {
List<Object[]> batchArgs = new ArrayList<>();
for (Map.Entry<Long, T> entry : map.entrySet()) {
T obj = entry.getValue();
Object[] args = objectToField(obj);
if (args.length != validateSize) {
throw new IllegalArgumentException("objectToField return " + args.length + " arguments, but expected " + validateSize);
}
batchArgs.add(args);
}
batchInsertUpdate(insertStatement, batchArgs);
}
}

View file

@ -0,0 +1,87 @@
package ru.spcex.clearing.imdg.base;
import org.springframework.jdbc.core.JdbcTemplate;
import ru.clearing.classes.objects.BusinessEvent;
import ru.clearing.classes.objects.BusinessObject;
import ru.spcex.clearing.imdg.utils.DbUtilsHelper;
import ru.spcex.clearing.imdg.utils.TimeUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
//@Component
public abstract class TemplateUpdateMapStore<T extends BusinessObject, TU extends BusinessEvent<T>> extends BusinessEventMapStore<TU> {
protected final int validateSize = getFields().length;
protected final String insertStatement = DbUtilsHelper.createUpdateOrInsert(getTableName(), getFields(), "id");
public TemplateUpdateMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
// @Override
// public String getTableName() {
// return "COMPANY_UPDATE";
// }
//
// @Override
// public String[] getFields() {
// return new String[]{
// "id", "event_time", "event_user_id",
// "company_id", "created_at", "updated_at", "status", "clearing_code", "exchange_code"
// };
// }
/**
* Сериализатор
* @param object
* @return Object[] args
*/
protected abstract Object[] objectToField(T object);
protected Object[] objectToField(TU updateObject, T object) {
ArrayList<Object> lst=new ArrayList<>();
lst.add(updateObject.getId());
lst.add(TimeUtil.fromInstant(updateObject.getEventTime()));
lst.add(updateObject.getUserId());
lst.addAll(Arrays.asList(objectToField(object)));
//todo order?
// new Object[]{
// companyUpdate,
// ,
// companyUpdate.getUserId(),
// new Object[]{
// companyUpdate.getId(),
// TimeUtil.fromInstant(companyUpdate.getEventTime()),
// companyUpdate.getUserId(),
//
// company.getId(), // company_id
// TimeUtil.fromInstant(company.getCreated()),
// TimeUtil.fromInstant(company.getUpdated()),
// company.getStatusId(),
// company.getClearingCode(),
// company.getExchangeCode()
// };
return lst.toArray();
}
@Override
public void store(Map<Long, TU> map) {
List<Object[]> batchArgs = new ArrayList<>();
for (Map.Entry<Long, TU> entry : map.entrySet()) {
TU updateContainer = entry.getValue();
T obj = updateContainer.getObject();
Object[] args = objectToField(updateContainer, obj);
if (args.length != validateSize) {
throw new IllegalArgumentException("objectToField return " + args.length + " arguments, but expected " + validateSize);
}
batchArgs.add(args);
}
batchInsertUpdate(insertStatement, batchArgs);
}
}

View file

@ -7,6 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration @Configuration
public class HazelcastConfiguration { public class HazelcastConfiguration {
@ -53,13 +55,19 @@ public class HazelcastConfiguration {
config.addMapConfig(poolMapConfigs.map_OrganizationTypeDictionary()); config.addMapConfig(poolMapConfigs.map_OrganizationTypeDictionary());
config.addMapConfig(poolMapConfigs.map_WorkflowStatusDictionary()); config.addMapConfig(poolMapConfigs.map_WorkflowStatusDictionary());
config.addMapConfig(poolMapConfigs.map_CompanyRoleSet()); // config.addMapConfig(poolMapConfigs.map_CompanyRoleSet()); todo протестировать автоконфигуратор конфигурации мапсторов
config.addMapConfig(poolMapConfigs.map_CompanySymbols()); config.addMapConfig(poolMapConfigs.map_CompanySymbols());
config.addMapConfig(poolMapConfigs.map_ProfileDocument()); config.addMapConfig(poolMapConfigs.map_ProfileDocument());
config.addMapConfig(poolMapConfigs.map_Contact()); config.addMapConfig(poolMapConfigs.map_Contact());
config.addMapConfig(poolMapConfigs.map_Company()); config.addMapConfig(poolMapConfigs.map_Company());
// config.addMapConfig(poolMapConfigs.map_CompanyUpdate()); // config.addMapConfig(poolMapConfigs.map_CompanyUpdate());
// Автоподключение мап
List<MapConfig> autoMapCfg = poolMapConfigs.autoconfiguratorOfMapstorages();
for (MapConfig cfg: autoMapCfg) {
config.addMapConfig(cfg);
}
return config; return config;
} }

View file

@ -2,8 +2,13 @@ package ru.spcex.clearing.imdg.config;
import com.hazelcast.config.*; import com.hazelcast.config.*;
import com.hazelcast.core.MapLoader; import com.hazelcast.core.MapLoader;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import ru.spcex.clearing.imdg.base.TemplateMapStore;
import ru.spcex.clearing.imdg.businessevent.CompanyUpdateMapStore; import ru.spcex.clearing.imdg.businessevent.CompanyUpdateMapStore;
import ru.spcex.clearing.imdg.businessobject.CompanyMapStore; import ru.spcex.clearing.imdg.businessobject.CompanyMapStore;
import ru.spcex.clearing.imdg.dictionary.*; import ru.spcex.clearing.imdg.dictionary.*;
@ -13,8 +18,14 @@ import ru.spcex.clearing.imdg.object.ContactMapStore;
import ru.spcex.clearing.imdg.object.ProfileDocumentMapStore; import ru.spcex.clearing.imdg.object.ProfileDocumentMapStore;
import ru.spcex.clearing.imdg.utils.IMDGDistributedNames; import ru.spcex.clearing.imdg.utils.IMDGDistributedNames;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@Configuration @Configuration
public class PoolMapConfigs { public class PoolMapConfigs {
private Logger log = LoggerFactory.getLogger(getClass());
private ConfigurationRootElement configRoot = DfaConfig.get().getRoot(); private ConfigurationRootElement configRoot = DfaConfig.get().getRoot();
@ -127,12 +138,43 @@ public class PoolMapConfigs {
@Autowired // @Autowired todo эксперимент с мапстором, см. ниже autoconfiguratorOfMapstorage.
private CompanyRoleSetMapStore companyRoleSetMapStore; // private CompanyRoleSetMapStore companyRoleSetMapStore;
//
// public MapConfig map_CompanyRoleSet() {
// return makeDefaultMapConfig(IMDGDistributedNames.Map_CompanyRoleSet, companyRoleSetMapStore)
// .addMapIndexConfig(makeMapIndexConfig("companyId"));
// }
public MapConfig map_CompanyRoleSet() { @Autowired
return makeDefaultMapConfig(IMDGDistributedNames.Map_CompanyRoleSet, companyRoleSetMapStore) private List<TemplateMapStore<?>> listOfAutopluginStores;
.addMapIndexConfig(makeMapIndexConfig("companyId"));
// @Qualifier("AutoconfiguredMapStore")
public List<MapConfig> autoconfiguratorOfMapstorages() {
List<MapConfig> out = new ArrayList<>();
HashSet<String> existMapStores = new HashSet<>();
for (TemplateMapStore<?> mapStore : listOfAutopluginStores) {
log.debug("Link mapstore {} to map {}", mapStore.toString(), mapStore.getMapName());
if (StringUtils.isEmpty(mapStore.getMapName())) {
throw new IllegalArgumentException("MapStore " + mapStore + " has empty mapName");
}
if (existMapStores.contains(mapStore.getMapName())) {
throw new IllegalArgumentException("MapStore " + mapStore + " has wrong mapName=\"" + mapStore.getMapName() + "\" is duplicated");
}
existMapStores.add(mapStore.getMapName()); // IMDGDistributedNames
MapConfig mapCfg = makeDefaultMapConfig(mapStore.getMapName(), mapStore);
if (mapStore.getIndexingField() != null && mapStore.getIndexingField().length > 0) {
if (log.isTraceEnabled()) {
log.trace("Create indexing filed on {}: {}", mapStore.getMapName(), Arrays.toString(mapStore.getIndexingField()));
}
for (String indexName : mapStore.getIndexingField()) {
mapCfg.addMapIndexConfig(makeMapIndexConfig(indexName));
}
}
out.add(mapCfg);
}
log.debug("Configured {} mapStore's", out.size());
return out;
} }

View file

@ -4,12 +4,16 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.clearing.classes.StaticData.Company.CompanyRoleSet; import ru.clearing.classes.StaticData.Company.CompanyRoleSet;
import ru.spcex.clearing.imdg.base.ObjectBaseMapStore; import ru.spcex.clearing.imdg.base.ObjectBaseMapStore;
import ru.spcex.clearing.imdg.base.TemplateMapStore;
import ru.spcex.clearing.imdg.utils.DbUtilsHelper; import ru.spcex.clearing.imdg.utils.DbUtilsHelper;
import ru.spcex.clearing.imdg.utils.IMDGDistributedNames;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*; import java.util.*;
@Component @Component
public class CompanyRoleSetMapStore extends ObjectBaseMapStore<CompanyRoleSet> { public class CompanyRoleSetMapStore extends TemplateMapStore<CompanyRoleSet> {
public CompanyRoleSetMapStore(JdbcTemplate jdbcTemplate) { public CompanyRoleSetMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate); super(jdbcTemplate);
@ -20,41 +24,39 @@ public class CompanyRoleSetMapStore extends ObjectBaseMapStore<CompanyRoleSet> {
return "COMPANY_ROLE_SET"; return "COMPANY_ROLE_SET";
} }
@Override
public String getMapName() {
return IMDGDistributedNames.Map_CompanyRoleSet;
}
@Override
public String[] getIndexingField() {
return new String[]{};// список индексируемых полей
}
// IMDGDistributedNames.Map_CompanyRoleSet
@Override @Override
public String[] getFields() { public String[] getFields() {
return new String[]{"id", "roleid", "companyid"}; return new String[]{"id", "roleid", "companyid"};
} }
protected final String insertStatement = DbUtilsHelper.createUpdateOrInsert(getTableName(), getFields(), "id");
@Override @Override
public Collection<CompanyRoleSet> load(Collection<Long> keys) { protected CompanyRoleSet objectReader(ResultSet resultSet) throws SQLException {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys); CompanyRoleSet companyRoleSet = new CompanyRoleSet();
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap, companyRoleSet.setId(resultSet.getObject("id", Long.class));
(resultSet, i) -> { companyRoleSet.setRoleId(resultSet.getObject("roleid", Long.class));
CompanyRoleSet companyRoleSet = new CompanyRoleSet(); companyRoleSet.setCompanyId(resultSet.getObject("companyid", Long.class));
companyRoleSet.setId(resultSet.getObject("id", Long.class)); return companyRoleSet;
companyRoleSet.setRoleId(resultSet.getObject("roleid", Long.class));
companyRoleSet.setCompanyId(resultSet.getObject("companyid", Long.class));
return companyRoleSet;
});
} }
@Override @Override
public void store(Map<Long, CompanyRoleSet> map) { protected Object[] objectToField(CompanyRoleSet partnerList) {
List<Object[]> batchArgs = new ArrayList<>(); Object[] args = new Object[]{
partnerList.getId(),
for (Map.Entry<Long, CompanyRoleSet> entry : map.entrySet()) { partnerList.getRoleId(),
CompanyRoleSet partnerList = entry.getValue(); partnerList.getCompanyId(),
};
Object[] args = new Object[]{ return args;
partnerList.getId(),
partnerList.getRoleId(),
partnerList.getCompanyId(),
};
batchArgs.add(args);
}
batchInsertUpdate(insertStatement, batchArgs);
} }
} }

View file

@ -42,10 +42,9 @@ public class DbUtilsHelper {
" (" + Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ")) + ") values (" + " (" + Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ")) + ") values (" +
// Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ", ":", "")); // Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ", ":", ""));
String.join(", ", Collections.nCopies(fields.length, "?")) + String.join(", ", Collections.nCopies(fields.length, "?")) +
") WHERE "; ") ON CONFLICT ";
if (matchingKey != null) // PostgreSQL conflict_target fixme проверить мэтчинг updateOrInsert += "(" + matchingKey.toUpperCase()+")";
updateOrInsert += " " + matchingKey.toUpperCase(); updateOrInsert += " DO UPDATE SET " + Arrays.stream(fields).filter(f->!matchingKey.equalsIgnoreCase(f)).map(f -> "\"" + f.toUpperCase() + "\"=EXCLUDED.\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", "));
updateOrInsert += " CONFLICT DO UPDATE";
return updateOrInsert; return updateOrInsert;
} }

View file

@ -0,0 +1,15 @@
package ru.spcex.clearing.imdg.utils;
import static org.testng.Assert.*;
public class DbUtilsHelperTest {
@org.testng.annotations.Test
public void testCreateUpdateOrInsert() {
String sql = DbUtilsHelper.createUpdateOrInsert("ENERGY",
new String[]{"id", "power", "circle", "of", "fantasy", "prime"}, "id");
assertEquals(sql,
"INSERT INTO ENERGY (\"ID\", \"POWER\", \"CIRCLE\", \"OF\", \"FANTASY\", \"PRIME\") values (?, ?, ?, ?, ?, ?) ON CONFLICT (ID) DO UPDATE SET \"POWER\"=EXCLUDED.\"POWER\", \"CIRCLE\"=EXCLUDED.\"CIRCLE\", \"OF\"=EXCLUDED.\"OF\", \"FANTASY\"=EXCLUDED.\"FANTASY\", \"PRIME\"=EXCLUDED.\"PRIME\""
);
}
}

View file

@ -89,6 +89,14 @@
<artifactId>c3p0</artifactId> <artifactId>c3p0</artifactId>
<version>0.9.5.2</version> <version>0.9.5.2</version>
</dependency> </dependency>
<!-- TEST -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.5</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>