Bu bo‘limda oddiy JpaRepositorydan yuqoriroq darajaga chiqamiz. Spring Data advanced mavzusi quyidagilarni qamrab oladi: Specifications & Criteria API, Projections, Auditing, Optimistic locking, Spring Data Envers, va QueryDSL integration.
1. Spring Data advanced nima?
Boshlang'ich darajada biz odatda shuni bilamiz:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}Bu yaxshi. Lekin real project kattalashganda oddiy findBy... yetmay qoladi.
Masalan:
Userlarni filter qilish kerak:
- name bo‘yicha
- phone bo‘yicha
- status bo‘yicha
- createdDate oralig‘i bo‘yicha
- role bo‘yicha
- city bo‘yicha
- faqat active userlar
- pagination bilanBuni faqat findByNameAndStatusAndCreatedAtBetween... qilib yozish yomonlashadi.
Shu joyda advanced Spring Data kerak bo‘ladi.
2. Oddiy derived query muammosi
List<User> findByNameAndStatusAndCityAndCreatedAtBetween(
String name,
UserStatus status,
String city,
LocalDateTime from,
LocalDateTime to
);Boshida ishlaydi. Keyin requirement o‘zgaradi:
name optional
status optional
city optional
date optional
role optionalEndi muammo:
findByName(...)
findByStatus(...)
findByCity(...)
findByNameAndStatus(...)
findByNameAndCity(...)
findByStatusAndCity(...)
findByNameAndStatusAndCity(...)Bu maintain qilib bo‘lmaydigan kodga aylanadi.
Yechimlar:
SpecificationCriteria API
QueryDSL
custom repository
JPQL dynamic query
3. Specification nima?
Specification - dynamic query yozish usuli.
Oddiy ma’noda:
Specification - query shartlarini alohida-alohida qilib yig‘ish imkonini beradi.
Masalan:
status = ACTIVE
AND city = Tashkent
AND age > 18Bularni alohida methodlarga ajratib, keraklisini qo‘shib ketish mumkin.
4. Specification ishlatish uchun repository
Entity:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String fullName;
private String phone;
@Enumerated(EnumType.STRING)
private UserStatus status;
private String city;
private LocalDateTime createdAt;
}Enum:
public enum UserStatus {
ACTIVE,
BLOCKED,
DELETED
}Repository:
public interface UserRepository extends
JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}Muhim joy:
JpaSpecificationExecutor<User>Shu interface Specification bilan query qilish imkonini beradi.
5. Oddiy Specification yozish
public class UserSpecifications {
public static Specification<User> hasStatus(UserStatus status) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get("status"), status);
}
public static Specification<User> hasCity(String city) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get("city"), city);
}
public static Specification<User> nameContains(String name) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(
criteriaBuilder.lower(root.get("fullName")),
"%" + name.toLowerCase() + "%"
);
}
}Bu yerda:
Qism | Ma’nosi |
|---|---|
| Entity jadvali, masalan |
| Query obyektining o‘zi |
| SQL shartlarini yasash uchun builder |
|
|
|
|
6. Specification ishlatish
Specification<User> spec = Specification
.where(UserSpecifications.hasStatus(UserStatus.ACTIVE))
.and(UserSpecifications.hasCity("Tashkent"));Repository:
List<User> users = userRepository.findAll(spec);Taxminiy SQL:
select *
from users
where status = 'ACTIVE'
and city = 'Tashkent';7. Optional filterlar bilan real search
Request DTO:
public record UserSearchRequest(
String name,
UserStatus status,
String city,
LocalDateTime from,
LocalDateTime to
) {
}Specification builder:
public class UserSpecifications {
public static Specification<User> filter(UserSearchRequest request) {
Specification<User> spec = Specification.where(null);
if (request.name() != null && !request.name().isBlank()) {
spec = spec.and(nameContains(request.name()));
}
if (request.status() != null) {
spec = spec.and(hasStatus(request.status()));
}
if (request.city() != null && !request.city().isBlank()) {
spec = spec.and(hasCity(request.city()));
}
if (request.from() != null) {
spec = spec.and(createdAfter(request.from()));
}
if (request.to() != null) {
spec = spec.and(createdBefore(request.to()));
}
return spec;
}
private static Specification<User> nameContains(String name) {
return (root, query, cb) ->
cb.like(
cb.lower(root.get("fullName")),
"%" + name.toLowerCase() + "%"
);
}
private static Specification<User> hasStatus(UserStatus status) {
return (root, query, cb) ->
cb.equal(root.get("status"), status);
}
private static Specification<User> hasCity(String city) {
return (root, query, cb) ->
cb.equal(root.get("city"), city);
}
private static Specification<User> createdAfter(LocalDateTime from) {
return (root, query, cb) ->
cb.greaterThanOrEqualTo(root.get("createdAt"), from);
}
private static Specification<User> createdBefore(LocalDateTime to) {
return (root, query, cb) ->
cb.lessThanOrEqualTo(root.get("createdAt"), to);
}
}Service:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional(readOnly = true)
public Page<User> search(UserSearchRequest request, Pageable pageable) {
Specification<User> spec = UserSpecifications.filter(request);
return userRepository.findAll(spec, pageable);
}
}Controller:
@GetMapping("/users")
public Page<User> search(
UserSearchRequest request,
Pageable pageable
) {
return userService.search(request, pageable);
}8. Specification + pagination + sorting
Spring Data buni tayyor beradi:
Pageable pageable = PageRequest.of(
0,
20,
Sort.by(Sort.Direction.DESC, "createdAt")
);
Page<User> page = userRepository.findAll(spec, pageable);REST API’da:
GET /users?status=ACTIVE&city=Tashkent&page=0&size=20&sort=createdAt,descBu real projectlarda juda ko‘p ishlatiladi.
9. Specification qachon yaxshi?
Specification yaxshi:
Holat | Sabab |
|---|---|
Ko‘p optional filterlar bor | Queryni dynamic yig‘ish oson |
Admin panel search | Filterlar soni ko‘p bo‘ladi |
CRM/ERP sistemalar | Search murakkab bo‘ladi |
Pagination kerak |
|
Reusable filterlar kerak | Har bir shart method bo‘lib turadi |
Lekin juda murakkab report querylar uchun Specification noqulay bo‘lishi mumkin. Bunda native SQL, QueryDSL yoki custom repository yaxshiroq.
10. Criteria API nima?
Specification ichida ishlatilayotgan narsa aslida JPA Criteria API.
Criteria API - SQL/JPQLni string bilan emas, Java obyektlari bilan qurish usuli.
Masalan JPQL:
@Query("select u from User u where u.status = :status")
List<User> findActive(UserStatus status);Criteria API bilan:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root)
.where(cb.equal(root.get("status"), UserStatus.ACTIVE));
List<User> users = entityManager
.createQuery(query)
.getResultList();Criteria API juda verbose. Shu sabab ko‘pincha uni to‘g‘ridan-to‘g‘ri emas, Specification orqali ishlatamiz.
11. Projection nima?
Projection - entityning hammasini emas, faqat kerakli fieldlarni olish.
Masalan User entity katta bo‘lishi mumkin:
@Entity
public class User {
private Long id;
private String fullName;
private String phone;
private String passwordHash;
private String passportNumber;
private LocalDateTime createdAt;
}API’da esa faqat shu kerak:
{
"id": 1,
"fullName": "Ali Valiyev"
}Entityni to‘liq olib, keyin DTOga map qilish har doim ham optimal emas. Projection yordamida DBdan kerakli columnlargina olinadi.
12. Interface-based projection
Projection interface:
public interface UserShortView {
Long getId();
String getFullName();
}Repository:
public interface UserRepository extends JpaRepository<User, Long> {
List<UserShortView> findByStatus(UserStatus status);
}Ishlatish:
List<UserShortView> users =
userRepository.findByStatus(UserStatus.ACTIVE);Natija faqat kerakli fieldlarni beradi.
13. DTO projection
DTO:
public record UserShortDto(
Long id,
String fullName
) {
}Repository:
@Query("""
select new com.example.dto.UserShortDto(
u.id,
u.fullName
)
from User u
where u.status = :status
""")
List<UserShortDto> findShortUsersByStatus(UserStatus status);Bu aniqroq va API response uchun qulay.
14. Projection qachon kerak?
Projection ishlating:
Holat | Sabab |
|---|---|
List endpoint | Hammasi kerak emas |
Mobile API | Response yengil bo‘ladi |
Admin table | Faqat table columnlar kerak |
Sensitive fieldlar bor | Password/passport chiqib ketmaydi |
Performance kerak | Kamroq data olinadi |
Yomon amaliyot:
@GetMapping("/users")
public List<User> getUsers() {
return userRepository.findAll();
}Nega yomon?
password hash chiqib ketishi mumkin;
entity relationlar JSONda loop bo‘lishi mumkin;
lazy loading muammosi chiqadi;
response og‘irlashadi;
API entityga bog‘lanib qoladi.
Yaxshi:
@GetMapping("/users")
public Page<UserShortDto> getUsers(Pageable pageable) {
return userService.getUsers(pageable);
}15. Dynamic projection
Spring Data’da method parametr orqali projection turini tanlash ham mumkin:
public interface UserRepository extends JpaRepository<User, Long> {
<T> List<T> findByStatus(UserStatus status, Class<T> type);
}Projectionlar:
public interface UserShortView {
Long getId();
String getFullName();
}public interface UserPhoneView {
Long getId();
String getPhone();
}Ishlatish:
List<UserShortView> shortUsers =
userRepository.findByStatus(
UserStatus.ACTIVE,
UserShortView.class
);
List<UserPhoneView> phoneUsers =
userRepository.findByStatus(
UserStatus.ACTIVE,
UserPhoneView.class
);Bu kuchli, lekin haddan tashqari ishlatilsa kod tushunarsizlashadi.
16. Auditing nima?
Auditing - entity qachon yaratilgan, qachon o‘zgargan, kim yaratgan, kim o‘zgartirganini avtomatik saqlash.
Ko‘p entitylarda bular bo‘ladi:
createdAt
updatedAt
createdBy
updatedByHar safar qo‘lda yozish yomon:
user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());Spring Data Auditing buni avtomatik qiladi.
17. Auditing yoqish
Main class yoki config:
@EnableJpaAuditing
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}Base entity:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}Entity:
@Entity
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}Endi Product save bo‘lganda createdAt, updatedAt avtomatik to‘ladi.
18. createdBy va updatedBy
Base entity:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private Long createdBy;
@LastModifiedBy
private Long updatedBy;
}AuditorAware:
@Component
public class SpringSecurityAuditorAware
implements AuditorAware<Long> {
@Override
public Optional<Long> getCurrentAuditor() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
if (authentication == null ||
!authentication.isAuthenticated()) {
return Optional.empty();
}
CustomUserDetails userDetails =
(CustomUserDetails) authentication.getPrincipal();
return Optional.of(userDetails.getId());
}
}Bu yerda current user ID security contextdan olinadi.
19. Auditing qachon foydali?
Auditing kerak:
Holat | Sabab |
|---|---|
Admin panel | Kim o‘zgartirganini ko‘rish |
CRM/ERP | Data history muhim |
Debugging | Qachon o‘zgarganini bilish |
Security | Sensitive actionlarni kuzatish |
Reporting | Created date bo‘yicha filter |
Lekin auditing to‘liq history emas. U faqat oxirgi holatni saqlaydi:
createdAt = birinchi yaratilgan vaqt
updatedAt = oxirgi o‘zgargan vaqtAgar har bir o‘zgarish tarixini saqlash kerak bo‘lsa, Envers yoki audit log kerak.
20. Optimistic locking nima?
Optimistic locking - parallel update muammolarini hal qilish usuli.
Masalan ikkita admin bitta productni ochdi:
Admin A: product price = 100 ko‘rdi
Admin B: product price = 100 ko‘rdi
Admin A: price = 120 qilib saqladi
Admin B: price = 90 qilib saqladiNatijada Admin A o‘zgarishi yo‘qolib ketishi mumkin. Bu lost update.
Optimistic locking buni oldini oladi.
21. @Version
Entityga version qo‘shamiz:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
@Version
private Long version;
}Endi Hibernate update qilganda taxminan shunday SQL yozadi:
update product
set name = ?, price = ?, version = version + 1
where id = ? and version = ?;Agar boshqa transaction oldin update qilib versionni oshirib yuborgan bo‘lsa, update 0 rowga ta’sir qiladi va exception chiqadi.
22. Optimistic locking oqimi
Product version = 1
Admin A productni oldi: version = 1
Admin B productni oldi: version = 1
Admin A save qildi:
where id = 1 and version = 1
success, version = 2
Admin B save qildi:
where id = 1 and version = 1
0 rows updated -> OptimisticLockExceptionBu to‘g‘ri xatti-harakat. Sistemada "oxirgi yozgan yutadi" emas, "conflict bor" deyiladi.
23. Optimistic lock exceptionni handle qilish
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ObjectOptimisticLockingFailureException.class)
public ResponseEntity<?> handleOptimisticLock(
ObjectOptimisticLockingFailureException ex
) {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(Map.of(
"error", "DATA_CONFLICT",
"message", "Data was changed by another user. Please reload and try again."
));
}
}HTTP status:
409 ConflictBu API uchun to‘g‘riroq.
24. Optimistic vs pessimistic locking
Lock turi | Qanday ishlaydi? | Qachon yaxshi? |
|---|---|---|
Optimistic | Version tekshiradi | Conflict kam bo‘lsa |
Pessimistic | Row lock qiladi | Conflict ko‘p bo‘lsa |
Atomic update | Direct update query | Stock/balance kabi joylarda |
Optimistic locking odatda yaxshi:
admin panel;
product edit;
user profile edit;
document edit;
kam conflictli CRUD.
Stock kamaytirish, balance update qilish kabi joylarda esa atomic update yoki pessimistic lock yaxshiroq bo‘lishi mumkin.
25. Spring Data Envers nima?
Hibernate Envers - entity o‘zgarish tarixini saqlash mexanizmi.
Auditing faqat buni beradi:
Product:
createdAt
updatedAtEnvers esa har bir o‘zgarish tarixini saqlaydi:
Product version 1: price = 100
Product version 2: price = 120
Product version 3: price = 90Bu quyidagilar uchun kerak:
audit history;
kim qachon nima o‘zgartirdi;
oldingi holatni ko‘rish;
regulatory requirement;
admin action tracking.
26. Envers dependency
Maven:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
</dependency>Ko‘pincha Hibernate Envers ham kerak bo‘ladi:
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-envers</artifactId>
</dependency>27. Entityda @Audited
@Entity
@Audited
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}Endi Envers audit table yaratadi:
product
product_aud
revinfoproduct_aud ichida productning eski holatlari saqlanadi.
28. Envers repository
public interface ProductRepository extends
JpaRepository<Product, Long>,
RevisionRepository<Product, Long, Integer> {
}Ishlatish:
Revisions<Integer, Product> revisions =
productRepository.findRevisions(productId);Oxirgi revision:
Revision<Integer, Product> lastRevision =
productRepository.findLastChangeRevision(productId)
.orElseThrow();29. Envers qachon kerak emas?
Enversni hamma entityga qo‘shish kerak emas.
Kerak bo‘lmaydi:
lookup table;
juda katta log entitylar;
tez-tez o‘zgaradigan high-volume data;
vaqtinchalik data;
cache-like table.
Sabab:
audit table kattalashadi;
write overhead oshadi;
storage ko‘payadi;
querylar murakkablashadi.
Faqat muhim entitylarga qo‘shing:
User
Role
Product
Contract
Order status
Payment setting
Tariff30. QueryDSL nima?
QueryDSL - type-safe query yozish uchun kutubxona.
Specificationda biz string ishlatdik:
root.get("fullName")Agar field nomi o‘zgarsa, compile-time error bermaydi. Runtime’da xato chiqadi.
QueryDSLda esa generated classlar ishlatiladi:
QUser user = QUser.user;
queryFactory
.selectFrom(user)
.where(user.fullName.containsIgnoreCase("ali"))
.fetch();Agar fullName field o‘chirilsa yoki nomi o‘zgarsa, compile error chiqadi. Bu katta projectlarda kuchli afzallik.
31. QueryDSL qachon yaxshi?
QueryDSL yaxshi:
Holat | Sabab |
|---|---|
Murakkab dynamic query | Readable bo‘ladi |
Compile-time safety kerak | Field nomi xatosi kamayadi |
Joinlar ko‘p | JPQL stringdan tozaroq |
Report querylar | Select/projection oson |
Katta project | Refactoring xavfsizroq |
Kamchiligi:
setup murakkabroq;
Q-class generation kerak;
jamoa o‘rganishi kerak;
oddiy CRUD uchun ortiqcha.
32. QueryDSL oddiy misol
QueryDSL generated class:
QUser user = QUser.user;Custom repository:
@Repository
public class UserQueryRepository {
private final JPAQueryFactory queryFactory;
public UserQueryRepository(EntityManager entityManager) {
this.queryFactory = new JPAQueryFactory(entityManager);
}
public List<User> search(String name, UserStatus status) {
QUser user = QUser.user;
BooleanBuilder builder = new BooleanBuilder();
if (name != null && !name.isBlank()) {
builder.and(user.fullName.containsIgnoreCase(name));
}
if (status != null) {
builder.and(user.status.eq(status));
}
return queryFactory
.selectFrom(user)
.where(builder)
.orderBy(user.createdAt.desc())
.fetch();
}
}Bu Specificationga qaraganda o‘qilishi osonroq bo‘lishi mumkin.
33. QueryDSL DTO projection
DTO:
public record UserListDto(
Long id,
String fullName,
String phone
) {
}Query:
public List<UserListDto> findUsers() {
QUser user = QUser.user;
return queryFactory
.select(Projections.constructor(
UserListDto.class,
user.id,
user.fullName,
user.phone
))
.from(user)
.where(user.status.eq(UserStatus.ACTIVE))
.fetch();
}Bu report/list endpointlar uchun qulay.
34. Specification vs QueryDSL
Mezон | Specification | QueryDSL |
|---|---|---|
Setup | Oson | Murakkabroq |
Type safety | Pastroq, string fieldlar bor | Yuqori |
Dynamic filter | Yaxshi | Juda yaxshi |
Readability | O‘rtacha | Ko‘pincha yaxshiroq |
Kichik project | Yetarli | Ortiqcha bo‘lishi mumkin |
Katta project | Ba’zan noqulay | Kuchliroq |
Amaliy tavsiya:
Oddiy admin filterlar -> Specification
Murakkab query/report/search -> QueryDSL
Juda performance-sensitive query -> native SQL yoki jOOQ35. N+1 muammo
Spring Data advanced mavzusida buni ham tushunish shart.
Entity:
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY)
private User user;
}Service:
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
System.out.println(order.getUser().getFullName());
}Muammo:
1 query: ordersni olish
N query: har bir order uchun userni olishAgar 100 ta order bo‘lsa:
1 + 100 queryBu N+1 problem.
36. Fetch join
Repository:
@Query("""
select o
from Order o
join fetch o.user
where o.status = :status
""")
List<Order> findByStatusWithUser(OrderStatus status);Bu order va userni bitta queryda olib keladi.
Taxminiy SQL:
select o.*, u.*
from orders o
join users u on u.id = o.user_id
where o.status = ?;37. EntityGraph
@EntityGraph ham relationlarni oldindan yuklash uchun ishlatiladi.
@EntityGraph(attributePaths = {"user", "items"})
List<Order> findByStatus(OrderStatus status);Bu method chaqirilganda user va items oldindan yuklanadi.
Lekin ehtiyot bo‘lish kerak. Ko‘p collectionlarni bir vaqtda fetch qilish katta result set va duplicate row muammolarini chiqarishi mumkin.
38. Bulk update
Oddiy JPA entity update:
@Transactional
public void blockUsers() {
List<User> users = userRepository.findByStatus(UserStatus.INACTIVE);
for (User user : users) {
user.setStatus(UserStatus.BLOCKED);
}
}Agar 100 ming user bo‘lsa, bu og‘ir.
Bulk update:
@Modifying
@Query("""
update User u
set u.status = :newStatus
where u.status = :oldStatus
""")
int updateStatus(
UserStatus oldStatus,
UserStatus newStatus
);Service:
@Transactional
public int blockInactiveUsers() {
return userRepository.updateStatus(
UserStatus.INACTIVE,
UserStatus.BLOCKED
);
}Muhim: bulk update persistence contextni avtomatik yangilamasligi mumkin. Shu sabab bulk operationdan keyin entity manager clear kerak bo‘lishi mumkin.
@Modifying(clearAutomatically = true, flushAutomatically = true)39. @Modifying
@Query default holatda select query deb qaraladi.
Update/delete uchun:
@Modifying
@Query("delete from User u where u.status = :status")
int deleteByStatus(UserStatus status);Va service method transaction ichida bo‘lishi kerak:
@Transactional
public int deleteBlockedUsers() {
return userRepository.deleteByStatus(UserStatus.BLOCKED);
}40. Advanced Spring Data best practice
1. Entityni API response sifatida qaytarmang
Yomon:
return userRepository.findAll();Yaxshi:
return userRepository.findAllProjectedBy();yoki DTO mapping.
2. Optional filterlarda Specification ishlating
Yomon:
findByNameAndStatusAndCityAndCreatedAtBetween(...)Yaxshi:
userRepository.findAll(UserSpecifications.filter(request), pageable);3. List endpointlarda projection ishlating
Entity katta bo‘lsa, faqat kerakli fieldlarni oling.
4. Audit fieldlarni qo‘lda to‘ldirmang
@CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy ishlating.
5. Parallel update uchun @Version qo‘shing
Muhim entitylarda lost update oldini oladi.
6. N+1 muammosini testda tekshiring
Ayniqsa:
order.getUser()
order.getItems()
user.getRoles()kabi relationlarda.
7. Bulk operationlarda persistence contextga ehtiyot bo‘ling
Bulk query DBni o‘zgartiradi, lekin memorydagi entitylar eski holatda qolishi mumkin.
41. Real project uchun pattern
Masalan admin panelda user search endpoint:
GET /admin/users?name=ali&status=ACTIVE&city=Tashkent&page=0&size=20Yaxshi arxitektura:
Controller
↓
UserSearchRequest
↓
Service
↓
Specification
↓
Repository
↓
Page<UserListDto>Service:
@Transactional(readOnly = true)
public Page<UserListDto> search(UserSearchRequest request, Pageable pageable) {
Specification<User> spec = UserSpecifications.filter(request);
return userRepository.findAll(spec, pageable)
.map(userMapper::toListDto);
}Bu clean va maintainable.
Agar performance kerak bo‘lsa, projectionni repository darajasida qilish mumkin.
42. Interview savollar
Savol 1: Specification nima?
Javob:
Specification - Spring Data JPA’da dynamic query shartlarini composable qilib yozish usuli. U Criteria API ustiga qurilgan va optional filterlar ko‘p bo‘lgan search endpointlarda foydali.
Savol 2: Projection nima uchun kerak?
Javob:
Projection entityning hammasini emas, faqat kerakli fieldlarni olish uchun ishlatiladi. Bu response hajmini kamaytiradi, sensitive fieldlar chiqib ketishining oldini oladi va performancega yordam beradi.
Savol 3: Auditing nima?
Javob:
Auditing entity yaratilgan va o‘zgartirilgan vaqtni, shuningdek kim yaratgani yoki o‘zgartirganini avtomatik saqlash mexanizmi. Spring Data’da
@CreatedDate,@LastModifiedDate,@CreatedBy,@LastModifiedByishlatiladi.
Savol 4: Optimistic locking qanday ishlaydi?
Javob:
Entityga
@Versionfield qo‘shiladi. Update paytida Hibernate eski versionni tekshiradi. Agar boshqa transaction oldin update qilgan bo‘lsa, version mos kelmaydi va optimistic lock exception chiqadi.
Savol 5: Envers nima?
Javob:
Hibernate Envers entity o‘zgarish tarixini saqlaydi. Oddiy auditing oxirgi holatni saqlasa, Envers har bir revisionni alohida audit tablelarda saqlaydi.
Savol 6: QueryDSL Specificationdan nimasi bilan farq qiladi?
Javob:
Specification Criteria API asosida ishlaydi va ba’zan string field nomlariga tayanadi. QueryDSL esa generated Q-classlar orqali type-safe query yozadi, shuning uchun refactoringda xatolar compile time’da chiqadi.
Savol 7: N+1 muammo nima?
Javob:
N+1 - avval asosiy entitylar bitta query bilan olinib, keyin har bir entity relationi uchun alohida query ketishi. Masalan 100 order va har birining userini olish 1 + 100 queryga aylanadi. Fetch join yoki EntityGraph bilan hal qilinadi.
43. Qisqa xulosa
Spring Data advanced - katta projectlarda data access qatlamini professional boshqarish uchun kerak.
Mavzu | Qachon kerak |
|---|---|
Specification | Optional filterlar ko‘p bo‘lsa |
Criteria API | Dynamic queryni pastroq darajada boshqarish kerak bo‘lsa |
Projection | Faqat kerakli fieldlarni olish uchun |
Auditing | createdAt/updatedAt/createdBy/updatedBy uchun |
Optimistic locking | Parallel update conflictlarini oldini olish uchun |
Envers | Entity history kerak bo‘lsa |
QueryDSL | Type-safe va murakkab querylar uchun |
Fetch join / EntityGraph | N+1 muammosini hal qilish uchun |
Bulk update | Katta hajmdagi update/delete uchun |
Eng muhim fikr:
Spring Data advanced - querylarni ko‘paytirish emas, balki data accessni nazoratli, xavfsiz va performancega mos qilishdir.