2010-09-09 12 views
6

Jestem w sytuacji, gdy nasza firma ma usługę wyszukiwania baz danych, która jest wysoce konfigurowalna, dla której bardzo przydatne jest konfigurowanie zapytań w sposób programistyczny. Interfejs API Criteria jest potężny, ale gdy jeden z naszych programistów odrzuca jeden z obiektów danych, ograniczenia kryteriów nie będą sygnalizować, że są uszkodzone, dopóki nie przeprowadzimy naszych testów jednostkowych, lub co gorsza, na żywo iw naszym środowisku produkcyjnym. Niedawno mieliśmy projekt związany z refaktoryzacją, który w wyniku tego problemu był zasadniczo podwójnie wydłużony w czasie pracy, a różnica w planowaniu projektu polegała na tym, że gdybyśmy wiedzieli, jak długo to potrwa, prawdopodobnie przyjęlibyśmy alternatywne podejście.Jak pozbyć się ograniczeń Kryteriów Hibernacji i Przykładowych API?

Chciałbym użyć przykładowego interfejsu API, aby rozwiązać ten problem. Kompilator Java może głośno wskazać, że nasze zapytania są zrywane, jeśli określamy warunki "where" na prawdziwych właściwościach POJO. W przykładowym interfejsie API jest jednak tylko tyle funkcji, które ogranicza na wiele sposobów. Weźmy następujący przykład

Product product = new Product(); 
product.setName("P%"); 
Example prdExample = Example.create(product); 
prdExample.excludeProperty("price"); 
prdExample.enableLike(); 
prdExample.ignoreCase(); 

Tutaj właściwość „nazwa” jest pytani przed (gdzie nazwa jak „P%”), a gdybym usunąć lub zmienić nazwę pola „Nazwa”, chcielibyśmy wiedzieć natychmiast . Ale co z nieruchomością "cena"? Jest wykluczany, ponieważ obiekt Product ma dla niego wartość domyślną, więc przekazujemy nazwę właściwości "price" do filtru wykluczającego. Teraz, jeśli "cena" zostanie usunięta, zapytanie to będzie składniowo nieprawidłowe i nie będzie wiadomo, dopóki nie zostanie uruchomione środowisko wykonawcze. KULAWY.

Innym problemem - co zrobić, jeśli dodaliśmy drugi gdzie klauzuli:

product.setPromo("Discounts up to 10%"); 

Ze względu na wezwanie do enableLike(), w tym przykładzie będzie pasował na tekst promocyjny „Rabaty do 10%”, ale także "Rabaty do 10 000 000 dolarów" lub cokolwiek innego, co pasuje. Ogólnie rzecz biorąc, modyfikacje całego obiektu Przykładowego obiektu, takie jak enableLike() lub ignoreCase(), nie zawsze będą miały zastosowanie do każdej sprawdzanej właściwości.

Oto trzeci i poważny problem - a co z innymi kryteriami specjalnymi? Nie ma sposobu, aby zdobyć każdy produkt za cenę wyższą niż 10 USD za pomocą standardowego schematu przykładowego. Nie ma możliwości zamówienia wyników przez promocję, zniżanie. Jeśli obiekt produktu dołączył do niektórych producentów, nie ma możliwości dodania kryterium do powiązanego obiektu producenta. Nie ma sposobu, aby bezpiecznie określić FetchMode na kryterium dla producenta (chociaż jest to problem z API Criteria w ogóle - nieprawidłowe pobrane relacje nie działają w sposób cichy, nawet więcej z bomby zegarowej)

Dla wszystkich powyższych Na przykład, powinieneś powrócić do API Criteria i użyć reprezentacji łańcuchowej właściwości do wykonania zapytania - znowu eliminując największą korzyść z przykładowych zapytań.

Jakie alternatywy dla przykładu interfejsu API dają nam porady na temat kompilacji, których potrzebujemy?

+0

To jest bardzo interesująca rzecz, ale stackoverflow nie jest forum dyskusyjnym, nie dostaniesz tutaj opinii, a twoje pytanie prawdopodobnie zostanie zamknięte w obecnej formie. Jak wspomniano w FAQ, * "równie dobrze jest zadawać i odpowiadać na twoje własne pytanie, o ile udajesz, że jesteś w Jeopardy: sformułuj to w formie pytania" *. Proponuję przeformułować ją i zamieścić szczegóły w odpowiedzi. –

+0

@Pascal dzięki, będę kontynuować i ponownie sformatować pytanie –

+0

Nie ma za co. I dzięki za umieszczenie tego. –

Odpowiedz

5

Moja firma daje programistom wiele dni, w których możemy eksperymentować i pracować nad projektami dla zwierząt domowych (a la Google), a ja spędziłem trochę czasu pracując nad strukturą, która pozwoli używać kwerend przykładowych, jednocześnie omijając opisane powyżej ograniczenia. Wymyśliłem coś, co może być przydatne także dla innych osób zainteresowanych przykładowymi zapytaniami. Oto przykład struktury korzystającej z przykładu produktu.

Criteria criteriaQuery = session.createCriteria(Product.class); 

Restrictions<Product> restrictions = Restrictions.create(Product.class); 
Product example = restrictions.getQueryObject(); 
example.setName(restrictions.like("N%")); 
example.setPromo("Discounts up to 10%"); 

restrictions.addRestrictions(criteriaQuery); 

Oto próba rozwiązać problemy w przykładzie kodu od pytania - problem wartości domyślnej dla pola „cena” już nie istnieje, bo to wymaga ramy Kryteria być wyraźnie określone. Drugi problem z włączeniem funkcji enableLike() zniknął - matcher znajduje się tylko w polu "nazwa".

Inne problemy wymienione w pytaniu również zniknęły w tym kontekście. Oto przykłady implementacji.

product.setPrice(restrictions.gt(10)); // price > 10 
product.setPromo(restrictions.order(false)); // order by promo desc 
Restrictions<Manufacturer> manufacturerRestrictions 
     = Restrictions.create(Manufacturer.class); 
//configure manuf restrictions in the same manner... 
product.setManufacturer(restrictions.join(manufacturerRestrictions)); 
/* there are also joinSet() and joinList() methods 
    for one-to-many relationships as well */ 

Dostępne są nawet bardziej wyrafinowane ograniczenia.

product.setPrice(restrictions.between(45,55)); 
product.setManufacturer(restrictions.fetch(FetchMode.JOIN)); 
product.setName(restrictions.or("Foo", "Bar")); 

Po pokazaniu ramy do współpracownika, wspomniał, że wiele danych odwzorowane obiekty dysponują ustawiające, czyniąc tego rodzaju kryteriów określających trudny, jak również (inny problem na przykładzie API!). Więc też to uwzględniłem. Zamiast używania ustawiających, pobierające są również możliwe do zapytania.

restrictions.is(product.getName()).eq("Foo"); 
restrictions.is(product.getPrice()).gt(10); 
restrictions.is(product.getPromo()).order(false); 

Dodałem także dodatkowe kontrole na obiektach w celu zapewnienia lepszego bezpieczeństwa typu - na przykład względnych kryteriów (gt, ge, le, lt) wszystkie wymagają wartość? rozszerza się Porównywalnie dla parametru. Ponadto, jeśli użyjesz gettera w stylu określonym powyżej, a na elemencie pobierającym znajduje się adnotacja @Transient, spowoduje to błąd runtime.

Ale poczekaj, jest więcej!

Jeśli podoba Ci się, że wbudowane narzędzie ograniczania Hibernate może być statycznie zaimportowane, możesz zrobić rzeczy takie jak kryteria.addRestriction (eq ("name", "foo")) bez konieczności pisania kodu, jest opcja również.

Restrictions<Product> restrictions = new Restrictions<Product>(){ 
     public void query(Product queryObject){ 
     queryObject.setPrice(gt(10)); 
     queryObject.setPromo(order(false)); 
     //gt() and order() inherited from Restrictions 
     }    
} 

To wszystko na teraz - z góry bardzo dziękuję za wszelkie uwagi! Opublikowaliśmy kod na Sourceforge dla zainteresowanych. http://sourceforge.net/projects/hqbe2/

+0

Podoba mi się. Wygląda na to, że zainspirowały Cię różne szydercze ramy? – waxwing

+0

@waxwing tak kocham EasyMock :) –

+0

Jak można połączyć ograniczenia z lub? Tak jak w "od osoby, której wiek> 18 lat lub (wiek> 16 lat i wniosek rodziców)"? – meriton

1

Interfejs API wygląda świetnie!

Ograniczenia.order (boolean) pachnie łączeniem sterującym. Jest trochę niejasne, co reprezentują wartości argumentu logicznego.

Proponuję zastąpienie lub uzupełnienie zamówieniemAscending() i orderDescending().

1

Spójrz na Querydsl. Ich moduł JPA/Hibernate wymaga generowania kodu. Ich moduł kolekcji Java korzysta z serwerów proxy, ale w tej chwili nie można ich używać z JPA/Hibernate.

Powiązane problemy