Wstęp
Przydatne klasy:
Code Block |
---|
language | java |
---|
title | Pobranie przydatnych obiektów |
---|
linenumbers | true |
---|
|
SQLFinder finder = FinderFactory.getSQLFinder();//udostępnia metody wyszukujące
|
Podstawowe możliwości
Code Block |
---|
language | java |
---|
title | Proste zapytanie SELECT |
---|
linenumbers | true |
---|
|
//proste zapytanie
SQLBuilder builder =new SQLBuilder();
builder.setQuery( "select * from testtable" );
builder.addScalar( "boolcol", StandardBasicTypes.BOOLEAN );
builder.addScalar( "textcol", StandardBasicTypes.STRING );
builder.addScalar( "datecol", StandardBasicTypes.DATE );
builder.addScalar( "intcol", StandardBasicTypes.INTEGER );
builder.addScalar( "doublecol", StandardBasicTypes.DOUBLE );
List<Map<String,Object>> data=finder.find( builder );//wykonanie zapytania |
W powyższym przykładzie w wyniku otrzymamy wartości tylko kolumn, dla których zdefiniowaliśmy scalar'y nawet jeżeli tabela testtable posiada więcej kolumn. Rekord jest reprezentowany jako mapa, w której kluczem jest nazwa kolumn(lub alias jeżeli został zdefiniowany). Podanie scalar'ów jest opcjonalne, lecz zalecane.
Info |
---|
title | Dlaczego należy używać scalar'ów |
---|
|
Scalar'y definiują do jakiego typu języka Java należy przekonwertować wartości z bazy danych. Jeżeli nie podamy scalar'ów system spróbuje sam wybrać odpowiedni typ. Jednak z uwagi na dużą różnorodność typów bazodanowych(w zależności od producenta SZBD) system nie zawsze jest w stanie dopasować typ bazodanowy do typu Java. W takim wypadku zostaje rzucony wyjątek. Nawet jeżeli system pomyślnie znajdzie odpowiedni typ to nie wiemy jaki konkretnie jest to typ. Np. jeżeli zdefiniujemy zapytanie z użyciem count( * ), lub z kolumną numeryczną i spróbujemy rzutować wartość na Long to możemy dostać wyjątek, ponieważ system dopasuje typ BigInteger, BigDecimal lub jeszcze inny w zależności od silnika bazy danych. |
Code Block |
---|
language | java |
---|
title | Podanie parametrów do zapytania |
---|
linenumbers | true |
---|
|
builder.setQuery( "select * from testtable where textcol=:textcol" );
//... definicja scalarów
builder.setParameter( "textcol", "text3" );//ustawienie pojedynczego parametru
Map<String,Object> parameters=new HashMap<String,Object>();//alternatywnie możemy podać mapę ze wszystkimi parametrami
parameters.put("textcol","text3");
builder.setParameters( parameters );//usunie wcześniej podane parametry i wstawi nowe |
Przy podawaniu parametrów ważne jest, aby ich typy zgadzały się z typami kolumn. Np. nie możemy podać "true" jako String, gdy kolumna jest typu boolean.
Definiowanie zwracanego typu
Dodawanie do każdego zapytania scalar'ów może być czasem uciążliwe, a odnoszenie się do wartości rekordów poprzez ich nazwę może powodować błędy wynikające np. z literówek. Dlatego innym podejściem jest utworzenie klasy, która będzie przechowywała zwracane z bazy danych informacje. Przejdźmy do przykładu:
Code Block |
---|
language | java |
---|
title | Definiowanie zwracanej klasy |
---|
linenumbers | true |
---|
|
builder.setQuery( "select textcol,boolcol as wlasnanazwa from testtable" );
List<TestDto> data = finder.find( builder, TestDto.class );//wykonanie zapytania |
Aby, przedstawiony kod zadziałał musimy oczywiście utworzyć klasę TestDto(nazwa może być inna
). Pola klasy powinny odpowiadać aliasom kolumn. W tym przypadku textcol i wlasnanazwa. Wielkość znaków nie ma znaczenia, jednak konieczne jest trzymanie się standardu JavaBean(gettery i settery). Dzięki temu otrzymujemy listę obiektów z polami uzupełnionymi przez wartości z bazy danych. Nie musimy się również przejmować typami kolumn, ponieważ zostaną automatycznie rzutowane na typy odpowiednich pól.
Filtrowanie
API umożliwia również dodatkowe funkcje tj. budowanie klauzuli where, order by, oraz ograniczanie zbioru wynikowego(offset i limit). Zacznijmy od filtrowania. Klasa SQLBuilder pozwala zbudować filtry za pomocą klas:
- SimpleSQLFilter - prosty filtr, opisuje nazwę kolumny, operator porównania i zawiera wartość filtru.
- GroupSQLFilter - pozwala grupować proste filtry, lub inne grupy filtrów. Warunki w grupie filtrów mogą być łączone operatorem logicznym OR, lub AND.
Code Block |
---|
language | java |
---|
title | Proste filtry |
---|
linenumbers | true |
---|
|
builder.setQuery( "select textcol,boolcol from testtable " );
builder.addFilter(new SimpleSQLFilter( "textcol", "text3" ));
builder.addFilter(new SimpleSQLFilter( "boolcol", true ));
List<TestDto> data = finder.find( builder, TestDto.class );//wykonanie zapytania |
W przykładzie zdefiniowaliśmy dwa proste warunki. Domyślnie operatorem porównania jest znak równości, a operatorem logicznym, który łączy warunki jest AND. Oczywiście możemy zmienić domyślne ustawienia.
Code Block |
---|
language | java |
---|
title | Operatory porównania |
---|
linenumbers | true |
---|
|
builder.setQuery( "select textcol,boolcol from testtable " );
builder.addFilter(new SimpleSQLFilter( "textcol", Arrays.asList("text3","text4"),FilterOperator.IN ));
builder.addFilter(new SimpleSQLFilter( "boolcol", true,FilterOperator.NOTEQ ));// różny od
builder.addFilter(new SimpleSQLFilter( "doublecol", 5.0,FilterOperator.GT ));// większy od
builder.addFilter(new SimpleSQLFilter( "intcol", new Integer[]{1,10},FilterOperator.BETWEEN ));//dla operator BETWEEN należy podać tablicę dwuelementową
List<TestDto> data = finder.find( builder, TestDto.class );//wykonanie zapytania |
Dostępne operatory są opisane w dokumentacji FilterOperator. Przejdźmy do grupowania filtrów:
Code Block |
---|
language | java |
---|
title | Grupowanie filtrów |
---|
linenumbers | true |
---|
|
builder.setQuery( "select textcol,boolcol from testtable " );
GroupSQLFilter g1 = new GroupSQLFilter(LogicOperator.OR );//główna grupa
GroupSQLFilter g2 = new GroupSQLFilter(LogicOperator.AND);//podgrupa 1
GroupSQLFilter g3 = new GroupSQLFilter(LogicOperator.AND);//podgrupa 2
//grupa g1 łączy g2 i g3 operatorem OR
g1.addFilter( g2 );
g1.addFilter( g3 );
//warunki pierwszej podgrupy połączone operatorem AND
g2.addFilter( new SimpleSQLFilter( "intcol", 5, FilterOperator.GT ) );
g2.addFilter( new SimpleSQLFilter( "intcol", 7, FilterOperator.LT ) );
//warunki drugiej podgrupy połączone operatorem AND
g3.addFilter( new SimpleSQLFilter( "textcol", Arrays.asList( "text3", "text4" ), FilterOperator.IN ) );
g3.addFilter( new SimpleSQLFilter( "boolcol", true, FilterOperator.NOTEQ ) );
builder.addFilter( g1 );//dodajemy stworzoną grupę filtów do zapytania
List<TestDto> data = finder.find( builder, TestDto.class );// wykonanie zapytania |
Powyższy kod wygeneruje następujące zapytanie:
select textcol,boolcol from testtable where ( ( intcol > :intcol_0 and intcol < :intcol_1 ) or ( textcol in (:textcol_2) and boolcol != :boolcol_3 ) )
Jak widzimy, możliwe jest zagnieżdżanie grup w innych grupach co pozwala na tworzenie drzewa warunków. Należy pamiętać, że gdy zdefiniujemy filtry, zapytanie, które podaliśmy poprzez setQuery ulegnie zmianie. Doklejona zostanie klauzula WHERE, wraz z warunkami. Dlatego nie możemy w setQuery podać zapytania z już istniejącą klauzulą WHERE. Jeżeli jednak pojawi się taka potrzeba możemy niezależnie zbudować fragment zapytania dotyczący warunków, w następujący sposób:
Code Block |
---|
language | java |
---|
title | Niezależne budwanie fragmentu zapytania |
---|
linenumbers | true |
---|
|
GroupSQLFilter g1 = new GroupSQLFilter( LogicOperator.OR );
//...definicja filtrów(patrz 'Grupowanie filtrów')
g2.addFilter( new SimpleSQLFilter( "intcol", 5, FilterOperator.GT ) );
g2.addFilter( new SimpleSQLFilter( "intcol", 7, FilterOperator.LT ) );
List<SQLFilter> filters=new ArrayList<SQLFilter>();
filters.add( g1 );
//builder.addFilter( g1 );//tym razem nie ustawiamy filtrów!
builder.setParameters( filters );//ale musimy podać parametry użyte w filtrach!
String where=SQLQueryBuilder.buildWhere( filters );//fragment zapytania zawierający warunki(bez słowa WHERE)
builder.setQuery( "select textcol,boolcol from testtable where "+where );
List<TestDto> data = finder.find( builder, TestDto.class );// wykonanie zapytania |
Efekt będzie dokładnie taki sam jak w przykładzie poprzednim. Należy zwrócić uwagę na parametry użyte w filtrach. Spójrzmy ponownie na zapytanie SQL wygenerowwane w przykładzie:
select textcol,boolcol from testtable where ( ( intcol > :intcol_0 and intcol < :intcol_1 ) or ( textcol in (:textcol_2) and boolcol != :boolcol_3 ) )
Parametry są poprzedzone są znakiem dwukropka. Widzimy, że nazwy parametrów różnią się od nazw podanych w definicji filtrów. Dzieje się tak dlatego, że podczas budowania zapytania parametry są numerowane. Celem numerowania jest umożliwienie definiowania wielu warunków dla jednej kolumny. Gdybyśmy nie wprowadzili numerowania zapytanie wyglądało by tak:
select textcol,boolcol from testtable where ( ( intcol > :intcol and intcol < :intcol ) or ( textcol in (:textcol) and boolcol != :boolcol ) )
Problem pojawiłby się przy kolumnie intcol, której dotyczą dwa różne warunki. Bez numeracji parametr :intcol przyjmie wartość 5(podany jako pierwszy) w dwóch miejscach co spowoduje, że nie otrzymamy żadnych wyników. Z tego powody zaleca się korzystanie z metody builder.setParameters(List<SQLFilter> filters) do ustawienia wartości filtrów.
Sortowanie
Code Block |
---|
language | java |
---|
title | Sortowanie |
---|
linenumbers | true |
---|
|
builder.addSorter( new Sorter( "intcol", SortDirection.ASC ) );
builder.addSorter( new Sorter( "textcol", SortDirection.DESC ) );
builder.setQuery( "select textcol,boolcol,intcol from testtable " );
List<TestDto> data = finder.find( builder, TestDto.class );// wykonanie zapytania
//niezależne budowanie klauzuli ORDER BY
List<Sorter> sorters=new ArrayList<Sorter>();
sorters.add( new Sorter( "intcol", SortDirection.ASC ) );
sorters.add( new Sorter( "textcol", SortDirection.DESC ) );
String order=SQLQueryBuilder.buildOrder( sorters );
//wygeneruje: intcol ASC, textcol DESC |
Paging
Code Block |
---|
language | java |
---|
title | Paging |
---|
linenumbers | true |
---|
|
CountedResult<Map<String,Object>> mapResult = finder.find( builder, 0, 5 );//
CountedResult<TestDto> classResult = finder.find( builder, TestDto.class, 0, 5 );
Long totalMap=mapResult.getTotal();
Long total=classResult.getTotal();
List<Map<String,Object>> mapData=mapResult.getData();
List<TestDto> classData=classResult.getData(); |
Przykład prezentuje sposób ograniczenia ilości zwracanych wyników. Poza parametrami oznaczającymi odpowiednio przesunięcie w zbiorze wynikowym(offset) i maksymalną ilość wyników(limit), widzimy, że zmienił się typ zwracanej wartości. Klasa CountedResult jest generyczna i służy przechowywaniu danych, wraz z ilością ogólną elementów. W tym przypadku otrzymamy maksymalnie 5 elementów, a wartość total będzie liczbą wszystkich elementów.