- Rukovodstvo
- статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
- Работа с PostgreSQL на Java
- Введение PostgreSQL [https://www.postgresql.org] (известный под названием Postgres) известен своей объектно-реляционной природой. Напротив, другие системы баз данных обычно реляционные. По своей природе это отличное сочетание с Java, которая сильно объектно-ориентирована. Доступ к базе данных Postgres с использованием Java требует, чтобы вы полагались на JDBC API [https://www.oracle.com/technetwork/java/javase/jdbc/index.html], как вы могли догадаться. Из-за этого подпрограммы Postgres и подпрограммы
- Вступление
- Что такое PostgreSQL?
- Демо-приложение
- Зависимость от Maven
- Модель домена
- Функциональность CRUD
- Создание базы данных PosgreSQL
- Подключение к базе данных
- Добавление сущностей
- Чтение сущностей
- Обновление сущностей
- Удаление объектов
- Запуск приложения
- Заключение
Rukovodstvo
статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
Работа с PostgreSQL на Java
Введение PostgreSQL [https://www.postgresql.org] (известный под названием Postgres) известен своей объектно-реляционной природой. Напротив, другие системы баз данных обычно реляционные. По своей природе это отличное сочетание с Java, которая сильно объектно-ориентирована. Доступ к базе данных Postgres с использованием Java требует, чтобы вы полагались на JDBC API [https://www.oracle.com/technetwork/java/javase/jdbc/index.html], как вы могли догадаться. Из-за этого подпрограммы Postgres и подпрограммы
Вступление
PostgreSQL (известный под названием Postgres) известен своей объектно-реляционной природой. Напротив, другие системы баз данных обычно реляционные . По своей природе это отличное сочетание с Java, которая сильно объектно-ориентирована.
Доступ к базе данных Postgres с использованием Java требует, чтобы вы полагались на JDBC API , как вы могли подозревать. Из-за этого подпрограммы Postgres и других систем баз данных похожи. Тем не менее, это не скрывает того факта, что Postgres предлагает дополнительные возможности, такие как расширенная поддержка пользовательских типов данных и больших наборов данных.
Что такое PostgreSQL?
PostgreSQL — это производная от ныне несуществующего проекта POSTGRES. POSTGRES нацелен не только на объектную ориентацию, но и на расширяемость. Тем не менее, Калифорнийский университет прекратил разработку ПОСТГРЭС в 1994 году.
Ранние выпуски Postgres предназначались для компьютеров UNIX. Тем не менее, с годами база данных стала портативной. Таким образом, вы можете найти его в системах MacOS, Linux и Windows.
Его открытый исходный код и бесплатное лицензирование также способствовали его широкому распространению. Разработчикам это нравится отчасти потому, что они могут покопаться в источниках, чтобы узнать, как именно это работает.
Демо-приложение
Руководство Postgres неполно без сопутствующей реализации CRUD. Мы напишем простое приложение Java, которое может создавать, читать, обновлять и удалять информацию о клиентах из базы данных Postgres.
Конечно, мы начнем с определения сущностей, а затем с их помощью сгенерируем схему базы данных, чтобы убедиться, что таблицы отображаются правильно.
И, как требует надлежащий API, уровень бизнес-логики не должен иметь представление о том, что происходит на уровне базы данных — практика, известная как многоуровневая архитектура . Таким образом, мы выберем шаблон объекта доступа к данным (DAO), чтобы удовлетворить эту потребность.
Зависимость от Maven
Мы начнем с maven-archetype-quickstart для простого скелетного проекта Maven через ваш терминал:
$ mvn archetype:generate -DgroupId=com.stackabuse.postgresql -DartifactId=java-postgresql-sample -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
После выполнения команды у вас должна получиться такая структура:
java-postgresql-sample ├── src | ├── main | ├── java | ├── com | ├── stackabuse | ├── postgresql └── test
Затем в вашем pom.xml добавьте зависимость Postgres:
Модель домена
Давайте создадим каталог с именем api в нашем src в котором мы определим модель / объект — Customer :
Этот объект будет отображен в нашей базе данных Postgres с соответствующими полями чуть позже.
Функциональность CRUD
Поскольку мы работаем в соответствии с шаблоном DAO, давайте начнем реализовывать нашу функциональность CRUD через интерфейс Dao spi , который будет содержать все наши интерфейсы и классы обслуживания:
public interface Dao < Optionalget(int id); Collection getAll(); Optional save(T t); void update(T t); void delete(T t); >
Обратите внимание на два [дженерика уровня класса](https://docs.oracle.com/javase/tutorial/java/generics/types.html) T и I T представляет собой фактический объект класса для передачи в базу данных и из нее, тогда как I — это класс первичного ключа сущности.
Теперь у нас есть каркас CRUD и объект домена. Сделав эти два действия, мы можем приступить к созданию нашей базы данных.
Создание базы данных PosgreSQL
Следуйте руководству по установке PostgreSQL для используемой вами платформы — установка довольно проста. После установки Postgres мы будем использовать pgAdmin для управления установкой.
В нашей localhost мы создадим базу данных с именем sampledb и создадим таблицу для наших Customer :
Для этого в pgAdmin запустим ввод в редакторе запросов:
CREATE TABLE public.customer ( customer_id integer NOT NULL GENERATED ALWAYS AS IDENTITY (START 1 INCREMENT 1 ), first_name character varying(45) NOT NULL, last_name character varying(45) NOT NULL, email character varying(50), CONSTRAINT customer_pkey PRIMARY KEY (customer_id) )
Итак, мы создали таблицу для Customer s.
Подключение к базе данных
Прежде чем мы сможем выполнять какие-либо операторы в базе данных из нашего кода, нам сначала нужно настроить соединение с базой данных. Мы сделаем это с JdcbConnection класса JdcbConnection:
public class JdbcConnection < private static final Logger LOGGER = Logger.getLogger(JdbcConnection.class.getName()); private static Optionalconnection = Optional.empty(); public static Optional getConnection() < if (connection.isEmpty()) < String url = "jdbc:postgresql://localhost:5432/sampledb"; String user = "postgres"; String password = "postgres"; try < connection = Optional.ofNullable( DriverManager.getConnection(url, user, password)); >catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >> return connection; > >
Основная задача приведенного выше класса — получить соединение с базой данных. Поскольку он не всегда может возвращать ненулевой Connection , соединение заключено в Optional .
Еще одна примечательная особенность — это статическая переменная . Следовательно, класс возвращает первый ненулевой экземпляр соединения, полученный им при первом запуске.
Добавление сущностей
Поскольку теперь мы действительно можем подключиться к базе данных, давайте продолжим и попробуем создать объект в базе данных. Для этого мы определим PostgreSqlDao который реализует вышеупомянутый интерфейс Dao
public class PostgreSqlDao implements Dao < private static final Logger LOGGER = Logger.getLogger(PostgreSqlDao.class.getName()); private final Optionalconnection; public PostgreSqlDao() < this.connection = JdbcConnection.getConnection(); >@Override public Optional save(Customer customer) < String message = "The customer to be added should not be null"; Customer nonNullCustomer = Objects.requireNonNull(customer, message); String sql = "INSERT INTO " + "customer(first_name, last_name, email) " + "VALUES(?, ?, ?)"; return connection.flatMap(conn -> < OptionalgeneratedId = Optional.empty(); try (PreparedStatement statement = conn.prepareStatement( sql, Statement.RETURN_GENERATED_KEYS)) < statement.setString(1, nonNullCustomer.getFirstName()); statement.setString(2, nonNullCustomer.getLastName()); statement.setString(3, nonNullCustomer.getEmail()); int numberOfInsertedRows = statement.executeUpdate(); // Retrieve the auto-generated id if (numberOfInsertedRows >0) < try (ResultSet resultSet = statement.getGeneratedKeys()) < if (resultSet.next()) < generatedId = Optional.of(resultSet.getInt(1)); >> > LOGGER.log( Level.INFO, " created successfully? ", new Object[] 0)>); > catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >return generatedId; >); > // Other methods of the interface which currently aren't implemented yet >
После создания Customer вы можете передать его методу save PostgreSqlDao чтобы добавить его в базу данных.
Метод save использует строку SQL для работы:
INSERT INTO customer(first_name, last_name, email) VALUES(?, ?, ?)
Затем, используя соединение с базой данных, DAO подготавливает оператор:
PreparedStatement statement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
Интересно, что оператор содержит флаг Statement.RETURN_GENERATED_KEYS . Это гарантирует, что база данных также сообщает первичный ключ, созданный для новой строки.
Стоит также отметить, что save использует средство сопоставления Java. Он преобразует соединение с базой данных в тип возвращаемого значения, который требуется методу. Более того, он использует функцию flatMap чтобы гарантировать, что возвращаемое значение не имеет Optional оболочки.
Остальные методы CRUD PostgreSqlDao должны следовать той же предпосылке. Они должны сопоставить соединение с возвратом, где это необходимо, и проверить, существует ли соединение, прежде чем работать с ним в противном случае.
Чтение сущностей
В нашей реализации мы решили иметь метод, который возвращает одного Customer на основе его id , и метод, который возвращает всех постоянных клиентов из базы данных.
Начнем с простого .get() который возвращает одного Customer с соответствующим id :
public Optional get(int id) < return connection.flatMap(conn -> < Optionalcustomer = Optional.empty(); String sql = "SELECT * FROM customer WHERE customer_id = " + id; try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) < if (resultSet.next()) < String firstName = resultSet.getString("first_name"); String lastName = resultSet.getString("last_name"); String email = resultSet.getString("email"); customer = Optional.of( new Customer(id, firstName, lastName, email)); LOGGER.log(Level.INFO, "Found in database", customer.get()); > > catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >return customer; >); >
Код довольно прост. Мы выполняем запрос через наш Statement и упаковываем результаты в ResultSet . Затем мы извлекаем информацию из ResultSet и упаковываем ее в конструктор для Customer , который возвращается.
Теперь давайте реализуем метод .getAll() :
public Collection getAll() < Collectioncustomers = new ArrayList<>(); String sql = "SELECT * FROM customer"; connection.ifPresent(conn -> < try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) < while (resultSet.next()) < int String firstName = resultSet.getString("first_name"); String lastName = resultSet.getString("last_name"); String email = resultSet.getString("email"); Customer customer = new Customer(id, firstName, lastName, email); customers.add(customer); LOGGER.log(Level.INFO, "Found in database", customer); > > catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >>); return customers; >
Опять же, довольно просто — мы выполняем соответствующий SQL-запрос, извлекаем информацию, создаем экземпляры Customer и упаковываем их в ArrayList .
Обновление сущностей
Затем, если мы когда-либо захотим обновить сущность после ее создания, нам понадобится метод .update()
public void update(Customer customer) < String message = "The customer to be updated should not be null"; Customer nonNullCustomer = Objects.requireNonNull(customer, message); String sql = "UPDATE customer " + "SET " + "first_name = ?, " + "last_name = ?, " + "email = ? " + "WHERE " + "customer_id = ?"; connection.ifPresent(conn ->< try (PreparedStatement statement = conn.prepareStatement(sql)) < statement.setString(1, nonNullCustomer.getFirstName()); statement.setString(2, nonNullCustomer.getLastName()); statement.setString(3, nonNullCustomer.getEmail()); statement.setInt(4, nonNullCustomer.getId()); int numberOfUpdatedRows = statement.executeUpdate(); LOGGER.log(Level.INFO, "Was the customer updated successfully? ", numberOfUpdatedRows > 0); > catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >>); >
Опять же, мы подготовили оператор и выполнили запрос на обновление на основе полей и id Customer переданного методу обновления.
Удаление объектов
И, наконец, иногда мы можем захотеть удалить объект, и для этого используется метод .delete() :
public void delete(Customer customer) < String message = "The customer to be deleted should not be null"; Customer nonNullCustomer = Objects.requireNonNull(customer, message); String sql = "DELETE FROM customer WHERE customer_id = ?"; connection.ifPresent(conn ->< try (PreparedStatement statement = conn.prepareStatement(sql)) < statement.setInt(1, nonNullCustomer.getId()); int numberOfDeletedRows = statement.executeUpdate(); LOGGER.log(Level.INFO, "Was the customer deleted successfully? ", numberOfDeletedRows > 0); > catch (SQLException ex) < LOGGER.log(Level.SEVERE, null, ex); >>); >
Опять же, на основе id Customer выполняется запрос на удаление для удаления объекта.
Запуск приложения
После детализации реализации DAO проекту теперь нужна точка входа. Лучшее место для этого было бы в main статическом методе:
public class CustomerApplication < private static final Logger LOGGER = Logger.getLogger(CustomerApplication.class.getName()); private static final DaoCUSTOMER_DAO = new PostgreSqlDao(); public static void main(String[] args) < // Test whether an exception is thrown when // the database is queried for a non-existent customer. // But, if the customer does exist, the details will be printed // on the console try < Customer customer = getCustomer(1); >catch (NonExistentEntityException ex) < LOGGER.log(Level.WARNING, ex.getMessage()); >// Test whether a customer can be added to the database Customer firstCustomer = new Customer("Manuel", "Kelley", " [email protected] "); Customer secondCustomer = new Customer("Joshua", "Daulton", " [email protected] "); Customer thirdCustomer = new Customer("April", "Ellis", " [email protected] "); addCustomer(firstCustomer).ifPresent(firstCustomer::setId); addCustomer(secondCustomer).ifPresent(secondCustomer::setId); addCustomer(thirdCustomer).ifPresent(thirdCustomer::setId); // Test whether the new customer's details can be edited firstCustomer.setFirstName("Franklin"); firstCustomer.setLastName("Hudson"); firstCustomer.setEmail(" [email protected] "); updateCustomer(firstCustomer); // Test whether all customers can be read from database getAllCustomers().forEach(System.out::println); // Test whether a customer can be deleted deleteCustomer(secondCustomer); > // Static helper methods referenced above public static Customer getCustomer(int id) throws NonExistentEntityException < Optionalcustomer = CUSTOMER_DAO.get(id); return customer.orElseThrow(NonExistentCustomerException::new); > public static Collection getAllCustomers() < return CUSTOMER_DAO.getAll(); >public static void updateCustomer(Customer customer) < CUSTOMER_DAO.update(customer); >public static Optional addCustomer(Customer customer) < return CUSTOMER_DAO.save(customer); >public static void deleteCustomer(Customer customer) < CUSTOMER_DAO.delete(customer); >>
Поскольку методы CRUD из PostgreSqlDao являются общедоступными, мы обернем его, чтобы предотвратить раскрытие уровня базы данных остальной части кода, когда он не нужен.
После этого необходимо добавить еще два настраиваемых класса исключений. Это NonExistentEntityException :
public class NonExistentEntityException extends Throwable < private static final long serialVersionUID = -3760558819369784286L; public NonExistentEntityException(String message) < super(message); >>
И его наследник NonExistentCustomerException :
public class NonExistentCustomerException extends NonExistentEntityException < private static final long serialVersionUID = 8633588908169766368L; public NonExistentCustomerException() < super("Customer does not exist"); >>
Эти два класса обрабатывают исключения, которые DAO генерирует, когда Customer не существует, чтобы сделать обработку исключений более удобной.
Заключение
Мы видели, как создать приложение CRUD на основе Postgres. Эти шаги показывают, что на самом деле настроить серверную часть Postgres — нетрудно. Привязка модели предметной области Java к соединению с базой данных Postgres требует немного больше работы. Это потому, что передовая практика требует разделения слоев и сокрытия информации .
Вы можете найти весь код проекта на GitHub .
Licensed under CC BY-NC-SA 4.0