Reading Data with JdbcTemplate | خواندن داده با JdbcTemplate
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
@Repository
یک Stereotype Annotation در فریمورک اسپرینگ که کلاسهای لایه دسترسی به داده (Data Access Layer) را مشخص میکند؛ این انوتیشن علاوه بر شناسایی خودکار کلاس در فرآیند Component Scanning، خطاهای پایگاه داده را به صورت خودکار به سلسلهمراتب DataAccessException تبدیل مینماید.
- Constructor Injection
رویکرد استاندارد و توصیهشده جهت تزریق وابستگیها در اسپرینگ؛ چنانچه یک کلاس تنها دارای یک Constructor باشد، اسپرینگ به صورت خودکار و بدون نیاز به نوشتن صریح انوتیشن @Autowired، وابستگیهای ورودی سازنده را تزریق میکند.
JdbcTemplate.query()
متدی در کلاس JdbcTemplate جهت اجرای کوئریهای خواندن از پایگاه داده که دستور SQL، یک پیادهسازی از RowMapper و پارامترهای اختیاری کوئری را دریافت کرده و فهرستی از اشیاء نگاشتشده را بازمیگرداند.
RowMapper
یک اینترفیس تابعی (Functional Interface) در اسپرینگ که نحوه تبدیل هر سطر از شیء ResultSet را به یک نمونه مشخص از اشیاء دامنه تعیین میکند.
- Method Reference
قابلیتی در جاوا (به فرمت ClassName::methodName) که اجازه میدهد یک متد موجود را به صورت مستقیم به عنوان پیادهسازی اینترفیس تابعی مانند RowMapper به متد query() پاس داد و کدهای ساختاری تکراری را حذف کرد.
- Parameterized Query
تکنیک ارسال پارامترها به دستور SQL با استفاده از عملگر جایگزینی ? در JdbcTemplate که علاوه بر جلوگیری از حملات SQL Injection، مدیریت انواع دادهها را به درایور پایگاه داده محول میسازد.
موارد مصاحبه ای
- تفاوت
@Repositoryبا@Componentچیست؟
هر دو انوتیشن باعث تعریف شیء به عنوان یک Bean در کانتکست اسپرینگ میشوند، اما @Repository به طور خاص برای لایه دسترسی به دادهها در نظر گرفته شده و قابلیت ترجمه خودکار خطاهای دیتابیس (Exception Translation) را فراهم میآورد.
- چرا در کلاسهایی با یک Constructor نیازی به قرار دادن انوتیشن
@Autowiredنیست؟
اگر کلاسی تنها یک سازنده داشته باشد، اسپرینگ به طور ضمنی (Implicitly) آن را به عنوان نقطه تزریق وابستگی در نظر میگیرد و نیازی به تعریف صریح @Autowired وجود ندارد.
- چگونه میتوان متد
RowMapperرا با روشهای نوین جاوا خلاصه کرد؟
به دلیل اینکه RowMapper یک اینترفیس تابعی با متد تکامضایی mapRow است، میتوان آن را با عبارات لامبدا (Lambda Expressions) یا ارجاع به متد (Method Reference) مانند this::mapRowToIngredient جایگزین نمود و از ایجاد کلاسهای بینام خودداری کرد.
- نحوه انتقال پارامترهای پویا به کوئریها در متد
query()چگونه است؟
پارامترها به صورت آرگومان متغیر (Varargs) پس از تعریف RowMapper به متد query() ارسال میشوند و بر اساس ترتیب قرارگیری، جایگزین علامتهای ? در دستور SQL خواهند شد.
سناریو کاربردی
- پیادهسازی لایه دسترسی به داده برای واکشی اقلام بر اساس شناسه و واکشی تمام دادهها
در این سناریو، اینترفیس مخزن با استفاده از JdbcTemplate پیادهسازی شده و از ارجاع به متد برای نگاشت دادههای ResultSet استفاده میشود:
@Repository
public class JdbcIngredientRepository implements IngredientRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcIngredientRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Iterable<Ingredient> findAll() {
return jdbcTemplate.query(
"select id, name, type from Ingredient",
this::mapRowToIngredient
);
}
@Override
public Optional<Ingredient> findById(String id) {
List<Ingredient> results = jdbcTemplate.query(
"select id, name, type from Ingredient where id = ?",
this::mapRowToIngredient,
id
);
return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
}
private Ingredient mapRowToIngredient(ResultSet row, int rowNum) throws SQLException {
return new Ingredient(
row.getString("id"),
row.getString("name"),
Ingredient.Type.valueOf(row.getString("type"))
);
}
}
بیشتر بدانید
- تزریق صریح وابستگی در کلاسهای چندسازندهای
چنانچه در یک کلاس بیش از یک Constructor تعریف شده باشد، کانتکست اسپرینگ نمیتواند سازنده هدف را تشخیص دهد؛ بنابراین در این شرایط استفاده از انوتیشن @Autowired بر روی سازنده اصلی الزامی است.
- استفاده از Lombok برای تزریق وابستگی تمیز
به جای تعریف دستی سازنده جهت تزریق وابستگی JdbcTemplate، میتوان از انوتیشن @RequiredArgsConstructor کتابخانه Lombok روی سطح کلاس استفاده کرد و متغیر jdbcTemplate را به صورت private final تعریف نمود تا کدهای تکراری سازنده حذف شوند.
