2015-10-20 36 views
7

Sto cercando di eseguire una query IN utilizzando JDBI di MYSQL su Dropwizard (non pertinente, presumo).Come utilizzare l'operatore IN con JDBI?

@SqlQuery("SELECT id FROM table where field in (<list>)") 
List<Integer> findSomething(@BindIn("list") List<String> someList); 

Come suggerito here, ho anche annotato la classe con

@UseStringTemplate3StatementLocator 

Ma quando sto iniziando l'applicazione, ottengo il seguente errore:

Exception in thread "main" java.lang.annotation.AnnotationFormatError: Invalid default: public abstract java.lang.Class org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator.errorListener() 

C'è qualcuno hai una buona idea su come risolvere questo problema?

risposta

5

Ci sono due modi per realizzarla.

. Utilizzando UseStringTemplate3StatementLocator

Questa annotazione si aspetta Group Files con istruzione SQL in StringTemplate

Dire che ho questo file PersonExternalizedSqlDAO

// PersonExternalizedSqlDAO.java 

package com.daoexp.dao; 

@@ExternalizedSqlViaStringTemplate3 
@RegisterMapper(PersonMapper.class) 
public interface PersonExternalizedSqlDAO { 
    @SqlQuery 
    List<Person> getPersonByNames(@BindIn("names") List<String> names); 
} 

Dal momento che stiamo usando UseStringTemplate3StatementLocator dobbiamo creare *.sql.stg file nella stessa strada di classe. Per esempio: in resources/com/daoexp/dao/PersonExternalizedSqlDAO.sql.stg

group PersonExternalizedSqlDAO; 

getPersonByNames(names) ::= << 
    select * from person where name in (<names>) 
>> 

Ora si dovrebbe essere in grado di interrogare senza problemi.


. Un altro approccio è quello di utilizzare ArgumentFactory che gestisce il tipo di dati personalizzati (in questo caso Elenco) per JDBI con @Bind. Questo è l'approccio più preferibile.

Quindi creare questa lista di argomenti fabbrica

public class ListArgumentFactory implements ArgumentFactory<List> { 
    @Override 
    public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) { 
     return value instanceof List; 
    } 

    @Override 
    public Argument build(Class<?> expectedType, final List value, StatementContext ctx) { 
     return new Argument() { 
      @Override 
      public void apply(int position, PreparedStatement statement, StatementContext ctx) throws SQLException { 
       String type = null; 
       if(value.get(0).getClass() == String.class){ 
        type = "varchar"; 
       } else if(value.get(0).getClass() == Integer.class){ 
        // For integer and so on... 
       } else { 
        // throw error.. type not handled 
       } 
       Array array = ctx.getConnection().createArrayOf(type, value.toArray()); 
       statement.setArray(position, array); 
      } 
     }; 
    } 
} 

Cosa questa classe fa?

  • accetta istanza di Lista
  • convertire l'elenco intero/stringa in array e si lega con istruzione preparata

Assicurarsi di registrare questa fabbrica discussione con l'istanza DBI.

final DBIFactory factory = new DBIFactory(); 
final DBI jdbi = factory.build(environment, configuration.getDataSourceFactory(), "h2"); 
jdbi.registerArgumentFactory(new ListArgumentFactory()); 

Ora si dovrebbe essere in grado di interrogare utilizzando List in modo più semplice (cioè) si deve utilizzare @Bind. Questo è tutto.

@RegisterMapper(PersonMapper.class) 
public interface PersonDAO { 
    @SqlQuery("select * from person where name = any(:names)") 
    List<Person> getPersonByNames(@Bind("names") List<String> names); 
} 

consultare:

Informazione supplementare:

// PersonMapper.java 
public class PersonMapper implements ResultSetMapper<Person> { 

    public Person map(int index, ResultSet r, StatementContext ctx) throws SQLException { 
     Person person = new Person(); 
     person.setId(r.getInt("id")); 
     person.setName(r.getString("name")); 

     return person; 
    } 
} 
+1

Sembra che non posso usare il metodo due a causa del mio database SQL che è MySQL. dove i tipi di array non sono supportati (createArrayOf). L'ho fatto usando il tuo primo metodo, con il file sql.stg. Grazie mille! E btw, ExternalizedSqlViaStringTemplate3 è deprecato, usa UseStringTemplate3StatementLocator. – Kenneth

+0

Ricevo un'eccezione nella soluzione 1 come causa: org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: si è verificato un errore nella sintassi SQL; controlla il manuale che corrisponde alla tua versione del server MySQL per la sintassi corretta da usare vicino a 'getPersonByNames' alla riga 1, penso che la stringa di sql non venga sostituita. – dkb

+0

Sembra che anche il metodo due non sia supportato in Oracle: 'Causato da: java.sql.SQLException: funzione non supportata su oracle.jdbc.driver.PhysicalConnection.createArrayOf (PhysicalConnection.java:9283)' –

9

Penso che si usi StringTemplate 4. È necessario utilizzare StringTemplate 3 anziché StringTemplate 4. Aggiungere questo dipendenza:

<dependency> 
    <groupId>org.antlr</groupId> 
    <artifactId>stringtemplate</artifactId> 
    <version>3.2.1</version> 
</dependency> 
+0

controllerà più tardi di oggi, ma nel codice ho incollato lo fa dire versione 3? "UseStringTemplate3StatementLocator" – Kenneth

+1

Grazie, se non ho aggiunto questa dipendenza, la mia app Dropwizard non è stata avviata con un 'AnnotationFormatError':' Default non valido: public abstract java.lang.Class org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator .errorListener() '. – Radu