جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


یکی از عملیات معمول در API ها search و filter کردن ریسورس ها بر اساس فیلد هایشان است اما این میتواند پیچیدگی های زیادی را روی طراحی اعمال کند و همینطور بهینه نخواهد بود

بهترین روش داشتن یک زبان منعطف برای کوئری زدن روی API است 


فرض کنید یک Entity با فیلد های زیر موجود است و میخواهیم روی فیلد های آن عملیات search/ ساده ای را انجام دهیم 
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String firstName;
    private String lastName;
    private String email;
 
    private int age;
}



فیلتر کردن با استفاده از CriteriaBuilder :


در کد زیر میتوانیم ببینیم که چطور با Criteria میتوان کوئری روی ریسورس را انعطاف پذیر تر کنیم 

@Repository
public class UserDAO implements IUserDAO {
 
    @PersistenceContext
    private EntityManager entityManager;
 
    @Override
    public List<User> searchUser(List<SearchCriteria> params) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = builder.createQuery(User.class);
        Root r = query.from(User.class);
 
        Predicate predicate = builder.conjunction();
 
        UserSearchQueryCriteriaConsumer searchConsumer = 
          new UserSearchQueryCriteriaConsumer(predicate, builder, r);
        params.stream().forEach(searchConsumer);
        predicate = searchConsumer.getPredicate();
        query.where(predicate);
 
        List<User> result = entityManager.createQuery(query).getResultList();
        return result;
    }
 
    @Override
    public void save(User entity) {
        entityManager.persist(entity);
    }
}


در کد زیر میبینیم که چطور از پارامتر های ورودی و Criteria یک کوئری ساخته ایم:

public class UserSearchQueryCriteriaConsumer implements Consumer<SearchCriteria>{
 
    private Predicate predicate;
    private CriteriaBuilder builder;
    private Root r;
 
    @Override
    public void accept(SearchCriteria param) {
        if (param.getOperation().equalsIgnoreCase(">")) {
            predicate = builder.and(predicate, builder
              .greaterThanOrEqualTo(r.get(param.getKey()), param.getValue().toString()));
        } else if (param.getOperation().equalsIgnoreCase("<")) {
            predicate = builder.and(predicate, builder.lessThanOrEqualTo(
              r.get(param.getKey()), param.getValue().toString()));
        } else if (param.getOperation().equalsIgnoreCase(":")) {
            if (r.get(param.getKey()).getJavaType() == String.class) {
                predicate = builder.and(predicate, builder.like(
                  r.get(param.getKey()), "%" + param.getValue() + "%"));
            } else {
                predicate = builder.and(predicate, builder.equal(
                  r.get(param.getKey()), param.getValue()));
            }
        }
    }
 
    // standard constructor, getter, setter
}


کلاسی که پارامترهای کوئری را میتوان در آن جای داد به سادگی کلاس زیر است :

public class SearchCriteria {
    private String key;
    private String operation;
    private Object value;
}


key نام فیلد را نگه داری میکند مانند "firstName"

operation عملیات درخواستی را مشخص میکند

value مقداری که عملیات روی فیلد بر اساس آن انجام میشود



تست :


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class })
@Transactional
@TransactionConfiguration
public class JPACriteriaQueryTest {
 
    @Autowired
    private IUserDAO userApi;
 
    private User userJohn;
 
    private User userTom;
 
    @Before
    public void init() {
        userJohn = new User();
        userJohn.setFirstName("John");
        userJohn.setLastName("Doe");
        userJohn.setEmail("john@doe.com");
        userJohn.setAge(22);
        userApi.save(userJohn);
 
        userTom = new User();
        userTom.setFirstName("Tom");
        userTom.setLastName("Doe");
        userTom.setEmail("tom@doe.com");
        userTom.setAge(26);
        userApi.save(userTom);
    }
}

در کد بالا ابتدا دو ابجکت از User ساخته ایم و در مرحله بعد کد اصلی تست را مینویسیم در این تست کوئری را بر اساس User هایی که firstName با مقدار John و lastName با مقدار Doe را میخواهیم فیلتر کنیم

@Test
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
    List<SearchCriteria> params = new ArrayList<SearchCriteria>();
    params.add(new SearchCriteria("firstName", ":", "John"));
    params.add(new SearchCriteria("lastName", ":", "Doe"));
 
    List<User> results = userApi.searchUser(params);
 
    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

یک تست دیگر که کوئری بر اساس تنها lastName :

@Test
public void givenLast_whenGettingListOfUsers_thenCorrect() {
    List<SearchCriteria> params = new ArrayList<SearchCriteria>();
    params.add(new SearchCriteria("lastName", ":", "Doe"));
 
    List<User> results = userApi.searchUser(params);
    assertThat(userJohn, isIn(results));
    assertThat(userTom, isIn(results));
}

تستی دیگر که کوئری آن ریسورس های با age بزرگتر یا مساوی 25 را فیلتر میکند :

@Test
public void givenLastAndAge_whenGettingListOfUsers_thenCorrect() {
    List<SearchCriteria> params = new ArrayList<SearchCriteria>();
    params.add(new SearchCriteria("lastName", ":", "Doe"));
    params.add(new SearchCriteria("age", ">", "25"));
 
    List<User> results = userApi.searchUser(params);
 
    assertThat(userTom, isIn(results));
    assertThat(userJohn, not(isIn(results)));
}

کوئری که هیچ چیزی برابر فیلد ها مقادیر ارسال شده وجود ندارد و چیزی برنمیگرداند:

@Test
public void givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect() {
    List<SearchCriteria> params = new ArrayList<SearchCriteria>();
    params.add(new SearchCriteria("firstName", ":", "Adam"));
    params.add(new SearchCriteria("lastName", ":", "Fox"));
 
    List<User> results = userApi.searchUser(params);
    assertThat(userJohn, not(isIn(results)));
    assertThat(userTom, not(isIn(results)));
}

تست کوئری بر اساس تنها firstName :

@Test
public void givenPartialFirst_whenGettingListOfUsers_thenCorrect() {
    List<SearchCriteria> params = new ArrayList<SearchCriteria>();
    params.add(new SearchCriteria("firstName", ":", "jo"));
 
    List<User> results = userApi.searchUser(params);
 
    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}


تست واقعی بر اساس مدل واقعی که دیتابیس وجود دارد و کوئری ها را کلاینت از راه دور درخواست و توسط یک مت کنترلر دریافت و پاسخ ساخته میشود :


http://localhost:8080/users?search=lastName:doe,age>25
@Controller
public class UserController {
 
    @Autowired
    private IUserDao api;
 
    @RequestMapping(method = RequestMethod.GET, value = "/users")
    @ResponseBody
    public List<User> findAll(@RequestParam(value = "search", required = false) String search) {
        List<SearchCriteria> params = new ArrayList<SearchCriteria>();
        if (search != null) {
            Pattern pattern = Pattern.compile("(\w+?)(:|<|>)(\w+?),");
            Matcher matcher = pattern.matcher(search + ",");
            while (matcher.find()) {
                params.add(new SearchCriteria(matcher.group(1), 
                  matcher.group(2), matcher.group(3)));
            }
        }
        return api.searchUser(params);
    }
}







نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی