Rychlý start: Vytvoření rozhraní API pro aplikaci Table pomocí sady Java SDK a Azure Cosmos DB
PLATÍ PRO: Tabulka
V tomto rychlém startu se dozvíte, jak získat přístup k rozhraní API tabulek služby Azure Cosmos DB z aplikace v Javě. Rozhraní API tabulek služby Azure Cosmos DB je úložiště dat bez schématu, které aplikacím umožňuje 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 služby 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 buď Visual Studio Code, nebo IntelliJ IDEA .
Pokud nemáte předplatné Azure, vytvořte si před zahájením bezplatného účtu .
Ukázková aplikace
Ukázkovou aplikaci pro tento kurz můžete naklonovat nebo stáhnout z úložiště https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-java. Úvodní i dokončená aplikace jsou součástí ukázkového úložiště.
git clone https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-java
Ukázková aplikace používá data o počasí jako příklad 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 tabulky, včetně ukládání objektů s dalšími vlastnostmi, které demonstrují možnosti rozhraní API tabulek bez schémat.
1. Vytvoření účtu služby Azure Cosmos DB
Nejprve musíte vytvořit účet rozhraní API tabulek služby Azure Cosmos DB, který bude obsahovat tabulky použité ve vaší aplikaci. Můžete to provést pomocí Azure Portal, Azure CLI nebo Azure PowerShell.
Přihlaste se k Azure Portal a podle těchto kroků 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. Po načtení dat do tabulky se vlastnosti (sloupce) automaticky vytvoří podle potřeby.
V Azure Portal proveďte následující kroky a vytvořte tabulku uvnitř účtu služby Azure Cosmos DB.
3. Získání připojovacího řetězce služby Azure Cosmos DB
Pro přístup k tabulce ve službě Azure Cosmos DB bude vaše aplikace potřebovat připojovací řetězec tabulky pro účet služby CosmosDB Storage. Připojovací řetězec je možné načíst pomocí Azure Portal, Azure CLI nebo Azure PowerShell.
Připojovací řetězec pro váš účet služby Azure Cosmos DB se považuje za tajný kód aplikace a musí být chráněný jako jakýkoli jiný tajný klíč aplikace nebo heslo. Tento příklad používá POM k uložení připojovacího řetězce během vývoje a jeho zpřístupnění aplikaci.
<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 služby 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 Table 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, který se používá ke komunikaci s rozhraním TABLE API služby Azure Cosmos DB.
Aplikace obvykle vytvoří jeden objekt TableClient pro každou tabulku, který se bude používat v celé aplikaci. Doporučuje se označit, že metoda vygeneruje objekt TableClient , který bude spravován kontejnerem Spring, a jako singleton, aby toho bylo možné dosáhnout.
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();
}
}
Do 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 Services . Balíček sady SDK budete muset importovat com.azure.data.tables
.
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čátek TableServiceImpl
třídy přidejte proměnnou člena pro objekt TableClient a konstruktor, který 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. Vzhledem k tomu, že v tomto příkladu se metodě nepředávají žádné parametry, budou z tabulky vybrány všechny řádky.
Metoda také přebírá obecný parametr typu TableEntity , který určuje, že data třídy modelu budou vrácena jako. V tomto případě se používá předdefinovaná třída TableEntity , což znamená, že listEntities
metoda vrátí kolekci PagedIterable<TableEntity>
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 obsahuje vlastnosti pro hodnoty klíče oddílu a 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ěsta) 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ěžnou praxí je mapovat objekt TableEntity na objekt vlastní definice. Ukázková aplikace definuje třídu WeatherDataModel
v adresáři Models pro tento účel. Tato třída má vlastnosti pro název stanice a datum pozorování, na které se namapuje klíč oddílu a klíč řádku, a poskytuje smysluplnější názvy vlastností pro tyto hodnoty. Potom použije slovník k uložení všech ostatních vlastností objektu. Toto je běžný vzor při práci s úložištěm tabulek, protože řádek může mít libovolný počet libovolných vlastností a chceme, aby objekty modelu mohly zachytit všechny. Tato třída obsahuje také 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
vlastnosti , RowKey
, Timestamp
a Etag
pak pomocí properties.keySet
metody iteruje ostatní vlastnosti v objektu TableEntity
a mapuje je na WeatherDataModel
objekt bez vlastností, které již byly přímo namapovány.
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
Pokud chcete 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 dotazu systému 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;
}
}
Když 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ří kombinovaný řetězec filtru spojením všech hodnot s klauzulí "a". Tento kombinovaný řetězec filtru se předá metodě listEntities v objektu TableClient a budou vráceny pouze řádky odpovídající řetězci filtru. Podobnou metodu můžete v kódu použít k vytvoření vhodných řetězců filtru podle požadavků 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žít objekt TableEntity . V tomto příkladu jsou data mapována 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
), které společně tvoří jedinečný klíč pro řádek v tabulce. Další vlastnosti vstupního objektu modelu jsou pak mapovány na vlastnosti slovníku v objektu TableClient . Nakonec metoda createEntity v 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 dat pomocí objektu TableEntity
Pokud se pokusíte vložit řádek do tabulky s kombinací klíče oddílu a klíče řádku, která už v této tabulce existuje, zobrazí se chyba. Z tohoto důvodu je při přidávání řádků do tabulky často vhodnější použít metodu insertEntity
upsertEntity místo metody . Pokud daná kombinace klíče oddílu a klíče řá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 upsert dat s vlastnostmi proměnné
Jednou z výhod použití rozhraní API pro tabulky služby Azure Cosmos DB je, že pokud objekt načtený do tabulky obsahuje 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
je přidání sloupců v tradiční databázi.
Tento model poskytuje vaší aplikaci flexibilitu při práci se zdroji dat, které můžou přidávat nebo upravovat data, která je potřeba zachytit v průběhu času, nebo když různé vstupy poskytují aplikaci různá data. 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 je třída postavena na interním slovníku, ExpandableWeatherObject
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();
}
}
Pokud chcete vložit nebo vložit takový objekt pomocí rozhraní API pro Table, namapujte vlastnosti rozbalitelného objektu do objektu TableEntity a podle potřeby použijte metody createEntity nebo upsertEntity u 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 tabulek může obsahovat libovolnou sadu vlastností, je často užitečné vytvořit objekt aktualizace založený na objektu slovníku podobnému ExpandableWeatherObject
výše. V tomto případě je jediným rozdílem etag
přidání 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 tento objekt entity aktualizuje a použije metodu updateEntity
uložení aktualizací do databáze. Všimněte si, jak metoda updateEntity přebírá aktuální Etag objektu, aby se zajistilo, že objekt se nezměnil od jeho počátečního načtení. 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
Pokud chcete 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 služby 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, které umožňuje vložit nebo vložit nový řádek pomocí objektu TableEntity
.
Když vyberete tlačítko Vložit pomocí rozbalitelných dat , zobrazí se dialogové okno, které vám umožní vložit objekt s vlastními vlastnostmi a předvádí, jak rozhraní API tabulek služby Azure Cosmos DB v případě potřeby automaticky přidává 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 ukázková data do tabulky Azure Cosmos DB.
Výběrem položky Filtrovat výsledky v horní nabídce přejděte na stránku Výsledky filtru. Na této stránce vyplňte kritéria filtru, abyste si ukázali, jak lze vytvořit klauzuli filtru a předat ji do rozhraní API tabulek služby Azure Cosmos DB.
Vyčištění prostředků
Až ukázkovou aplikaci dokončíte, měli byste ze svého účtu Azure odebrat všechny prostředky Azure související s tímto článkem. Můžete to udělat odstraněním skupiny prostředků.
Skupinu prostředků můžete odstranit pomocí 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ď můžete data dotazovat pomocí rozhraní API pro Tabulku.