Java for Beginner
672 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Интеграция Liquibase с другими инструментами

Maven / Gradle

Maven

Добавьте зависимость в pom.xml:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.25.0</version>
</dependency>


Конфигурация через pom.xml:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>4.25.0</version>
<configuration>
<changeLogFile>src/main/resources/db/changelog/db.changelog-master.yaml</changeLogFile>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<username>user</username>
<password>pass</password>
</configuration>
</plugin>


Запуск миграций:
mvn liquibase:update


Gradle

Добавьте в build.gradle:
plugins {
id 'org.liquibase.gradle' version '2.2.0'
}

dependencies {
liquibaseRuntime 'org.liquibase:liquibase-core:4.25.0'
liquibaseRuntime 'org.postgresql:postgresql:42.6.0'
}

liquibase {
changeLogFile 'src/main/resources/db/changelog/db.changelog-master.yaml'
url 'jdbc:postgresql://localhost:5432/mydb'
username 'user'
password 'pass'
}


Запуск:
gradle liquibaseUpdate


Spring Boot (автоконфигурация)

Spring Boot автоматически настраивает Liquibase, если он обнаружен в classpath.

Настройки в application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: user
password: pass
liquibase:
change-log: classpath:db/changelog/db.changelog-master.yaml
enabled: true
contexts: dev # Фильтрация по контексту


Поведение:
Миграции запускаются при старте приложения.
Можно отключить через spring.liquibase.enabled=false.


Расширенные функции Liquibase

Динамические свойства (property)

Позволяют выносить повторяющиеся значения в переменные.

Пример (YAML):
databaseChangeLog:
- property:
name: default.schema
value: public
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: ${default.schema}.users
columns:
- column:
name: id
type: INT


В
liquibase.properties:
default.schema=public


Через командную строку:
liquibase update -Ddefault.schema=public


Контексты (contexts) и фильтрация changeSet’ов

Контексты позволяют выбирать, какие changeSet’ы применять в определённых окружениях.

Пример (YAML):
- changeSet:
id: 2
author: dev
context: "dev,test" # Будет выполнен только в dev и test
changes:
- insert:
tableName: users
columns:
- column:
name: username
value: "test_user"

- changeSet:
id: 3
author: dev
context: "prod" # Только в production
changes:
- createIndex:
tableName: users
indexName: idx_user_email
columns:
- column:
name: email


Как передать контекст?

Через командную строку:
liquibase update --contexts="dev"


В Spring Boot:
spring:
liquibase:
contexts: dev


#Java #middle #Liquibase
Best Practices

Организация changelog-файлов

Рекомендуемая структура:
src/main/resources/db/changelog/
├── db.changelog-master.yaml # Главный файл
├── changes/
│ ├── 001-create-tables.yaml
│ ├── 002-add-indexes.yaml
│ └── 003-insert-data.yaml
└── rollback/
├── 001-rollback.yaml # Ручные откаты


Главный файл (db.changelog-master.yaml):
databaseChangeLog:
- includeAll:
path: db/changelog/changes/
relativeToChangelogFile: true


Почему так?

Удобно управлять версиями.
Легко находить изменения.


Тестирование миграций

Стратегии:
Локальная проверка:
liquibase validate  # Проверка синтаксиса
liquibase update # Применение в test-DB


Интеграционные тесты (JUnit + Testcontainers):
@Testcontainers
@SpringBootTest
class LiquibaseMigrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

@Test
void testMigrations() {
// Spring Boot автоматически применит миграции
assertTrue(true); # Если не упало — значит, успешно
}
}


Работа в команде (избежание конфликтов)


Правила:

Именование changeSet’ов:
Используйте id: <дата>-<номер> (например, id: 20240315-1).
Указывайте автора (author: github_username).


Порядок изменений:
Не зависеть от порядка выполнения (например, не предполагать, что таблица A уже существует при создании B).

Использование preConditions:
- changeSet:
id: 20240315-1
author: dev
preConditions:
- tableExists:
tableName: users
changes:
- addColumn:
tableName: users
column:
name: phone
type: VARCHAR(20)


Регулярные обновления:
Перед началом работы выполняйте liquibase update, чтобы получить актуальную схему.

#Java #middle #Liquibase
Что выведет код?

import java.util.Optional;

public class Task060625 {
public static void main(String[] args) {
Optional<String> opt = Optional.ofNullable(null)
.or(() -> Optional.of("Java"))
.map(v -> ((String) v).toUpperCase());

System.out.println(opt.orElse("Default"));
}
}


#Tasks
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓

Голосуем за тему к рассмотрению в эти выходные!

Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)

Не стесняемся! ✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое лямбда-выражения в Java? 🤓

Ответ:

Лямбда-выражения в Java — это краткий способ записи анонимных функций, которые можно передавать как аргументы в методы или сохранять в переменные. Они появились в Java 8 и позволяют писать более компактный и читаемый код, особенно при работе с функциональными интерфейсами (интерфейсами с одним абстрактным методом, например, Runnable, Comparator, Predicate).

Синтаксис лямбда-выражения:
(параметры) -> { тело_лямбды }

Если параметр один, скобки () можно опустить.
Если тело состоит из одной строки, фигурные скобки {} и return можно не писать.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
С 31.05 по 06.06
Предыдущий пост(с 24.05 по 30.05)
Следующая неделя

Воскресный мотивационный пост:
«Ты не программируешь — ты притворяешься, что учишься»

Выбранная голосованием тема:
Архитектурный шаблон MVC в Java Spring: теория, правила, ошибки

Запись встреч:
Многопоточка во всей красе. Часть 1.

Обучающие статьи:
Типы changesets и стратегии развертывания в Liquibase
Откаты (Rollback) и теги в Liquibase
Интеграция Liquibase с другими инструментами

Глубокое изучение типа данных boolean в Java
Ссылочные типы данных в Java

Пост под которым нет поздравлений:
Сегодня каналу исполнился год! 🥳

Авторская статья (которая кому-то не понравилась):

Пагинация, которую начинаешь ненавидеть 😵

Полезные статьи и видео:
Создаём HTTP-сервер на Java NIO
Большой гайд. Пишем микросервисы на Java и Spring Boot, заворачиваем в Docker, запускаем на EKS, мониторим на Grafana

ИСТОРИЯ НЕЙРОСЕТЕЙ - ОТ ПЕРЦЕПТРОНА ДО CHATGPT

Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование

#memory
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Настройка OAuth2 для Gmail

Для Gmail предпочтительно использовать OAuth2.

Пример конфигурации:
@Configuration
public class MailConfig {

@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("your.email@gmail.com");

Properties props = mailSender.getJavaMailProperties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");

// Настройка OAuth2 токена через Google API Client Library
// Подробности: https://developers.google.com/identity/protocols/oauth2
return mailSender;
}
}


Примечание: Зарегистрируйте приложение в Google Cloud Console и используйте библиотеку google-auth-library-oauth2-http для получения accessToken.

Создание сервиса отправки почты

Создадим сервис EmailService для отправки писем.

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private TemplateEngine templateEngine;

private static final Logger logger = LoggerFactory.getLogger(EmailService.class);

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}

public void sendHtmlEmail(String to, String subject, String htmlBody) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}

public void sendEmailWithAttachment(String to, String subject, String text, File attachment) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(text);
FileSystemResource file = new FileSystemResource(attachment);
helper.addAttachment(attachment.getName(), file);
mailSender.send(message);
}

@Async
public CompletableFuture<Void> sendSimpleEmailAsync(String to, String subject, String text IMPORTANT: text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
message.setText(text);
mailSender.send(message);
return CompletableFuture.completedFuture(null);
}

public void sendTemplatedEmail(String to, String subject, String username) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
Context context = new Context();
context.setVariable("username", username);
String htmlBody = templateEngine.process("email-template", context);
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}
}


Конфигурация асинхронности:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
}


#Java #middle #on_request #JavaMailSender
Примеры кода

Простой текст:
SimpleMailMessage message = new SimpleMailMessage();
message.setTo("user@example.com");
message.setSubject("Тестовое письмо");
message.setText("Привет из Spring Boot!");
mailSender.send(message);


HTML-письмо:
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("HTML письмо", "UTF-8", null));
helper.setText("<h1>Привет!</h1><p>Это HTML письмо.</p>", true);
mailSender.send(message);


Вложение:

File file = new File("/path/to/file.pdf");
FileSystemResource resource = new FileSystemResource(file);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("Письмо с вложением", "UTF-8", null));
helper.setText("Смотри вложение!");
helper.addAttachment("file.pdf", resource);
mailSender.send(message);


Обработка ошибок и логирование

JavaMailSender может выбрасывать исключения, которые нужно обрабатывать.

