JDBC 🆚 Hibernate: как Java работает с базами — и чем это тебе грозит
Java умеет работать с БД как вручную (через JDBC), так и “по-умному” (через Hibernate/JPA).
Один путь — контроль и боль. Второй — магия и её последствия.
Разберём всё по фактам, на живых примерах👇
🔌 JDBC — низкоуровневый доступ через SQL
🟢 Всё вручную: от подключения до маппинга.
🟢 Полный контроль, но каждый чих — в 5 строк.
🟢 Если ты решил добавить
🏗 Hibernate / JPA — ORM и сущности
🟢 SQL скрыт под капотом, данные — это Java-объекты.
🟢 Быстро, декларативно, красиво.
🟢 Но ты больше не контролируешь, что именно улетает в базу.
💥 Пример боли: Lazy vs Eager
🟢 Если сессия закрыта — Hibernate не подгрузит данные.
🟢 Работает только внутри транзакции.
🟢 Или делай
🛠 Spring + JPARepository — удобный уровень
➡️ Никаких SQL, методы создаются из названия.
➡️ CRUD, пагинация, фильтры — всё из коробки.
➡️ Но если нужен ручной SQL — Spring Data позволяет это тоже:
⚙️ JdbcTemplate — между ручным и магическим
➡️ Ты пишешь SQL, но не паришься с ResultSet.
🟢 Оптимально для кастомных запросов.
🧪 Тестирование — через @DataJpaTest и H2
➡️ H2 работает в памяти, быстро поднимается и убивается.
🟢 Подходит для unit+integration тестов одновременно.
🗣️ Запомни: JDBC — как нарезать салат вручную: аккуратно, но долго. Hibernate — как блендер: быстро, но иногда не туда. Выбирай по задаче, а не по вкусу архитектора.
Java умеет работать с БД как вручную (через JDBC), так и “по-умному” (через Hibernate/JPA).
Один путь — контроль и боль. Второй — магия и её последствия.
Разберём всё по фактам, на живых примерах
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
stmt.setInt(1, 42);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("name"));
}
JOIN — добро пожаловать в SQL-приключения.🏗 Hibernate / JPA — ORM и сущности
@Entity
public class User {
@Id
private Long id;
private String name;
}
User user = entityManager.find(User.class, 42L);
💥 Пример боли: Lazy vs Eager
@Entity
public class Post {
@ManyToOne(fetch = FetchType.LAZY)
private User author;
}
Post post = postRepo.findById(1L).get();
System.out.println(post.getAuthor().getName()); // 💣 LazyInitializationException
JOIN FETCH, или не забывай про открытые сессии.public interface UserRepo extends JpaRepository<User, Long> {
List<User> findByName(String name);
}@Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
List<User> search(@Param("name") String name);List<User> users = jdbcTemplate.query(
"SELECT * FROM users",
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))
);
@DataJpaTest
class RepoTest {
@Autowired UserRepo repo;
@Test
void testSave() {
repo.save(new User("Test"));
assertTrue(repo.findByName("Test").size() > 0);
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍9🔥4😇1
Java не умеет JSON "из коробки", но есть два монстра: Jackson и Gson.
Оба читают и пишут JSON. Оба работают с POJO. Но подходы — разные.
Покажу, как это работает в реальных проектах
ObjectMapper mapper = new ObjectMapper();
User user = new User("Jon", 42);
String json = mapper.writeValueAsString(user);
User copy = mapper.readValue(json, User.class);
writeValue и readValue.📛 Кастомизация через аннотации:
public class User {
@JsonProperty("username")
private String name;
@JsonIgnore
private String password;
}🧼 Gson — проще, но со своими приколами
Gson gson = new Gson();
User user = new User("Jon", 42);
String json = gson.toJson(user);
User copy = gson.fromJson(json, User.class);
📛 Переименование через
@SerializedName:public class User {
@SerializedName("username")
private String name;
}🔄 Коллекции и вложенные структуры
List<User> users = List.of(new User("Jon", 1), new User("Doe", 2));
String json = mapper.writeValueAsString(users);Gson тоже справится, но если читаешь обратно — будь осторожен с типами:
Type type = new TypeToken<List<User>>(){}.getType();
List<User> list = gson.fromJson(json, type);🧪 Кастомные сериализаторы — когда всё сложно
public class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User u, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(u.getName().toUpperCase());
}
}Используешь, когда стандартная сериализация не подходит:
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new UserSerializer());
mapper.registerModule(module);
| Фича | Jackson | Gson |
| -------------- | ---------- | ----------------------- |
| Скорость | 🟢 Быстрее | 🟡 Средняя |
| Поддержка даты | 🟢 Да | 🔴 Только через адаптер |
| Кастомизация | 🟢 Гибко | 🟡 Просто |
| Работа с Map | 🟢 Легко | 🟡 Можно, но осторожно |
| Вес | 🟡 Большой | 🟢 Маленький |
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥2👎1
Когда Java-приложение готово, остаётся один вопрос: а как его запустить в проде?
Если тебе нужен масштаб, отказоустойчивость и автоматизация — ответ один: Kubernetes.
Показываю, как он работает с Java-приложением — без воды
FROM eclipse-temurin:17
COPY target/app.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
Docker imageapiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: app
image: myorg/java-app:1.0
ports:
- containerPort: 8080
deployment.yaml заменяет тонны bash-скриптов и ручного запускаapiVersion: v1
kind: Service
metadata:
name: java-service
spec:
selector:
app: java-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Service приложение живёт, но недоступно📡 Настройка входа — через Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: java-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: java-service
port:
number: 80
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
➡️ Kubernetes обновляет поды по очереди
🛠 ConfigMap и Secrets — без пересборки образа
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
SPRING_PROFILES_ACTIVE: prod
envFrom:
- configMapRef:
name: app-config
Please open Telegram to view this post
VIEW IN TELEGRAM
101👍12❤3🤯1
Когда у тебя Spring Boot, база, очередь и ещё 2 микросервиса — руками это не запустишь.
Docker Compose — способ задеплоить всё одним махом.
Без скриптов, без флагов, без “а где тут Redis?”. Просто
up -d — и готово version: '3.9'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/demo
- SPRING_DATASOURCE_USERNAME=java
- SPRING_DATASOURCE_PASSWORD=secret
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_DB=demo
- POSTGRES_USER=java
- POSTGRES_PASSWORD=secret
depends_on гарантирует порядок запускаvolumes:
- .:/app
redis:
image: redis:7
ports:
- "6379:6379"
localhost:6379 — и всёSPRING_DATASOURCE_USERNAME=java
SPRING_DATASOURCE_PASSWORD=secret
env_file:
- .env
.env в репозиторий docker compose down -v
-v — чтобы не остался мусор от базыPlease open Telegram to view this post
VIEW IN TELEGRAM
❤9👍8
Оба запускают контейнеры.
Оба читают YAML.
Но у одного — кнопка "всё просто", а у второго — масштаб и продакшен.
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
☸️ Kubernetes: для продакшена и масштабов
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
template:
spec:
containers:
- name: java
image: myapp:1.0
ports:
- containerPort: 8080
kubectl, ingress, сервисов, конфигов📡 Сеть: как доступен сервис
Compose:
ports:
- "8080:8080"
Kubernetes:
kind: Service
spec:
type: LoadBalancer
🔐 Переменные и секреты
Compose:
env_file:
- .env
Kubernetes:
envFrom:
- configMapRef:
name: app-config
Compose:
docker compose down && docker compose up -d
Kubernetes:
strategy:
type: RollingUpdate
main(). Kubernetes — как запускать через продакшеновый оркестр с логами, шинами и охраной.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍3❤2
Media is too big
VIEW IN TELEGRAM
В этом видео автор объясняет, что такое переменные и типы данных в Java.
Разбирается, как в языке хранится информация, какие бывают типы (int, double, boolean и др.) и как правильно их использовать.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4😇1
В Java API Gateway — это слой, который принимает все запросы к микросервисам, решает, куда их направить, и что с ними сделать по пути.
Он не выполняет бизнес-логику — он управляет потоком данных и правилами доступа.
📛 Пример: простейший роутинг
public class ApiGateway {
public void handleRequest(String path) {
if (path.startsWith("/users")) {
userService();
} else if (path.startsWith("/orders")) {
orderService();
} else {
notFound();
}
}
void userService() { System.out.println("👥 Обработка пользователей"); }
void orderService() { System.out.println("📦 Обработка заказов"); }
void notFound() { System.out.println("❌ Маршрут не найден"); }
}public void handleRequest(String path, String token) {
if (!isValidToken(token)) {
System.out.println("⛔️ Доступ запрещён");
return;
}
if (path.startsWith("/admin")) {
adminService();
} else {
publicService();
}
}
boolean isValidToken(String token) {
return token != null && token.equals("secret");
}
void adminService() { System.out.println("🔐 Админ-панель"); }
void publicService() { System.out.println("🌍 Публичный API"); }Смотри, нужно добавить кеширование без изменения кода сервисов:
import java.util.HashMap;
import java.util.Map;
public class CachingGateway {
private Map<String, String> cache = new HashMap<>();
public String handle(String path) {
if (cache.containsKey(path)) {
return "⚡️ Из кеша: " + cache.get(path);
}
String response = callService(path);
cache.put(path, response);
return response;
}
String callService(String path) {
return "Данные для " + path;
}
}
public class AggregationGateway {
public String aggregate() {
String users = getUsers();
String orders = getOrders();
return "👥 " + users + " | 📦 " + orders;
}
String getUsers() { return "5 пользователей"; }
String getOrders() { return "12 заказов"; }
}public class SecureLoggingGateway {
public void handle(String path, String token) {
log(path);
if (!isValidToken(token)) return;
route(path);
}
void log(String path) { System.out.println("📜 Лог: " + path); }
boolean isValidToken(String token) { return "secret".equals(token); }
void route(String path) { System.out.println("➡️ Роутинг к " + path); }
}public enum Route {
USERS, ORDERS, STATS
}
public class EnumGateway {
public void dispatch(Route r) {
switch (r) {
case USERS -> System.out.println("👥 Пользователи");
case ORDERS -> System.out.println("📦 Заказы");
case STATS -> System.out.println("📊 Статистика");
}
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3
🧵 Structured Concurrency в Java — управление задачами в одном scope, без утечек и хаоса
Structured Concurrency — это модель, где все задачи живут в одном «контейнере».
Падает одна — падают все. Контейнер закрыт — задачи завершены.
📁 Шаг 1: проблема без структуры
➡️ Потоки живут сами по себе, ошибки не передаются, ресурсы не контролируются.
📁 Шаг 2: ждём задачи вручную
➡️ Мы можем подождать завершения, но всё ещё нет общей отмены при ошибке.
📁 Шаг 3: общий пул потоков
➡️ Теперь задачи в одном месте, можно управлять ими через Futures.
📁 Шаг 4: обработка ошибок централизованно
➡️ Если одна задача падает — отменяем все остальные.
📁 Шаг 5: таймауты для всех задач
➡️ Через 1 секунду все незавершённые задачи отменяются.
📁 Шаг 6: try-with-resources для пула
➡️ При выходе из блока пул и все задачи закрываются автоматически.
📁 Шаг 7: объединение результатов
➡️ Structured Concurrency упрощает сбор данных от разных задач.
📁 Шаг 8: CompletableFuture для асинхронщины
➡️ Задачи стартуют параллельно, и можно дождаться всех вместе.
📁 Шаг 9: join с обработкой ошибок
➡️ Ждём обе задачи; если одна упадёт — получим исключение и сможем отменить вторую.
📁 Шаг 10: собственный scope-класс
➡️ Теперь у нас есть контейнер для задач, который сам их отменит при закрытии.
📁 Шаг 11: запуск в scope
➡️ Выйдя из try — все задачи будут остановлены.
📁 Шаг 12: добавляем логику таймаута внутрь scope
➡️ Теперь каждая задача в scope может иметь свой лимит времени.
📁 Шаг 13: завершение
➡️ Structured Concurrency даёт полный контроль над жизнью задач — от запуска до завершения.
🗣️ Запомни: Structured Concurrency — это про контроль и предсказуемость.
Все задачи в одном scope → нет висящих потоков, нет утечек, нет забытых таймаутов.
Structured Concurrency — это модель, где все задачи живут в одном «контейнере».
Падает одна — падают все. Контейнер закрыт — задачи завершены.
Thread t1 = new Thread(() -> fetchUsers());
Thread t2 = new Thread(() -> fetchOrders());
t1.start(); t2.start();
t1.join();
t2.join();
ExecutorService ex = Executors.newFixedThreadPool(2);
Future<String> f1 = ex.submit(() -> fetchUsers());
Future<String> f2 = ex.submit(() -> fetchOrders());
try {
String u = f1.get();
String o = f2.get();
} catch (Exception e) {
f1.cancel(true);
f2.cancel(true);
}List<Future<String>> results = ex.invokeAll(tasks, 1, TimeUnit.SECONDS);
try (ExecutorService ex2 = Executors.newFixedThreadPool(2)) {
// запуск задач
}String combined = u + " | " + o;
System.out.println(combined);
CompletableFuture<String> u = CompletableFuture.supplyAsync(() -> fetchUsers());
CompletableFuture<String> o = CompletableFuture.supplyAsync(() -> fetchOrders());
CompletableFuture.allOf(u, o).join();
class TaskScope implements AutoCloseable {
ExecutorService ex = Executors.newCachedThreadPool();
List<Future<?>> futures = new ArrayList<>();
<T> Future<T> fork(Callable<T> c) { var f = ex.submit(c); futures.add(f); return f; }
public void close() { futures.forEach(f -> f.cancel(true)); ex.shutdownNow(); }
}try (TaskScope scope = new TaskScope()) {
var f1 = scope.fork(() -> fetchUsers());
var f2 = scope.fork(() -> fetchOrders());
}Future<T> forkWithTimeout(Callable<T> c, long t, TimeUnit u) {
return ex.submit(() -> ex.submit(c).get(t, u));
}System.out.println("✅ Все задачи завершены в одном месте");Все задачи в одном scope → нет висящих потоков, нет утечек, нет забытых таймаутов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤2
CI/CD позволяет запускать сборку, тесты и деплой без участия человека.
Но если ты подключаешь CI/CD к проекту — ты отмечаешь его как “готовый к автоматизации”.
Это и есть CI/CD — конвейер, который действует по событиям.
📛 Пример: сборка и тесты в Jenkins
pipeline {
agent any
stages {
stage('Build') { steps { sh 'mvn clean package' } }
stage('Test') { steps { sh 'mvn test' } }
}
}push в main
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '17'
- run: mvn clean verify
Смотри, тебе нужно различать коммиты с кодом и релизы.
Не хочешь вручную запускать тесты перед релизом? — решается одной конфигурацией.
on:
push:
tags:
- 'v*'
- name: Deploy
run: ssh user@server 'cd /app && git pull && ./restart.sh'
- run: mvn test # тесты
- run: docker build # сборка контейнера
- run: helm upgrade # деплой в Kubernetes
- uses: hashicorp/setup-terraform@v2
- run: terraform apply -auto-approve
Оно не думает, а действует. Всё, что нужно — событие в репозитории.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3❤🔥2
Java даёт три больших способа хранить данные: массивы (
Array), строки (String) и коллекции (ArrayList, HashMap).Каждый из них — под свой сценарий.
int[] numbers = {1, 2, 3};
System.out.println(numbers[0]); // 1
numbers[1] = 42; // меняем элемент📜 Строки — неизменяемые последовательности символов
String text = "Hello";
String upper = text.toUpperCase();
System.out.println(text); // Hello
System.out.println(upper); // HELLO
String в Java — immutable. Любое «изменение» создаёт новую строку. Для частых правок есть StringBuilder.📋 ArrayList — динамический список
import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
System.out.println(list.get(1)); // Python
list.remove(0); // удалили "Java"
import java.util.HashMap;
HashMap<String, Integer> ages = new HashMap<>();
ages.put("Alice", 25);
ages.put("Bob", 30);
System.out.println(ages.get("Bob")); // 30
ArrayList<int[]> matrix = new ArrayList<>();
matrix.add(new int[]{1, 2, 3});
matrix.add(new int[]{4, 5, 6});
System.out.println(matrix.get(1)[2]); // 6
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤5👏2⚡1
Media is too big
VIEW IN TELEGRAM
Это видео посвящено циклам в языке Java — фундаментальной теме для любого разработчика. Ты научишься использовать циклы for, while и do-while, а также освоишь операторы управления потоком внутри циклов.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍3
Сборка руками? Заливка через FTP?
Jenkins автоматизирует всё — от сборки до деплоя, пока ты пьёшь кофе ☕️
Вот как это работает на Java
Jenkinsfile — это сценарий, который описывает, что и как делать с кодом.
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('Deploy') {
steps {
sh 'scp build/libs/app.jar user@server:/app/'
}
}
}
}🟢 Костыльные .bat скрипты и ручной scp
🟢 Кто-то залил баг — тесты никто не запускал
🟢 Ночью прод лег, никто не понял, почему
🟢 git push → Jenkins билдит и пушит сам🟢 Ошибся в тестах? Не пройдёшь дальше🟢 Всё логируется, откатывается и работает одинаково везде
Сборка Java проекта в пайплайне
stage('Build') {
steps {
sh './mvnw package -DskipTests'
}
}.jar или .war самstage('Test') {
steps {
sh './mvnw test'
}
}stage('Deploy') {
steps {
sh 'scp target/app.jar user@host:/srv/'
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥6❤5
В Java массивы — фиксированные, а коллекции умеют расти, менять размер и работать с разными типами данных.
ArrayList и HashMap — два столпа, на которых держатся 80% задач.import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
ArrayList хранит элементы в порядке добавления и умеет расти автоматически.String lang = list.get(0);
System.out.println(lang); // Java
for (String l : list) {
System.out.println(l);
}for-each для чтения всех значений без возни с индексами.import java.util.HashMap;
HashMap<String, Integer> map = new HashMap<>();
map.put("Apples", 5);
map.put("Oranges", 3);
HashMap — это ключ-значение. Ключи уникальны, значения можно повторять.int count = map.get("Apples");
System.out.println(count); // 5if (map.containsKey("Oranges")) {
System.out.println("Есть апельсины!");
}false, а не ошибку.for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
} ArrayList и HashMap дают гибкость. Используй массивы, когда важна скорость и фиксированный размер, а коллекции — когда нужно удобно, динамично и без лишней возни.Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤5🔥4
Media is too big
VIEW IN TELEGRAM
(if-else, switch-case)Это видео объясняет, как работают условные операторы в Java. Ты узнаешь, как с помощью if, else if, else и switch-case принимать решения в коде и управлять логикой программы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3🔥2
Дженерики позволяют определить параметр типа, который будет заменен конкретным типом данных при создании экземпляра класса или вызове метода. Например,
List<T> может использоваться как List<String>, List<Integer> и т.д.Благодаря дженерикам, ошибки типа (например, попытка вставить объект неправильного типа) обнаруживаются на этапе компиляции, а не во время выполнения программы.
Один универсальный класс или метод может работать с различными типами данных, что позволяет избежать дублирования кода.
Во время компиляции информация о типах стирается, и вместо этого используется базовый тип, что позволяет сохранять совместимость с кодом, написанным до появления дженериков.
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String str = stringBox.getItem();
В этом примере класс
Box<T> является обобщенным и может работать с любым типом данных, который заменит T при создании объекта.#java #generics #T
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤5🔥4
java.util. Он облегчает создание текстовых последовательностей, объединяя строки и вставляя разделители между ними.Вызов метода
toString() возвращает объединенную строку с разделителями и окружающими символами.Вы также можете использовать метод
setEmptyValue() для определения значения, которое будет использоваться, если StringJoiner остается пустым.
StringJoiner sj = new StringJoiner(", ", "[" , "]");
sj.setEmptyValue("No items");
System.out.println(sj.toString()); // No items
sj.add("Apple").add("Banana").add("Cherry");
System.out.println(sj.toString()); // [Apple, Banana, Cherry]
#java #StringJoiner
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍9🔥4
Когда у тебя микросервисы, баги превращаются в квест.
Клиент сделал запрос➡️ прошёл через API➡️ сервис авторизации➡️ сервис платежей➡️ сервис нотификаций.
А потом где-то что-то упало. Где искать?
Логов тонны, grep не спасает.
Тут появляется Zipkin — Sherlock Holmes твоей системы.
📦 Поднять Zipkin проще простого
Через Docker:
docker run -d -p 9411:9411 openzipkin/zipkin
Zipkin использует концепцию trace и span:
🟢 trace = весь путь запроса от начала до конца🟢 span = отдельный шаг внутри пути (запрос к БД, вызов другого сервиса)
Каждый сервис при вызове следующего передаёт trace-id и span-id в заголовках (обычно
X-B3-TraceId, X-B3-SpanId).Добавляешь зависимости:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
А в
application.yml:spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
[traceId, spanId].2025-08-22 12:42:01 [traceId=4af3, spanId=1a2b] Calling PaymentService
🎯 Зачем это реально нужно
👍 🕵️ Находить медленные места (Bottlenecks)👍 🐛 Локализовать баг в цепочке сервисов👍 📊 Смотреть статистику по задержкам и ошибкам
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍7
Когда у тебя миллион запросов и сервер начинает задыхаться — обычный
Spring MVC с потоками умирает. Тут выходит на сцену Spring WebFlux и движок Project Reactor. Асинхронщина, backpressure, и код, который реально дышит под нагрузкой.pom.xml:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
DispatcherServlet — реактивный движок Netty.@RestController
public class HelloController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("👋 Привет, реактивный мир!");
}
}
Mono = 0..1 значение (как Future). Flux = поток значений.📛 Пример со стримом чисел:
@GetMapping("/numbers")
public Flux<Integer> numbers() {
return Flux.range(1, 5).delayElements(Duration.ofSeconds(1));
}Flux.just("A", "B", "C")
.map(s -> s.toLowerCase())
.filter(s -> !s.equals("b"))
.subscribe(System.out::println);Flux.interval(Duration.ofMillis(10))
.onBackpressureDrop()
.subscribe(i -> System.out.println("⚡️ " + i));
@Repository
public interface UserRepo extends ReactiveCrudRepository<User, String> {}
@GetMapping("/users")
public Flux<User> getUsers() {
return userRepo.findAll();
}
Чем отличается WebFlux 🆚 Project Reactor?
👍 Project Reactor = библиотека реактивных стримов (Mono, Flux, операторы).👍 WebFlux = фреймворк на базе Reactor, чтобы писать контроллеры, API и сервисы.
📚 Документация:
👉 Reactor👈 👉 WebFlux👈
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤5👍5
Media is too big
VIEW IN TELEGRAM
В этом видео разбираются основы работы с массивами в Java. Автор показывает, как создавать и использовать одномерные и многомерные массивы, как обращаться к их элементам и перебирать данные. Это базовый инструмент, без которого невозможно работать с большими наборами информации.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4
Если ты думаешь, что GC — это просто «собрал мусор и пошёл дальше», то добро пожаловать в ад продакшена с кучей тредов и 200 ГБ heap’а.
Обычный GC может стопнуть мир на секунду — и твой сервис превращается в «слайдшоу». Для маркетплейса или финтеха — смерть.
Тут на сцену выходит ZGC — сборщик мусора, который обещает паузы меньше 1 мс, даже на огромных хипах.
Запускаешь Java-приложение с флагами:
java -XX:+UseZGC -Xmx16g -Xms16g -jar app.jar
-XX:+UseZGC активирует Z Garbage Collector.Обычный GC работает так: стопнул мир → пересобрал кучу → отпустил.
ZGC же:
🟢 Делает конкурентную маркировку и компактификацию (почти весь GC идёт параллельно с твоим кодом).🟢 Использует цветные указатели (colored pointers), чтобы отслеживать состояние объектов.🟢 Работает без длинных стопов — паузы \~0.5–2 мс даже при 100+ ГБ heap.
Базовые:
-XX:+UseZGC
-XX:ZUncommitDelay=300
-XX:SoftMaxHeapSize=12g
🟢 ZUncommitDelay — через сколько секунд освобождать неиспользуемую память (по умолчанию 300).🟢 SoftMaxHeapSize — мягкий лимит кучи (GC будет стараться держаться в нём, даже если Xmx больше).
Мониторинг:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintGC -Xlog:gc*:gc.log
[2.345s][info][gc,start ] GC(0) Pause Mark Start
[2.345s][info][gc ] GC(0) Concurrent Mark
[2.348s][info][gc ] GC(0) Pause Mark End 0.2ms
[2.349s][info][gc ] GC(0) Concurrent Relocate
Когда брать ZGC, а когда нет
🟢 У тебя огромные хипы (16–500 ГБ).🟢 Важны низкие latency (игры, трейдинг, API).🟢 Ты не хочешь тратить дни на ручной тюнинг старых GC.
🟢 Если у тебя микросервис на 512 МБ heap (Overkill).🟢 Если нужен максимальный throughput (G1 иногда быстрее на маленьких heap’ах).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤4🔥4👏1
Media is too big
VIEW IN TELEGRAM
В этом видео автор подробно объясняет, как работает Collections Framework в Java.
Пошагово демонстрируются основные интерфейсы и реализации: List, Set, Map, а также важные классы вроде ArrayList, HashSet, HashMap. Показано, как правильно выбирать подходящую коллекцию, когда использовать список, а когда — отображение, как добавлять, удалять и перебирать элементы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3🔥3😁1