Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b0c62c7

Browse files
Add files and folders ConnectionPoolLess of JDBC Practice.
1 parent fda3af7 commit b0c62c7

File tree

4 files changed

+336
-0
lines changed

4 files changed

+336
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
db.url=jdbc:postgresql://localhost:5432/flight_repository
2+
db.username=postgres
3+
db.password=testadmin
4+
db.pool.size=5
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Тестируем наш пул соединений.
3+
4+
При этом не забываем подключить в настройках
5+
драйвер соединения с базой данных (*.jar),
6+
который лежит в папке 'lib' проекта 'JDBCLessonOne',
7+
иначе словим исключение. Не забываем пометить
8+
папку 'resources', как ресурсную.
9+
*/
10+
import connection_util.ConnectionPoolManager;
11+
12+
import java.sql.Connection;
13+
import java.sql.ResultSet;
14+
import java.sql.SQLException;
15+
import java.sql.Statement;
16+
17+
public class CPMTestApp {
18+
19+
public static void main(String[] args) throws SQLException{
20+
/*
21+
Вставляем данные в таблицу 'info' схемы
22+
'train_base'. Вставляем только данные, т.к.
23+
id у нас автоинкрементируется. И вот его
24+
значение мы и хотим получить.
25+
*/
26+
String sql_insert_query = """
27+
INSERT INTO train_base.info (data)
28+
VALUES
29+
('autogenerated_insert_data')
30+
""";
31+
/*
32+
В блоке try-with-resources создаем соединение с базой,
33+
и создаем объект Statement для отправки SQL операторов
34+
(запросов) в базу данных.
35+
*/
36+
try (Connection connection = ConnectionPoolManager.getConnectionFromPool();
37+
/*
38+
TYPE_SCROLL_INSENSITIVE - Константа, указывающая тип объекта ResultSet,
39+
который можно прокручивать, но обычно не
40+
чувствителен к изменениям данных, лежащих в
41+
основе ResultSet.
42+
43+
Т.е. мы можем свободно перемещаться по нашему
44+
полученному SET-у и вверх и вниз, а не только
45+
в одну сторону например FETCH_FORWARD.
46+
47+
И можем использовать методы для установки курсора:
48+
*.afterLast(), *.last(), *.beforeFirst(), *.first()
49+
и т.д. Например: executeResult.last()
50+
51+
CONCUR_UPDATABLE - Константа, указывающая режим параллелизма для объекта
52+
ResultSet, который может быть обновлен.
53+
54+
Т.е. мы можем используя методы *.update...() производить
55+
изменения в таблицах базы данных.
56+
Например: executeResult.updateLong("id", 1000);
57+
*/
58+
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
59+
ResultSet.CONCUR_UPDATABLE)) {
60+
/*
61+
В данном случе применяем для разнообразия не конкретный класс/
62+
интерфейс int или ResultSet, а var.
63+
64+
Начиная с версии 10, в Java появилось ключевое слово var. Новая
65+
фича — local variable type inference (выведение типа локальной
66+
переменной) — не даёт переменным дополнительных возможностей и
67+
ограничений на них не накладывает. Просто разработчикам не нужно
68+
теперь писать лишний код при объявлении переменных, когда их тип
69+
очевиден из контекста.
70+
71+
Метод int executeUpdate(String sql, int autoGeneratedKeys) -
72+
Выполняет запрос SQL и сигнализирует драйверу с заданным флагом о
73+
том, следует ли сделать автоматически сгенерированные ключи,
74+
созданные этим объектом оператора, доступными для извлечения.
75+
76+
RETURN_GENERATED_KEYS - Константа, указывающая, что сгенерированные
77+
ключи должны быть доступны для извлечения.
78+
*/
79+
var executeResult = statement.executeUpdate(sql_insert_query,
80+
Statement.RETURN_GENERATED_KEYS);
81+
/*
82+
ResultSet getGeneratedKeys() - Извлекает любые автоматически
83+
сгенерированные ключи, созданные
84+
в результате выполнения этого
85+
объекта оператора.
86+
*/
87+
var generatedKeys = statement.getGeneratedKeys();
88+
/*
89+
Если новый ключ сгенерирован, т.е. перед курсором есть запись,
90+
то извлекаем значение ключа. Поскольку в нашем запросе мы точно
91+
знаем, что сделали одну вставку и будет сгенерирован лишь один
92+
ключ, мы не пользуемся циклом для получения всего SET-а, вернее
93+
наш SET будет содержать только одно значение. Его мы и извлекаем.
94+
95+
Если бы мы вставляли много записей и было сгенерировано n-ключей,
96+
мы бы применили цикл для чтения содержимого всего SET-a.
97+
*/
98+
if (generatedKeys.next()) {
99+
var generatedId = generatedKeys.getInt("id");
100+
System.out.println("Только что добавили запись с id:" + generatedId);
101+
/*
102+
Мы так же можем использовать порядковый номер колонки в ResultSet,
103+
у колонки-поля id порядковый номер - 1 у поля data - 2
104+
*/
105+
var generatedId_by_num = generatedKeys.getInt(1);
106+
System.out.println("Только что добавили запись в поле 1:" + generatedId_by_num);
107+
}
108+
109+
}
110+
}
111+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package connection_util;
2+
3+
import java.lang.reflect.Proxy;
4+
import java.sql.Connection;
5+
import java.sql.DriverManager;
6+
import java.sql.SQLException;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.concurrent.ArrayBlockingQueue;
10+
import java.util.concurrent.BlockingQueue;
11+
12+
public final class ConnectionPoolManager {
13+
/* Извлекаем данные из application.properties */
14+
private static final String PASSWORD_KEY = "db.password";
15+
private static final String USERNAME_KEY = "db.username";
16+
private static final String URL_KEY = "db.url";
17+
private static final String POOL_SIZE_KEY = "db.pool.size";
18+
/* Задаем размер пула по-умолчанию */
19+
private static final Integer DEFAULT_POOL_SIZE = 10;
20+
/*
21+
Потокобезопасная блокирующая очередь для
22+
хранения наших прокси - соединений
23+
*/
24+
private static BlockingQueue<Connection> pool;
25+
/* Список наших реальных соединений */
26+
private static List<Connection> sourceConnections;
27+
28+
static {
29+
loadDriver();
30+
initConnectionPool();
31+
}
32+
/* Приватный конструктор данного класса */
33+
private ConnectionPoolManager() {
34+
}
35+
36+
private static void initConnectionPool() {
37+
/* Извлекаем значение размера будущего пула соединений из файла*/
38+
String poolSize = PropertiesUtil.get(POOL_SIZE_KEY);
39+
/*
40+
Существует вероятность, что в ходе извлечения данных из файла
41+
свойств, либо в момент преобразования данных в int может произойти
42+
ошибка (вот какая интересно?). Для того чтобы этого избежать
43+
воспользуемся тернарным оператором и как вариант подставим дефолтный
44+
размер пула.
45+
*/
46+
int size = poolSize == null ? DEFAULT_POOL_SIZE : Integer.parseInt(poolSize);
47+
/* Создаем очередь для хранения наших прокси - соединений */
48+
pool = new ArrayBlockingQueue<>(size);
49+
/*
50+
Создаем список для хранения наших реальных соединений.
51+
К нему мы обратимся в момент закрытия всех реальных
52+
соединений.
53+
*/
54+
sourceConnections = new ArrayList<>(size);
55+
/*
56+
В цикле создаем реальное и прокси - соединение с базой.
57+
Помещаем и то и другое в свои коллекции данных.
58+
*/
59+
for (int i = 0; i < size; i++) {
60+
Connection connection = openConnection();
61+
/*
62+
В общем случае прокси-объект - это объект, который служит
63+
посредником для доступа к другому объекту, каким-то образом
64+
меняя свойства или поведение этого объекта.
65+
66+
Так что со стороны клиента (т.е. объекта-пользователя)
67+
поведение выглядит несколько не так, как было бы при
68+
непосредственном доступе.
69+
70+
Используется также в случаях, когда прямой доступ к
71+
используемому объекту по какой-то причине невозможен.
72+
('прокси - объект' - паттерн проектирования)
73+
74+
Самое простое — использовать java.lang.reflect.Proxy,
75+
который является частью JDK. Этот класс может создать
76+
прокси-класс или напрямую его инстанс.
77+
78+
Пользоваться прокси, встроенным в Java, очень просто.
79+
Все что нужно — реализовать java.lang.InvocationHandler,
80+
чтобы прокси-объект мог его вызывать.
81+
82+
Интерфейс InvocationHandler крайне прост и содержит только
83+
один метод: invoke(). При его вызове, аргументы содержат
84+
проксируемый оригинальный объект, вызванный метод
85+
(как отражение объекта Method) и массив объектов исходных
86+
аргументов.
87+
*/
88+
Connection proxyConnection = (Connection) Proxy.newProxyInstance(
89+
ConnectionPoolManager.class.getClassLoader(),
90+
new Class[]{Connection.class},
91+
(proxy, method, args) -> method.getName().equals("close")
92+
? pool.add((Connection) proxy)
93+
: method.invoke(connection, args));
94+
pool.add(proxyConnection);
95+
sourceConnections.add(connection);
96+
}
97+
}
98+
/* Методы для получения прокси - соединения из пула */
99+
public static Connection getConnectionFromPool() {
100+
try {
101+
return pool.take();
102+
} catch (InterruptedException e) {
103+
throw new RuntimeException(e);
104+
}
105+
}
106+
/*
107+
Приватный метод для создания соединения с базой данных
108+
сделанный для защиты от случайного создания соединения
109+
сверх размера пула.
110+
*/
111+
private static Connection openConnection() {
112+
try {
113+
return DriverManager.getConnection(
114+
PropertiesUtil.get(URL_KEY),
115+
PropertiesUtil.get(USERNAME_KEY),
116+
PropertiesUtil.get(PASSWORD_KEY)
117+
);
118+
} catch (SQLException e) {
119+
throw new RuntimeException(e);
120+
}
121+
}
122+
/* Загружаем драйвер PostgreSQL */
123+
private static void loadDriver() {
124+
try {
125+
Class.forName("org.postgresql.Driver");
126+
} catch (ClassNotFoundException e) {
127+
throw new RuntimeException(e);
128+
}
129+
}
130+
/* Метод закрывает реальные соединения с базой данных всего пула */
131+
public static void closePool() {
132+
try {
133+
for (Connection sourceConnection : sourceConnections) {
134+
sourceConnection.close();
135+
}
136+
} catch (SQLException e) {
137+
throw new RuntimeException(e);
138+
}
139+
}
140+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package connection_util;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.util.Properties;
6+
7+
public final class PropertiesUtil {
8+
/*
9+
Создаем объект класса Properties - унаследован от Hashtable<Object,Object>.
10+
Его даже можно рассматривать как HashTable, который умеет загружать себя
11+
из файла.
12+
13+
Обычно нам нужно выполнить всего две операции – загрузить в объект Properties
14+
данные из какого-нибудь файла, а затем получить эти свойства с помощью метода
15+
getProperty(). И естественно мы можем пользоваться объектом Properties как
16+
HashMap.
17+
18+
Класс Properties представляет постоянный набор свойств. Свойства можно
19+
сохранить в поток или загрузить из потока. Каждый ключ и соответствующее ему
20+
значение в списке свойств являются строкой.
21+
*/
22+
private static final Properties PROPERTIES = new Properties();
23+
24+
static {
25+
loadProperties();
26+
}
27+
28+
private PropertiesUtil() {
29+
}
30+
/*
31+
Метод позволяющий получить свойства ключей
32+
из файла свойств application.properties
33+
*/
34+
public static String get(String key) {
35+
return PROPERTIES.getProperty(key);
36+
}
37+
38+
private static void loadProperties() {
39+
/*
40+
В блоке try -with-resources получаем (открываем)
41+
входящий поток данных из файла свойств.
42+
43+
1. Метод getClassLoader() класса java.lang.Class
44+
используется для получения classLoader текущего
45+
объекта. Этот объект может быть классом, массивом,
46+
интерфейсом и т. д. Метод возвращает classLoader
47+
этого объекта.
48+
49+
classLoader - это объект, отвечающий за загрузку
50+
классов. Класс ClassLoader является абстрактным
51+
классом. Учитывая двоичное имя класса, загрузчик
52+
класса должен попытаться найти или сгенерировать
53+
данные, составляющие определение класса. Типичная
54+
стратегия состоит в том, чтобы преобразовать имя в
55+
имя файла, а затем прочитать «файл класса» с этим
56+
именем из файловой системы.
57+
58+
2. Метод getResourceAsStream() класса java.lang.Class
59+
используется для получения ресурса с указанным ресурсом
60+
текущего класса. Метод возвращает указанный ресурс
61+
данного класса в виде объекта InputStream.
62+
63+
Метод принимает параметр resourceName, который является
64+
ресурсом для получения данных (например, пары KEY-VALUE,
65+
как у нас).
66+
*/
67+
try (InputStream inputStream = PropertiesUtil.class
68+
.getClassLoader()
69+
.getResourceAsStream("application.properties"))
70+
{
71+
/*
72+
Метод *.load() считывает список свойств
73+
(пары ключей и элементов) из входного
74+
потока байтов.
75+
*/
76+
PROPERTIES.load(inputStream);
77+
} catch (IOException e) {
78+
throw new RuntimeException(e);
79+
}
80+
}
81+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /