Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Panache в MongoDB: упрощённое ORM для Quarkus
Panache — это проект в экосистеме Quarkus, который предоставляет упрощённое ORM (Object-Relational Mapping) для работы с MongoDB. Panache делает работу с MongoDB намного проще, чем использование низкоуровневого драйвера, предоставляя удобный API для CRUD операций и запросов.
Паначе поддерживает два подхода:
- Active Record паттерн — методы на самом entity классе
- Repository паттерн — отдельный repository класс
Основная идея
Вместо написания boilerplate кода для подключения, сериализации, десериализации и выполнения запросов, Panache предоставляет удобные методы:
// БЕЗ Panache (низкоуровневый MongoDB драйвер)
MongoClient client = MongoClients.create();
MongoDatabase database = client.getDatabase("mydb");
MongoCollection<Document> collection = database.getCollection("users");
Document doc = new Document("name", "John").append("email", "john@example.com");
collection.insertOne(doc);
// С Panache (высокоуровневый API)
User user = new User();
user.name = "John";
user.email = "john@example.com";
user.persist();
Установка Panache
<!-- pom.xml -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
# application.properties
quarkus.mongodb.connection-string=mongodb://localhost:27017
quarkus.mongodb.database=mydb
Active Record Паттерн
При Active Record паттерне entity наследует PanacheMongoEntity и получает CRUD методы:
import io.quarkus.mongodb.panache.PanacheMongoEntity;
import org.bson.types.ObjectId;
public class User extends PanacheMongoEntity {
public String name;
public String email;
public int age;
public String city;
@Override
public String toString() {
return "User(id=" + id + ", name='" + name + "', email='" + email + "')";
}
}
CRUD операции
@RestController
@RequestMapping("/api/users")
public class UserController {
// CREATE - Создать пользователя
@PostMapping
public ResponseEntity<User> createUser(User user) {
user.persist(); // Сохранить в БД
return ResponseEntity.status(201).body(user);
}
// READ - Получить пользователя по ID
@GetMapping("/{id}")
public User getUser(@PathVariable String id) {
User user = User.findById(new ObjectId(id));
if (user == null) {
throw new NotFoundException("User not found");
}
return user;
}
// READ ALL - Получить всех пользователей
@GetMapping
public List<User> getAllUsers() {
return User.listAll();
}
// UPDATE - Обновить пользователя
@PutMapping("/{id}")
public User updateUser(@PathVariable String id, User updateData) {
User user = User.findById(new ObjectId(id));
if (user == null) {
throw new NotFoundException("User not found");
}
user.name = updateData.name;
user.email = updateData.email;
user.age = updateData.age;
user.update(); // Обновить в БД
return user;
}
// DELETE - Удалить пользователя
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
boolean deleted = User.deleteById(new ObjectId(id));
if (!deleted) {
throw new NotFoundException("User not found");
}
return ResponseEntity.noContent().build();
}
}
Запросы с Panache
Поиск по полям
public class UserController {
// Найти пользователя по email
@GetMapping("/by-email")
public User findByEmail(@RequestParam String email) {
return User.find("email", email).firstResult();
}
// Найти всех пользователей старше 18 лет
@GetMapping("/adults")
public List<User> getAdults() {
return User.find("age > ?1", 18).list();
}
// Найти пользователей в городе
@GetMapping("/by-city/{city}")
public List<User> findByCity(@PathVariable String city) {
return User.find("city", city).list();
}
// Сложный запрос с несколькими условиями
@GetMapping("/search")
public List<User> search(
@RequestParam(required = false) String city,
@RequestParam(required = false) Integer minAge) {
if (city != null && minAge != null) {
return User.find("city = ?1 and age >= ?2", city, minAge).list();
} else if (city != null) {
return User.find("city", city).list();
} else {
return User.listAll();
}
}
}
Пагинация
@GetMapping("/paginated")
public Page<User> getPaginatedUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int pageSize) {
return User.findAll()
.page(Page.of(page, pageSize))
.pageAndCount();
}
// Или простая версия
@GetMapping("/paginated")
public List<User> getPaginatedUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int pageSize) {
return User.findAll()
.page(page * pageSize, pageSize)
.list();
}
Сортировка
@GetMapping("/sorted")
public List<User> getSortedUsers() {
// Сортировка по имени (возрастающая)
return User.find("1=1").sort("name").list();
}
@GetMapping("/sorted-desc")
public List<User> getSortedDescending() {
// Сортировка по возрасту (убывающая)
return User.find("1=1").sort("age desc").list();
}
@GetMapping("/top-users")
public List<User> getTopUsers() {
// Самые молодые 5 пользователей
return User.find("1=1").sort("age asc").page(0, 5).list();
}
Repository Паттерн
Вместо Active Record можно использовать Repository паттерн:
// Entity без наследования
public class Product {
public ObjectId id;
public String name;
public Double price;
public int stock;
}
// Repository
@ApplicationScoped
public class ProductRepository implements PanacheMongoRepository<Product> {
// Автоматически унаследованы методы: persist, find, delete и т.д.
// Кастомные методы
public List<Product> findByPriceRange(double minPrice, double maxPrice) {
return find("price between ?1 and ?2", minPrice, maxPrice).list();
}
public List<Product> findInStock() {
return find("stock > 0").list();
}
public void reduceStock(ObjectId id, int quantity) {
Product product = findById(id);
if (product != null && product.stock >= quantity) {
product.stock -= quantity;
update(product);
}
}
}
// Использование в контроллере
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Inject
ProductRepository productRepository;
@GetMapping("/in-stock")
public List<Product> getInStock() {
return productRepository.findInStock();
}
@PostMapping("/{id}/buy")
public ResponseEntity<Void> buyProduct(
@PathVariable String id,
@RequestParam int quantity) {
productRepository.reduceStock(new ObjectId(id), quantity);
return ResponseEntity.ok().build();
}
}
Индексы и аннотации
import io.quarkus.mongodb.panache.MongoEntity;
import org.bson.codecs.pojo.annotations.BsonProperty;
import io.quarkus.mongodb.panache.PanacheMongoEntity;
public class User extends PanacheMongoEntity {
public String name;
// Укажи имя поля в MongoDB
@BsonProperty("email_address")
public String email;
@BsonProperty("registration_date")
public LocalDateTime createdAt;
}
Batch операции
public class UserService {
// Массовое обновление
@Transactional
public void updateAllEmails(String newDomain) {
User.update("email = concat(split(email, '@')[0], '@' + ?1)", newDomain).execute();
}
// Удаление по условию
@Transactional
public void deleteInactiveUsers(LocalDateTime inactiveDate) {
User.delete("lastLoginDate < ?1", inactiveDate).execute();
}
// Подсчёт
public long countAdults() {
return User.count("age >= 18");
}
}
Встроенные документы
public class Address {
public String street;
public String city;
public String zipCode;
}
public class User extends PanacheMongoEntity {
public String name;
public String email;
public Address address; // Встроенный документ
public List<String> tags;
public Map<String, String> metadata;
}
// Запросы на встроенные поля
@GetMapping("/by-city/{city}")
public List<User> findByCity(@PathVariable String city) {
return User.find("address.city", city).list();
}
Преимущества Panache
- Меньше boilerplate — нет нужны в дополнительных классах
- Понятный API — интуитивные названия методов
- Интеграция с Quarkus — работает с dependency injection
- Производительность — оптимизировано для быстрого старта
- Type-safe — полная поддержка типов Java
Недостатки
- Привязка к Quarkus — не работает с Spring Boot
- Сложные запросы — для очень сложных запросов может быть недостаточно
- Меньше контроля — низкоуровневый доступ ограничен
Сравнение с другими подходами
| Подход | Простота | Контроль | Производительность |
|---|---|---|---|
| Panache | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| MongoDB Reactive Streams | ⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Spring Data MongoDB | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| Raw MongoDB Driver | ⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
Заключение
Panache — это отличный выбор для проектов на Quarkus, где нужна простота использования MongoDB без сложного boilerplate. Это идеально подходит для микросервисов, REST API и приложений, где требуется быстрое прототипирование и развёртывание. Panache делает MongoDB работой удобной и быстрой, позволяя разработчикам сосредоточиться на бизнес-логике вместо технических деталей интеграции.