Основные исключения:
MailAuthenticationException
MailSendException
MessagingException


Пример отлова:
try {
mailSender.send(message);
} catch (MailException e) {
logger.error("Ошибка отправки письма", e);
throw e;
}


Повторные попытки
Используйте @Retryable из Spring Retry:
@Retryable(value = MailException.class, maxAttempts = 3)
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}


Зависимость:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>


Мониторинг и метрики

Для продакшен-приложений полезно отслеживать метрики отправки писем с помощью Spring Actuator:
@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private MeterRegistry meterRegistry;

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
try {
mailSender.send(message);
meterRegistry.counter("email.sent.success").increment();
} catch (MailException e) {
meterRegistry.counter("email.sent.failure").increment();
throw e;
}
}
}


Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>


Безопасность и защита данных

Никогда не храните пароли в открытом виде в git-репозиториях.

Рекомендации:
Используйте переменные окружения или секреты (например, Kubernetes Secrets).
Применяйте Spring Cloud Vault или HashiCorp Vault.
Используйте шифрование с Jasypt.
Настройте OAuth2 для Gmail.


Пример с переменными окружения:
spring.config.import=optional:configserver:http://config-server
spring.mail.password=${MAIL_PASSWORD}


#Java #middle #on_request #JavaMailSender
Тестирование отправки почты

Интеграционные тесты с Testcontainers:
@Testcontainers
@SpringBootTest
public class EmailServiceTest {

@Container
private static final GreenMailContainer greenMail = new GreenMailContainer();

@Autowired
private EmailService emailService;

@Test
void shouldSendEmail() {
emailService.sendSimpleEmail("test@example.com", "Test Subject", "Test Body");
assertEquals(1, greenMail.getReceivedMessages().length);
}
}


Зависимость:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>greenmail</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>


Unit-тестирование:
@MockBean
private JavaMailSender mailSender;

@Test
void shouldSendMail() {
SimpleMailMessage message = new SimpleMailMessage();
emailService.sendSimpleEmail("to", "subject", "body");
verify(mailSender, times(1)).send(any(SimpleMailMessage.class));
}


Интеграция с очередями

Для отложенной отправки используйте RabbitMQ(ну или другой брокер сообщений):

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private RabbitTemplate rabbitTemplate;

public void sendEmailToQueue(String to, String subject, String text) {
EmailMessage email = new EmailMessage(to, subject, text);
rabbitTemplate.convertAndSend("emailQueue", email);
}

@RabbitListener(queues = "emailQueue")
public void processEmail(EmailMessage email) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email.getTo());
message.setSubject(email.getSubject());
message.setText(email.getText());
mailSender.send(message);
}

@Data
static class EmailMessage {
private String to;
private String subject;
private String text;
}
}


Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>


Распространенные ошибки и подводные камни

Неверная конфигурация SMTP (порт, шифрование).
Проблемы с двухфакторной авторизацией (особенно Gmail).
Ограничения на массовую рассылку (например, Gmail: ~500 писем/день для бесплатных аккаунтов).
Проблемы с кодировкой (например, кириллица в теме письма).
Отсутствие MIME-типа у вложений.
Блокировка сервером (например,
Mail.ru требует DKIM и SPF).

Решение для кодировки:
helper.setSubject(MimeUtility.encodeText("Тема на кириллице", "UTF-8", null));


#Java #middle #on_request #JavaMailSender
Сегодня продолжаем изучение многопоточки

В связи с техническими нюансами, встречу по многопоточке пришлось отменить.

Но мы же не могли оставить Вас без продолжения? 😏

Поэтому @rKiraLis39 записал (за что ему огромное спасибо) для Вас видео в котором мы узнали:

Что такое атомарные операции в потоках
Что такое волатильность и как ее использовать
Как взаимодействовать с Executor'ами и как их настраивать
И много еще интересного

Ссылка на Youtube
Ссылка на Рутьюб

Смотрите, ставьте лайки, подписывайтесь на каналы!
Please open Telegram to view this post
VIEW IN TELEGRAM
«Обучение Java — марафон, а не прыжок с трамплина»

Так вышло, что мое знакомство с Java и программированием в целом, началось еще в 2021 году.
Тогда, окрыленный надеждами на новую, интересную работу, я потратил приличное количество времени на изучение Java Core. Но как часто бывает, реальность продиктовала свои условия, я не смог осилить обучение и бросил его.

Периодически возвращаясь я понял, что придется начинать сначала.


И когда я смог подогнать жизненные условия к тому, чтобы бросить все и посвятить себя только обучению, которое в итоге привело меня сюда, я понял важную вещь - обучение программированию (да наверно любой другой сложной концепции) — это долгая, часто тяжелая дорога, где не будет "легко", но "результат" стоит того.


Вот, что важно понимать если ты только принял решение вступить на этот путь:

🟥 Начнем с массового, современного увлечения курсами, буткемпами, менторством, тренингами и подобной требухой.

Пойми простую вещь - кто обещает «легко и быстро» — 100% врет. Так практически не бывает, за исключением единичных примеров. Но ссылаясь на эти примеры, некоторые строят целые онлайн-школы и бизнес (на твоих деньгах).
И я не говорю, что ты не получишь ничего полезного на этих занятиях. НО! Ты не сможешь, к примеру за 3 месяца, стать конкурентным специалистом в Java, если ты обычный среднестатистический человек.

Ну никак, сорян, кто бы что не говорил 🤷‍♀️


🟥 Ощущение «я тупой» — обязательный этап каждого.

Перед окунанием в "программирование", знай, что к примеру Java, уже отпраздновала 30-летие в этом году. А это, возможно больше чем ты живешь на свете.

А это значит, что изучение её (по крайней мере, как ощущаю это я) - это погружение в бесконечную кроличью нору. 🐇
На каждую понятую тобой концепцию или алгоритм, за его спиной вырастут еще с десяток и ты постоянно будешь ощущать себя неполноценным 😂

Не переживай - это нормально. Все проходят через это 🙂


🟥 Медленный прогресс — это не глупость.

Пойми и прими, то, что порой в Java встречаются темы, которые не взять "с наскока". Ну никак.
И если ты вдруг, ловишь себя на мысли, что на какую-то тему ты тратишь неприлично долгое время - это не значит, что ты "не тянешь".

Просто это тоже нормально. Мы все разные, с разным бекграундом.

Важно не «как быстро», а как долго ты не сливаешься. Ведь в итоге самые стойкие — выигрывают, даже если тупят на старте. 🏝


🟥 Ты просто обязан пройти через говнокод.

Знай, никто не пишет "красиво" и "правильно" с первого раза. Даже "гении".

А ты и я тем более.
Поэтому, как я писал в предыдущей статье - если первые месяцы ты будешь писать грязно, неэффективно и тупо — это тоже нормально и правильно.

Запомни: в начале пути, идеальный код — не твоя цель. Цель — живой и работающий код, а все остальное приложится.🧑‍💻


🟥 Ну и главное - обучение не должно быть "в кайф".

Если тебе в процессе обучения неприятно, неудобно и сложно — значит, ты делаешь правильно. (Особенно это актуально для тех кто за "30" 👋)

Твой мозг вышел из зоны комфорта, он испытывает стресс и пытается повлиять на тебя, вернув в теплые объятия неведения.
Твоя задача - ежедневно, ежеминутно, бороться с ленью, преодолевать алгоритмические и идейные трудности, выстраивая новые нейронные связи.

Комфорт — не показатель правильности выбранной методики обучения. Часто наоборот.


И вот когда ты преодолеешь себя, увидишь как на твою почту придет первый оффер, тогда ты как и я, поймешь, что и не стоило ждать удобств в обучении. (Тем более, что оно всегда будет продолжаться)
Ведь те трудности которые ты преодолел - сформировали из тебя того кто смог пройти собеседование и получить заветную работу.
🥳

Главное вера в себя и свои силы💪

😎

Понравилась статья - поделись с другом и, будет тебе моя благодарность
👏

#motivation
Please open Telegram to view this post
VIEW IN TELEGRAM
Предлагаем темы для разбора и публикации! 📖

В комментариях к данному посту предлагайте вопросы, которые вы хотели бы увидеть максимально подробно разобранными в постах, а если будет интересно то и на видео.

Голосование будет проводиться всю неделю, а статья или видео - выходить по выходным.

Примерные правила:
🟢 темы, не выше уровня middle, чтоб был интерес общим.
🟢 один человек - одна тема.
🟢Тема должна быть отдельным теоретически-практическим вопросом. Готовый проект - это не тема!

Жду Ваших предложений! 👏
Please open Telegram to view this post
VIEW IN TELEGRAM