Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Это важно
Azure Cosmos DB для PostgreSQL больше не поддерживается для новых проектов. Не используйте эту службу для новых проектов. Вместо этого используйте одну из этих двух служб:
Используйте Azure Cosmos DB для NoSQL как распределенное решение базы данных, предназначенное для крупномасштабных сценариев с соглашением об уровне доступности (SLA) 99.999%, мгновенным автомасштабированием и автоматическим переключением в случае отказа в нескольких регионах.
Используйте функцию эластичных кластеров Базы данных Azure для PostgreSQL для сегментированного PostgreSQL с помощью расширения Citus с открытым кодом.
В этом кратком руководстве показано, как использовать код Java для подключения к кластеру и использовать инструкции SQL для создания таблицы. Затем вы вставляете, запрашиваете, обновляете и удаляете данные в базе данных. Предполагается, что вы знакомы с разработкой на Java и JDBC, но впервые работаете с Azure Cosmos DB для PostgreSQL.
Настройка проекта Java и подключения
Создайте проект Java и файл конфигурации для подключения к Azure Cosmos DB для PostgreSQL.
Создание нового проекта Java
Используя любимую интегрированную среду разработки (IDE), создайте проект Java с groupId test и artifactId crud. В корневом каталоге проекта добавьте файл pom.xml со следующим содержимым. Этот файл настраивает Apache Maven для использования Java 8 и недавнего драйвера PostgreSQL для Java.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>crud</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
Настройка подключения к базе данных
В src/main/resources/создайте файл application.properties со следующим содержимым. Замените <cluster> именем вашего кластера и замените <password> административным паролем или маркером идентификатора Microsoft Entra.
driver.class.name=org.postgresql.Driver
db.url=jdbc:postgresql://c-<cluster>.<uniqueID>.postgres.cosmos.azure.com:5432/citus?ssl=true&sslmode=require
db.username=citus
db.password=<password>
Строка ?ssl=true&sslmode=require в свойстве db.url указывает драйверу JDBC использовать Transport Layer Security (TLS) при подключении к базе данных. Обязательно использовать TLS с Azure Cosmos DB для PostgreSQL и является хорошей практикой безопасности.
Создание таблиц
Настройте схему базы данных с распределенными таблицами. Подключитесь к базе данных для создания схемы и таблиц.
Создание схемы базы данных
В src/main/resources/создайте файл schema.sql со следующим содержимым:
DROP TABLE IF EXISTS public.pharmacy;
CREATE TABLE public.pharmacy(pharmacy_id integer,pharmacy_name text ,city text ,state text ,zip_code integer);
CREATE INDEX idx_pharmacy_id ON public.pharmacy(pharmacy_id);
Распределение таблиц
Azure Cosmos DB для PostgreSQL предоставляет супер возможности распределения таблиц между несколькими узлами для масштабируемости. С помощью приведенной ниже команды можно распределить таблицу. Дополнительные сведения о create_distributed_table и столбце распределения см. здесь.
Примечание.
Распределение таблиц позволяет им расширяться на любые рабочие узлы, добавленные в кластер.
Чтобы распространить таблицы, добавьте следующую строку в файл schema.sql , созданный в предыдущем разделе.
select create_distributed_table('public.pharmacy','pharmacy_id');
Подключение к базе данных и создание схемы
Затем добавьте код Java, использующий JDBC для хранения и извлечения данных из кластера. В коде используются файлы application.properties и schema.sql для подключения к кластеру и создания схемы.
Создайте файл DButil.java со следующим кодом, который содержит
DButilкласс. КлассDBUtilнастраивает пул подключений к PostgreSQL с помощью HikariCP. Этот класс используется для подключения к PostgreSQL и запуска запросов.Совет
В приведенном ниже примере кода используется пул подключений для создания подключений к PostgreSQL и управления ими. Настоятельно рекомендуется использовать пул подключений на стороне приложения, так как:
- Это гарантирует, что приложение не будет создавать слишком много подключений к базе данных, поэтому можно будет избежать превышения ограничений на подключения.
- Это может значительно повысить производительность — как с точки зрения задержки, так и с точки зрения пропускной способности. Процесс сервера PostgreSQL должен разделиться, чтобы обработать каждое новое подключение, а повторное использование подключения позволяет избежать этих издержек.
//DButil.java package test.crud; import java.io.FileInputStream; import java.io.IOException; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import com.zaxxer.hikari.HikariDataSource; public class DButil { private static final String DB_USERNAME = "db.username"; private static final String DB_PASSWORD = "db.password"; private static final String DB_URL = "db.url"; private static final String DB_DRIVER_CLASS = "driver.class.name"; private static Properties properties = null; private static HikariDataSource datasource; static { try { properties = new Properties(); properties.load(new FileInputStream("src/main/java/application.properties")); datasource = new HikariDataSource(); datasource.setDriverClassName(properties.getProperty(DB_DRIVER_CLASS )); datasource.setJdbcUrl(properties.getProperty(DB_URL)); datasource.setUsername(properties.getProperty(DB_USERNAME)); datasource.setPassword(properties.getProperty(DB_PASSWORD)); datasource.setMinimumIdle(100); datasource.setMaximumPoolSize(1000000000); datasource.setAutoCommit(true); datasource.setLoginTimeout(3); } catch (IOException | SQLException e) { e.printStackTrace(); } } public static DataSource getDataSource() { return datasource; } }В src/main/java/создайте файл DemoApplication.java , содержащий следующий код:
package test.crud; import java.io.IOException; import java.sql.*; import java.util.*; import java.util.logging.Logger; import java.io.FileInputStream; import java.io.FileOutputStream; import org.postgresql.copy.CopyManager; import org.postgresql.core.BaseConnection; import java.io.IOException; import java.io.Reader; import java.io.StringReader; public class DemoApplication { private static final Logger log; static { System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$-7s] %5$s %n"); log =Logger.getLogger(DemoApplication.class.getName()); } public static void main(String[] args)throws Exception { log.info("Connecting to the database"); Connection connection = DButil.getDataSource().getConnection(); System.out.println("The Connection Object is of Class: " + connection.getClass()); log.info("Database connection test: " + connection.getCatalog()); log.info("Creating table"); log.info("Creating index"); log.info("distributing table"); Scanner scanner = new Scanner(DemoApplication.class.getClassLoader().getResourceAsStream("schema.sql")); Statement statement = connection.createStatement(); while (scanner.hasNextLine()) { statement.execute(scanner.nextLine()); } log.info("Closing database connection"); connection.close(); } }Примечание.
База данных
userиpasswordучетные данные используются при выполненииDriverManager.getConnection(properties.getProperty("url"), properties);. Учетные данные хранятся в файле application.properties , который передается в качестве аргумента.Теперь вы можете выполнить основной класс с помощью вашего любимого инструмента.
- С помощью интегрированной среды разработки вы сможете щелкнуть правой кнопкой мыши на классе
DemoApplicationи выполнить его. - С помощью Maven можно запустить приложение, выполнив следующее:
mvn exec:java -Dexec.mainClass="com.example.demo.DemoApplication".
- С помощью интегрированной среды разработки вы сможете щелкнуть правой кнопкой мыши на классе
Приложение должно подключиться к Azure Cosmos DB для PostgreSQL, создать схему базы данных, а затем закрыть подключение, как показано в журналах консоли:
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Create database schema
[INFO ] Closing database connection
Создание доменного класса
Создайте новый класс Java Pharmacy рядом с классом DemoApplication и добавьте следующий код:
public class Pharmacy {
private Integer pharmacy_id;
private String pharmacy_name;
private String city;
private String state;
private Integer zip_code;
public Pharmacy() { }
public Pharmacy(Integer pharmacy_id, String pharmacy_name, String city,String state,Integer zip_code)
{
this.pharmacy_id = pharmacy_id;
this.pharmacy_name = pharmacy_name;
this.city = city;
this.state = state;
this.zip_code = zip_code;
}
public Integer getpharmacy_id() {
return pharmacy_id;
}
public void setpharmacy_id(Integer pharmacy_id) {
this.pharmacy_id = pharmacy_id;
}
public String getpharmacy_name() {
return pharmacy_name;
}
public void setpharmacy_name(String pharmacy_name) {
this.pharmacy_name = pharmacy_name;
}
public String getcity() {
return city;
}
public void setcity(String city) {
this.city = city;
}
public String getstate() {
return state;
}
public void setstate(String state) {
this.state = state;
}
public Integer getzip_code() {
return zip_code;
}
public void setzip_code(Integer zip_code) {
this.zip_code = zip_code;
}
@Override
public String toString() {
return "TPharmacy{" +
"pharmacy_id=" + pharmacy_id +
", pharmacy_name='" + pharmacy_name + '\'' +
", city='" + city + '\'' +
", state='" + state + '\'' +
", zip_code='" + zip_code + '\'' +
'}';
}
}
Этот класс является доменной моделью, сопоставленной с таблицей Pharmacy, которую вы создали при выполнении скрипта schema.sql.
Вставка данных
В файле DemoApplication.java после метода добавьте следующий метод, использующий инструкцию main INSERT INTO SQL для вставки данных в базу данных:
private static void insertData(Pharmacy todo, Connection connection) throws SQLException {
log.info("Insert data");
PreparedStatement insertStatement = connection
.prepareStatement("INSERT INTO pharmacy (pharmacy_id,pharmacy_name,city,state,zip_code) VALUES (?, ?, ?, ?, ?);");
insertStatement.setInt(1, todo.getpharmacy_id());
insertStatement.setString(2, todo.getpharmacy_name());
insertStatement.setString(3, todo.getcity());
insertStatement.setString(4, todo.getstate());
insertStatement.setInt(5, todo.getzip_code());
insertStatement.executeUpdate();
}
Добавьте две следующие строки в основной метод:
Pharmacy todo = new Pharmacy(0,"Target","Sunnyvale","California",94001);
insertData(todo, connection);
Теперь при запуске класса main вы увидите следующие выходные данные.
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Closing database connection
Чтение данных
Прочитайте ранее вставленные данные, чтобы убедиться, что код работает правильно.
В файле DemoApplication.java после метода добавьте следующий метод, использующий инструкцию insertData SELECT SQL для чтения данных из базы данных:
private static Pharmacy readData(Connection connection) throws SQLException {
log.info("Read data");
PreparedStatement readStatement = connection.prepareStatement("SELECT * FROM Pharmacy;");
ResultSet resultSet = readStatement.executeQuery();
if (!resultSet.next()) {
log.info("There is no data in the database!");
return null;
}
Pharmacy todo = new Pharmacy();
todo.setpharmacy_id(resultSet.getInt("pharmacy_id"));
todo.setpharmacy_name(resultSet.getString("pharmacy_name"));
todo.setcity(resultSet.getString("city"));
todo.setstate(resultSet.getString("state"));
todo.setzip_code(resultSet.getInt("zip_code"));
log.info("Data read from the database: " + todo.toString());
return todo;
}
Добавьте следующую строку в основной метод:
todo = readData(connection);
Теперь при запуске класса main вы увидите следующие выходные данные.
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Sunnyvale', state='California', zip_code='94001'}
[INFO ] Closing database connection
Обновление данных
Обновите ранее вставленные данные.
По-прежнему в файле DemoApplication.java после readData метода добавьте следующий метод для обновления данных в базе данных с помощью инструкции UPDATE SQL:
private static void updateData(Pharmacy todo, Connection connection) throws SQLException {
log.info("Update data");
PreparedStatement updateStatement = connection
.prepareStatement("UPDATE pharmacy SET city = ? WHERE pharmacy_id = ?;");
updateStatement.setString(1, todo.getcity());
updateStatement.setInt(2, todo.getpharmacy_id());
updateStatement.executeUpdate();
readData(connection);
}
Добавьте две следующие строки в основной метод:
todo.setcity("Guntur");
updateData(todo, connection);
Теперь при запуске класса main вы увидите следующие выходные данные.
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Sunnyvale', state='California', zip_code='94001'}
[INFO ] Update data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Guntur', state='California', zip_code='94001'}
[INFO ] Closing database connection
Удаление данных
Наконец, удалите ранее вставленные данные. По-прежнему в файле DemoApplication.java после updateData метода добавьте следующий метод для удаления данных в базе данных с помощью инструкции DELETE SQL:
private static void deleteData(Pharmacy todo, Connection connection) throws SQLException {
log.info("Delete data");
PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM pharmacy WHERE pharmacy_id = ?;");
deleteStatement.setLong(1, todo.getpharmacy_id());
deleteStatement.executeUpdate();
readData(connection);
}
Теперь вы можете добавить следующую строку в метод main.
deleteData(todo, connection);
Теперь при запуске класса main вы увидите следующие выходные данные.
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Sunnyvale', state='California', zip_code='94001'}
[INFO ] Update data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Guntur', state='California', zip_code='94001'}
[INFO ] Delete data
[INFO ] Read data
[INFO ] There is no data in the database!
[INFO ] Closing database connection
Команда COPY для быстрого приема
Команда COPY может обеспечить огромную пропускную способность при приеме данных в Azure Cosmos DB для PostgreSQL. Команда COPY может принимать данные в файлах или из микропакетов данных в памяти для приема в реальном времени.
Команда COPY для загрузки данных из файла
Следующий код копирует данные из CSV-файла в таблицу базы данных. Для примера кода требуется файл pharmacies.csv.
public static long
copyFromFile(Connection connection, String filePath, String tableName)
throws SQLException, IOException {
long count = 0;
FileInputStream fileInputStream = null;
try {
Connection unwrap = connection.unwrap(Connection.class);
BaseConnection connSec = (BaseConnection) unwrap;
CopyManager copyManager = new CopyManager((BaseConnection) connSec);
fileInputStream = new FileInputStream(filePath);
count = copyManager.copyIn("COPY " + tableName + " FROM STDIN delimiter ',' csv", fileInputStream);
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
Теперь вы можете добавить следующую строку в метод main.
int c = (int) copyFromFile(connection,"C:\\Users\\pharmacies.csv", "pharmacy");
log.info("Copied "+ c +" rows using COPY command");
Теперь при запуске класса main вы должны увидеть следующее:
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Sunnyvale', state='California', zip_code='94001'}
[INFO ] Update data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Guntur', state='California', zip_code='94001'}
[INFO ] Delete data
[INFO ] Read data
[INFO ] There is no data in the database!
[INFO ] Copied 5000 rows using COPY command
[INFO ] Closing database connection
Команда COPY для загрузки данных в памяти
Следующий код копирует данные в память в таблицу.
private static void inMemory(Connection connection) throws SQLException,IOException
{
log.info("Copying inmemory data into table");
final List<String> rows = new ArrayList<>();
rows.add("0,Target,Sunnyvale,California,94001");
rows.add("1,Apollo,Guntur,Andhra,94003");
final BaseConnection baseConnection = (BaseConnection) connection.unwrap(Connection.class);
final CopyManager copyManager = new CopyManager(baseConnection);
// COPY command can change based on the format of rows. This COPY command is for above rows.
final String copyCommand = "COPY pharmacy FROM STDIN with csv";
try (final Reader reader = new StringReader(String.join("\n", rows))) {
copyManager.copyIn(copyCommand, reader);
}
}
Теперь вы можете добавить следующую строку в метод main.
inMemory(connection);
Теперь при запуске класса main вы увидите следующие выходные данные.
[INFO ] Loading application properties
[INFO ] Connecting to the database
[INFO ] Database connection test: citus
[INFO ] Creating table
[INFO ] Creating index
[INFO ] distributing table
[INFO ] Insert data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Sunnyvale', state='California', zip_code='94001'}
[INFO ] Update data
[INFO ] Read data
[INFO ] Data read from the database: Pharmacy{pharmacy_id=0, pharmacy_name='Target', city='Guntur', state='California', zip_code='94001'}
[INFO ] Delete data
[INFO ] Read data
[INFO ] There is no data in the database!
5000
[INFO ] Copying in-memory data into table
[INFO ] Closing database connection
Повтор попыток приложения при сбоях выполнения запросов к базе данных
Иногда запросы к базе данных из приложения могут завершаться ошибкой. Такие проблемы могут возникать при различных сценариях, включая сбой сети между приложением и базой данных, неправильный пароль и т. д. Некоторые проблемы могут быть временными и самостоятельно устраняться в течение нескольких секунд или минут. Для устранения временных ошибок можно настроить в приложении логику повторных попыток.
Настройка логики повторных попыток в приложении помогает улучшить взаимодействие с пользователем. В сценариях сбоя пользователи просто будут немного дольше ждать, пока приложение обслужит запросы, а не столкнутся с ошибками.
В приведенном ниже примере показано, как реализовать логику повторных попыток в вашем приложении. Фрагмент примера кода пытается выполнить запрос к базе данных каждые 60 секунд (до пяти раз) до достижения требуемого результата. Количество и частоту повторных попыток можно настроить в зависимости от требований вашего приложения.
В этом коде замените <кластер> именем кластера и <паролем> администратора.
package test.crud;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.logging.Logger;
import com.zaxxer.hikari.HikariDataSource;
public class DemoApplication
{
private static final Logger log;
static
{
System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$-7s] %5$s %n");
log = Logger.getLogger(DemoApplication.class.getName());
}
private static final String DB_USERNAME = "citus";
private static final String DB_PASSWORD = "<password>";
private static final String DB_URL = "jdbc:postgresql://c-<cluster>.<uniqueID>.postgres.cosmos.azure.com:5432/citus?sslmode=require";
private static final String DB_DRIVER_CLASS = "org.postgresql.Driver";
private static HikariDataSource datasource;
private static String executeRetry(String sql, int retryCount) throws InterruptedException
{
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
for (int i = 1; i <= retryCount; i++)
{
try
{
datasource = new HikariDataSource();
datasource.setDriverClassName(DB_DRIVER_CLASS);
datasource.setJdbcUrl(DB_URL);
datasource.setUsername(DB_USERNAME);
datasource.setPassword(DB_PASSWORD);
datasource.setMinimumIdle(10);
datasource.setMaximumPoolSize(1000);
datasource.setAutoCommit(true);
datasource.setLoginTimeout(3);
log.info("Connecting to the database");
con = datasource.getConnection();
log.info("Connection established");
log.info("Read data");
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
StringBuilder builder = new StringBuilder();
int columnCount = rs.getMetaData().getColumnCount();
while (rs.next())
{
for (int j = 0; j < columnCount;)
{
builder.append(rs.getString(j + 1));
if (++j < columnCount)
builder.append(",");
}
builder.append("\r\n");
}
return builder.toString();
}
catch (Exception e)
{
Thread.sleep(60000);
System.out.println(e.getMessage());
}
}
return null;
}
public static void main(String[] args) throws Exception
{
String result = executeRetry("select 1", 5);
System.out.print(result);
}
}
Следующие шаги
- Узнайте, как API Azure Cosmos DB для PostgreSQL расширяет PostgreSQL и попробуйте полезные диагностические запросы
- Выбор оптимального размера кластера для рабочей нагрузки
- Мониторинг производительности кластера