Rychlý start: Vytvoření aplikace API pro tabulky pomocí sady Java SDK a Azure Cosmos DB
PLATÍ PRO: Stůl
V tomto rychlém startu se dozvíte, jak získat přístup k rozhraní API tabulek Azure Cosmos DB z aplikace v Javě. Rozhraní API pro tabulky Azure Cosmos DB je úložiště dat bez schématu, které umožňuje aplikacím ukládat strukturovaná data NoSQL v cloudu. Vzhledem k tomu, že data jsou uložená v návrhu bez schématu, při přidání objektu s novým atributem do tabulky se do tabulky automaticky přidají nové vlastnosti (sloupce).
Aplikace v Javě mají přístup k rozhraní API tabulek Azure Cosmos DB pomocí klientské knihovny azure-data-tables .
Požadavky
Ukázková aplikace je napsaná ve Spring Bootu 2.6.4. Jako integrované vývojové prostředí můžete použít Visual Studio Code nebo IntelliJ IDEA .
Pokud ještě nemáte předplatné Azure, vytvořte si bezplatný účet před tím, než začnete.
Ukázková aplikace
Ukázková aplikace pro tento kurz může být naklonována nebo stažena z úložiště https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-java. Součástí ukázkového úložiště je úvodní i dokončená aplikace.
git clone https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-java
Ukázková aplikace používá jako příklad data o počasí k předvedení možností rozhraní API pro tabulky. Objekty představující pozorování počasí se ukládají a načítají pomocí rozhraní API pro tabulku, včetně ukládání objektů s dalšími vlastnostmi, které demonstrují možnosti bez schématu rozhraní API pro tabulky.
1. Vytvoření účtu služby Azure Cosmos DB
Nejprve musíte vytvořit účet rozhraní API pro tabulky azure Cosmos DB, který bude obsahovat tabulky použité ve vaší aplikaci. Můžete to provést pomocí webu Azure Portal, Azure CLI nebo Azure PowerShellu.
Přihlaste se k webu Azure Portal a následujícím postupem vytvořte účet služby Azure Cosmos DB.
2. Vytvoření tabulky
Dále musíte vytvořit tabulku v rámci účtu služby Azure Cosmos DB, kterou bude vaše aplikace používat. Na rozdíl od tradiční databáze stačí zadat pouze název tabulky, nikoli vlastnosti (sloupce) v tabulce. Při načítání dat do tabulky se vlastnosti (sloupce) automaticky vytvoří podle potřeby.
Na webu Azure Portal proveďte následující kroky a vytvořte tabulku uvnitř účtu služby Azure Cosmos DB.
3. Získání služby Azure Cosmos DB připojovací řetězec
Pro přístup k tabulce ve službě Azure Cosmos DB bude vaše aplikace potřebovat tabulku připojovací řetězec pro účet služby CosmosDB Storage. Připojovací řetězec je možné načíst pomocí webu Azure Portal, Azure CLI nebo Azure PowerShellu.
Připojovací řetězec pro váš účet služby Azure Cosmos DB se považuje za tajný klíč aplikace a musí být chráněný stejně jako jakýkoli jiný tajný klíč aplikace nebo heslo. Tento příklad používá POM k uložení připojovací řetězec během vývoje a zpřístupnění aplikace.
<profiles>
<profile>
<id>local</id>
<properties>
<azure.tables.connection.string>
<![CDATA[YOUR-DATA-TABLES-SERVICE-CONNECTION-STRING]]>
</azure.tables.connection.string>
<azure.tables.tableName>WeatherData</azure.tables.tableName>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
4. Zahrnutí balíčku azure-data-tables
Pokud chcete získat přístup k rozhraní API tabulek Azure Cosmos DB z aplikace v Javě, zahrňte balíček azure-data-tables .
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-data-tables</artifactId>
<version>12.2.1</version>
</dependency>
5. Konfigurace klienta tabulky v TableServiceConfig.java
Sada Azure SDK komunikuje s Azure pomocí klientských objektů ke spouštění různých operací v Azure. Objekt TableClient je objekt používaný ke komunikaci s rozhraním Table API služby Azure Cosmos DB.
Aplikace obvykle vytvoří jeden objekt TableClient pro každou tabulku, která se použije v celé aplikaci. Doporučuje se označit, že metoda vytvoří objekt TableClient bean, který se bude spravovat kontejnerem Spring, a jako jednoúčelové provedení tohoto postupu.
TableServiceConfig.java
V souboru aplikace upravte metodu tableClientConfiguration()
tak, aby odpovídala následujícímu fragmentu kódu:
@Configuration
public class TableServiceConfiguration {
private static String TABLE_NAME;
private static String CONNECTION_STRING;
@Value("${azure.tables.connection.string}")
public void setConnectionStringStatic(String connectionString) {
TableServiceConfiguration.CONNECTION_STRING = connectionString;
}
@Value("${azure.tables.tableName}")
public void setTableNameStatic(String tableName) {
TableServiceConfiguration.TABLE_NAME = tableName;
}
@Bean
public TableClient tableClientConfiguration() {
return new TableClientBuilder()
.connectionString(CONNECTION_STRING)
.tableName(TABLE_NAME)
.buildClient();
}
}
V horní části TableServiceConfig.java
souboru budete také muset přidat následující příkaz using.
import com.azure.data.tables.TableClient;
import com.azure.data.tables.TableClientBuilder;
6. Implementace operací tabulek Azure Cosmos DB
Všechny operace tabulek Azure Cosmos DB pro ukázkovou aplikaci se implementují ve TablesServiceImpl
třídě umístěné v adresáři Služeb . Budete muset importovat com.azure.data.tables
balíček sady SDK.
import com.azure.data.tables.TableClient;
import com.azure.data.tables.models.ListEntitiesOptions;
import com.azure.data.tables.models.TableEntity;
import com.azure.data.tables.models.TableTransactionAction;
import com.azure.data.tables.models.TableTransactionActionType;
Na začátku TableServiceImpl
třídy přidejte členovou proměnnou objektu TableClient a konstruktor, který objektu TableClient umožní vložení objektu TableClient do třídy.
@Autowired
private TableClient tableClient;
Získání řádků z tabulky
Třída TableClient obsahuje metodu s názvem listEntities , která umožňuje vybrat řádky z tabulky. V tomto příkladu, protože metodě nejsou předány žádné parametry, budou vybrány všechny řádky z tabulky.
Metoda také přebírá obecný parametr typu TableEntity , který určuje data třídy modelu budou vrácena jako. V tomto případě se používá integrovaná třída TableEntity , což znamená, že listEntities
metoda vrátí PagedIterable<TableEntity>
kolekci jako její výsledky.
public List<WeatherDataModel> retrieveAllEntities() {
List<WeatherDataModel> modelList = tableClient.listEntities().stream()
.map(WeatherDataUtils::mapTableEntityToWeatherDataModel)
.collect(Collectors.toList());
return Collections.unmodifiableList(WeatherDataUtils.filledValue(modelList));
}
Třída TableEntity definovaná v com.azure.data.tables.models
balíčku má vlastnosti pro klíč oddílu a hodnoty klíče řádku v tabulce. Společně tyto dvě hodnoty pro jedinečný klíč pro řádek v tabulce. V této ukázkové aplikaci je název meteorologické stanice (město) uložen v klíči oddílu a datum a čas pozorování je uložen v klíči řádku. Všechny ostatní vlastnosti (teplota, vlhkost, rychlost větru) jsou uloženy ve slovníku v objektu TableEntity
.
Běžně se namapuje objekt TableEntity na objekt vlastní definice. Ukázková aplikace pro tento účel definuje třídu WeatherDataModel
v adresáři Models . Tato třída má vlastnosti pro název stanice a datum pozorování, na které se klíč oddílu a klíč řádku mapuje, a poskytuje smysluplnější názvy vlastností pro tyto hodnoty. Pak pomocí slovníku uloží všechny ostatní vlastnosti objektu. Jedná se o běžný vzor při práci s úložištěm tabulek, protože řádek může mít libovolný počet vlastností a chceme, aby objekty modelu mohly zachytit všechny. Tato třída také obsahuje metody pro výpis vlastností třídy.
public class WeatherDataModel {
public WeatherDataModel(String stationName, String observationDate, OffsetDateTime timestamp, String etag) {
this.stationName = stationName;
this.observationDate = observationDate;
this.timestamp = timestamp;
this.etag = etag;
}
private String stationName;
private String observationDate;
private OffsetDateTime timestamp;
private String etag;
private Map<String, Object> propertyMap = new HashMap<String, Object>();
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public String getObservationDate() {
return observationDate;
}
public void setObservationDate(String observationDate) {
this.observationDate = observationDate;
}
public OffsetDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(OffsetDateTime timestamp) {
this.timestamp = timestamp;
}
public String getEtag() {
return etag;
}
public void setEtag(String etag) {
this.etag = etag;
}
public Map<String, Object> getPropertyMap() {
return propertyMap;
}
public void setPropertyMap(Map<String, Object> propertyMap) {
this.propertyMap = propertyMap;
}
}
Metoda mapTableEntityToWeatherDataModel
se používá k mapování objektu TableEntity na WeatherDataModel
objekt. Metoda mapTableEntityToWeatherDataModel
přímo mapuje PartitionKey
, , RowKey
Timestamp
a vlastnosti a Etag
pak používá properties.keySet
k iteraci nad ostatními vlastnostmi v objektu TableEntity
a mapovat je na WeatherDataModel
objekt, minus vlastnosti, které již byly mapovány přímo.
Upravte kód v mapTableEntityToWeatherDataModel
metodě tak, aby odpovídal následujícímu bloku kódu.
public static WeatherDataModel mapTableEntityToWeatherDataModel(TableEntity entity) {
WeatherDataModel observation = new WeatherDataModel(
entity.getPartitionKey(), entity.getRowKey(),
entity.getTimestamp(), entity.getETag());
rearrangeEntityProperties(observation.getPropertyMap(), entity.getProperties());
return observation;
}
private static void rearrangeEntityProperties(Map<String, Object> target, Map<String, Object> source) {
Constants.DEFAULT_LIST_OF_KEYS.forEach(key -> {
if (source.containsKey(key)) {
target.put(key, source.get(key));
}
});
source.keySet().forEach(key -> {
if (Constants.DEFAULT_LIST_OF_KEYS.parallelStream().noneMatch(defaultKey -> defaultKey.equals(key))
&& Constants.EXCLUDE_TABLE_ENTITY_KEYS.parallelStream().noneMatch(defaultKey -> defaultKey.equals(key))) {
target.put(key, source.get(key));
}
});
}
Filtrování řádků vrácených z tabulky
Chcete-li filtrovat řádky vrácené z tabulky, můžete předat řetězec filtru stylu OData metodě listEntities . Pokud byste například chtěli získat všechna čtení počasí pro Chicago mezi půlnocí 1. července 2021 a půlnocí 2. července 2021 (včetně), předali byste následující řetězec filtru.
PartitionKey eq 'Chicago' and RowKey ge '2021-07-01 12:00 AM' and RowKey le '2021-07-02 12:00 AM'
Všechny operátory filtru OData můžete zobrazit na webu OData v části Možnost systémového dotazu filtru.
V ukázkové aplikaci je objekt navržen tak, FilterResultsInputModel
aby zachytil všechna kritéria filtru poskytnutá uživatelem.
public class FilterResultsInputModel implements Serializable {
private String partitionKey;
private String rowKeyDateStart;
private String rowKeyTimeStart;
private String rowKeyDateEnd;
private String rowKeyTimeEnd;
private Double minTemperature;
private Double maxTemperature;
private Double minPrecipitation;
private Double maxPrecipitation;
public String getPartitionKey() {
return partitionKey;
}
public void setPartitionKey(String partitionKey) {
this.partitionKey = partitionKey;
}
public String getRowKeyDateStart() {
return rowKeyDateStart;
}
public void setRowKeyDateStart(String rowKeyDateStart) {
this.rowKeyDateStart = rowKeyDateStart;
}
public String getRowKeyTimeStart() {
return rowKeyTimeStart;
}
public void setRowKeyTimeStart(String rowKeyTimeStart) {
this.rowKeyTimeStart = rowKeyTimeStart;
}
public String getRowKeyDateEnd() {
return rowKeyDateEnd;
}
public void setRowKeyDateEnd(String rowKeyDateEnd) {
this.rowKeyDateEnd = rowKeyDateEnd;
}
public String getRowKeyTimeEnd() {
return rowKeyTimeEnd;
}
public void setRowKeyTimeEnd(String rowKeyTimeEnd) {
this.rowKeyTimeEnd = rowKeyTimeEnd;
}
public Double getMinTemperature() {
return minTemperature;
}
public void setMinTemperature(Double minTemperature) {
this.minTemperature = minTemperature;
}
public Double getMaxTemperature() {
return maxTemperature;
}
public void setMaxTemperature(Double maxTemperature) {
this.maxTemperature = maxTemperature;
}
public Double getMinPrecipitation() {
return minPrecipitation;
}
public void setMinPrecipitation(Double minPrecipitation) {
this.minPrecipitation = minPrecipitation;
}
public Double getMaxPrecipitation() {
return maxPrecipitation;
}
public void setMaxPrecipitation(Double maxPrecipitation) {
this.maxPrecipitation = maxPrecipitation;
}
}
Pokud je tento objekt předán retrieveEntitiesByFilter
metodě ve TableServiceImpl
třídě, vytvoří řetězec filtru pro každou hodnotu vlastnosti, která není null. Potom vytvoří sloučený filtrovací řetězec spojením všech hodnot společně s klauzulí "and". Tento kombinovaný řetězec filtru se předá metodě listEntities objektu TableClient a vrátí se pouze řádky odpovídající řetězci filtru. Podobnou metodu v kódu můžete použít k vytvoření vhodných řetězců filtru podle potřeby vaší aplikace.
public List<WeatherDataModel> retrieveEntitiesByFilter(FilterResultsInputModel model) {
List<String> filters = new ArrayList<>();
if (!StringUtils.isEmptyOrWhitespace(model.getPartitionKey())) {
filters.add(String.format("PartitionKey eq '%s'", model.getPartitionKey()));
}
if (!StringUtils.isEmptyOrWhitespace(model.getRowKeyDateStart())
&& !StringUtils.isEmptyOrWhitespace(model.getRowKeyTimeStart())) {
filters.add(String.format("RowKey ge '%s %s'", model.getRowKeyDateStart(), model.getRowKeyTimeStart()));
}
if (!StringUtils.isEmptyOrWhitespace(model.getRowKeyDateEnd())
&& !StringUtils.isEmptyOrWhitespace(model.getRowKeyTimeEnd())) {
filters.add(String.format("RowKey le '%s %s'", model.getRowKeyDateEnd(), model.getRowKeyTimeEnd()));
}
if (model.getMinTemperature() != null) {
filters.add(String.format("Temperature ge %f", model.getMinTemperature()));
}
if (model.getMaxTemperature() != null) {
filters.add(String.format("Temperature le %f", model.getMaxTemperature()));
}
if (model.getMinPrecipitation() != null) {
filters.add(String.format("Precipitation ge %f", model.getMinPrecipitation()));
}
if (model.getMaxPrecipitation() != null) {
filters.add(String.format("Precipitation le %f", model.getMaxPrecipitation()));
}
List<WeatherDataModel> modelList = tableClient.listEntities(new ListEntitiesOptions()
.setFilter(String.join(" and ", filters)), null, null).stream()
.map(WeatherDataUtils::mapTableEntityToWeatherDataModel)
.collect(Collectors.toList());
return Collections.unmodifiableList(WeatherDataUtils.filledValue(modelList));
}
Vložení dat pomocí objektu TableEntity
Nejjednodušší způsob, jak přidat data do tabulky, je použití TableEntity objektu. V tomto příkladu se data mapují ze vstupního objektu modelu na objekt TableEntity . Vlastnosti vstupního objektu představující název meteorologické stanice a datum a čas pozorování jsou mapovány na PartitionKey
vlastnosti a RowKey
) v uvedeném pořadí, které společně tvoří jedinečný klíč pro řádek v tabulce. Další vlastnosti vstupního objektu modelu se pak mapují na vlastnosti slovníku v objektu TableClient . Nakonec metoda createEntity u objektu TableClient slouží k vložení dat do tabulky.
insertEntity
Upravte třídu v ukázkové aplikaci tak, aby obsahovala následující kód.
public void insertEntity(WeatherInputModel model) {
tableClient.createEntity(WeatherDataUtils.createTableEntity(model));
}
Upsert data pomocí objektu TableEntity
Pokud se pokusíte vložit řádek do tabulky s kombinací klíče oddílu nebo řádku, která už v této tabulce existuje, zobrazí se chyba. Z tohoto důvodu je často vhodnější použít upsertEntity místo insertEntity
metody při přidávání řádků do tabulky. Pokud daná kombinace klíče oddílu nebo řádku již v tabulce existuje, metoda upsertEntity aktualizuje existující řádek. V opačném případě se řádek přidá do tabulky.
public void upsertEntity(WeatherInputModel model) {
tableClient.upsertEntity(WeatherDataUtils.createTableEntity(model));
}
Vložení nebo upsertování dat s vlastnostmi proměnné
Jednou z výhod použití rozhraní API tabulek Azure Cosmos DB je, že pokud objekt načtený do tabulky obsahuje jakékoli nové vlastnosti, tyto vlastnosti se automaticky přidají do tabulky a hodnoty uložené ve službě Azure Cosmos DB. Není nutné spouštět příkazy DDL, jako ALTER TABLE
by se přidávaly sloupce jako v tradiční databázi.
Tento model dává vaší aplikaci flexibilitu při práci se zdroji dat, které můžou přidávat nebo upravovat data, která je potřeba zachytávat v průběhu času nebo když různé vstupy poskytují různým datům vaší aplikaci. V ukázkové aplikaci můžeme simulovat meteorologická stanice, která odesílá nejen základní data o počasí, ale také některé další hodnoty. Když je objekt s těmito novými vlastnostmi poprvé uložen v tabulce, odpovídající vlastnosti (sloupce) se automaticky přidají do tabulky.
V ukázkové aplikaci ExpandableWeatherObject
je třída postavená na interním slovníku, který podporuje libovolnou sadu vlastností objektu. Tato třída představuje typický vzor pro, kdy objekt musí obsahovat libovolnou sadu vlastností.
public class ExpandableWeatherObject {
private String stationName;
private String observationDate;
private Map<String, Object> propertyMap = new HashMap<String, Object>();
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public String getObservationDate() {
return observationDate;
}
public void setObservationDate(String observationDate) {
this.observationDate = observationDate;
}
public Map<String, Object> getPropertyMap() {
return propertyMap;
}
public void setPropertyMap(Map<String, Object> propertyMap) {
this.propertyMap = propertyMap;
}
public boolean containsProperty(String key) {
return this.propertyMap.containsKey(key);
}
public Object getPropertyValue(String key) {
return containsProperty(key) ? this.propertyMap.get(key) : null;
}
public void putProperty(String key, Object value) {
this.propertyMap.put(key, value);
}
public List<String> getPropertyKeys() {
List<String> list = Collections.synchronizedList(new ArrayList<String>());
Iterator<String> iterators = this.propertyMap.keySet().iterator();
while (iterators.hasNext()) {
list.add(iterators.next());
}
return Collections.unmodifiableList(list);
}
public Integer getPropertyCount() {
return this.propertyMap.size();
}
}
Chcete-li vložit nebo upsertovat takový objekt pomocí rozhraní API pro tabulku, namapujte vlastnosti rozbalitelného objektu na objekt TableEntity a podle potřeby použijte metody createEntity nebo upsertEntity v objektu TableClient .
public void insertExpandableEntity(ExpandableWeatherObject model) {
tableClient.createEntity(WeatherDataUtils.createTableEntity(model));
}
public void upsertExpandableEntity(ExpandableWeatherObject model) {
tableClient.upsertEntity(WeatherDataUtils.createTableEntity(model));
}
Aktualizace entity
Entity lze aktualizovat voláním metody updateEntity v objektu TableClient . Vzhledem k tomu, že entita (řádek) uložená pomocí rozhraní API pro tabulky může obsahovat libovolnou sadu vlastností, je často užitečné vytvořit aktualizační objekt založený na objektu slovníku podobnému ExpandableWeatherObject
dříve diskutovaným objektům. V tomto případě je jediným rozdílem přidání etag
vlastnosti, která se používá pro řízení souběžnosti během aktualizací.
public class UpdateWeatherObject {
private String stationName;
private String observationDate;
private String etag;
private Map<String, Object> propertyMap = new HashMap<String, Object>();
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public String getObservationDate() {
return observationDate;
}
public void setObservationDate(String observationDate) {
this.observationDate = observationDate;
}
public String getEtag() {
return etag;
}
public void setEtag(String etag) {
this.etag = etag;
}
public Map<String, Object> getPropertyMap() {
return propertyMap;
}
public void setPropertyMap(Map<String, Object> propertyMap) {
this.propertyMap = propertyMap;
}
}
V ukázkové aplikaci se tento objekt předá updateEntity
metodě ve TableServiceImpl
třídě. Tato metoda nejprve načte existující entitu z rozhraní Table API pomocí metody getEntity v TableClient. Potom aktualizuje objekt entity a použije metodu updateEntity
, uloží aktualizace do databáze. Všimněte si, jak metoda updateEntity přebírá aktuální Etag objektu, aby se zajistilo, že se objekt od počátečního načtení nezměnil. Pokud chcete entitu aktualizovat bez ohledu na to, můžete metodě předat hodnotu etag
updateEntity
.
public void updateEntity(UpdateWeatherObject model) {
TableEntity tableEntity = tableClient.getEntity(model.getStationName(), model.getObservationDate());
Map<String, Object> propertiesMap = model.getPropertyMap();
propertiesMap.keySet().forEach(key -> tableEntity.getProperties().put(key, propertiesMap.get(key)));
tableClient.updateEntity(tableEntity);
}
Odebrání entity
Chcete-li odebrat entitu z tabulky, zavolejte metodu deleteEntity u objektu TableClient s klíčem oddílu a klíčem řádku objektu.
public void deleteEntity(WeatherInputModel model) {
tableClient.deleteEntity(model.getStationName(),
WeatherDataUtils.formatRowKey(model.getObservationDate(), model.getObservationTime()));
}
7. Spuštění kódu
Spusťte ukázkovou aplikaci pro interakci s rozhraním API tabulek Azure Cosmos DB. Při prvním spuštění aplikace nebudou k dispozici žádná data, protože tabulka je prázdná. Pomocí libovolného tlačítka v horní části aplikace přidejte data do tabulky.
Výběrem tlačítka Vložit pomocí entity tabulky se otevře dialogové okno, ve kterém můžete vložit nebo vložit nový řádek pomocí objektu TableEntity
.
Když vyberete tlačítko Vložit pomocí rozbalitelného data , zobrazí se dialogové okno, které umožňuje vložit objekt s vlastními vlastnostmi. V případě potřeby rozhraní API pro tabulky Azure Cosmos DB automaticky přidá do tabulky vlastnosti (sloupce). Pomocí tlačítka Přidat vlastní pole přidejte jednu nebo více nových vlastností a předveďte tuto funkci.
Pomocí tlačítka Vložit ukázková data načtěte do tabulky Azure Cosmos DB ukázková data.
V horní nabídce vyberte položku Filtrovat výsledky, která se má přesunout na stránku Filtrovat výsledky. Na této stránce vyplňte kritéria filtru, abyste ukázali, jak lze vytvořit a předat klauzuli filtru do rozhraní API tabulek Azure Cosmos DB.
Vyčištění prostředků
Až budete s ukázkovou aplikací hotovi, měli byste ze svého účtu Azure odebrat všechny prostředky Azure související s tímto článkem. Můžete to provést odstraněním skupiny prostředků.
Skupinu prostředků můžete odstranit pomocí webu Azure Portal následujícím způsobem.
Další kroky
V tomto rychlém startu jste se seznámili s postupem vytvoření databázového účtu Azure Cosmos DB, vytvoření tabulky pomocí Průzkumníka dat a spuštění aplikace. Teď se můžete dotazovat na data pomocí rozhraní API pro tabulku.