简体   繁体   中英

How to use the IN operator in conjunction with the JDBI?

Good afternoon.

I'm trying to bind a list with strings to the query that the IN operator uses. im use Oracle. I did following the example that was described by the link: How to use IN operator with JDBI?

List<String> ms = new ArrayList();
    ms.add("Novosibirsk");
    ms.add("Perm");

public interface CityDAO {
   @RegisterMapper(CitiesMapper.class)
   @SqlQuery("SELECT *
              FROM Universities
              WHERE Location IN (:cities)")
   List<cities> getItems(@Bind("cities") List<String> cities);}
 }

I created a ListArgumentFactory

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);
        }
    };
  }
 }

I registered the factory

   public class DBI extends AbstractModule {
     private DBI dbi;

     @Override
     protected void configure() {
       this.dbi = new DBI(provideConfig().url());
       this.dbi.registerArgumentFactory(new ListArgumentFactory());
    }
  }

But when I make a request I get an exception

org.skife.jdbi.v2.exceptions.UnableToCreateStatementException: Exception while binding 'cities' [statement:"SELECT * FROM Universities WHERE Location IN (:cities)", arguments:{ positional:{}, named {cities:factory.ListArgumentFactory$1@6788168c}, finder:[]}]

Help me figure out what I'm doing wrong

According to the JDBI documentation , achieving something like that using Oracle could be quite complex so might be a better idea to use the first approach described (UseStringTemplate3StatementLocator):

Oracle supports something similar, but you need to use Oracle specific APIs and oracle.sql.ARRAY instances. In the Oracle case you have to pre-declare the array type in the database first, and as it stores the array in the database, free it after the call.

Having said that, there is a simple approach that can be used to make this work in Oracle which is to join the elements in the list with a comma. I have modified the ListArgumentFactory using the Java 8 String.join method:

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 {
                statement.setString(position, String.join(",", value));
            }
        };
    }
}

I have tried the approach described in the JDBI documentation to use oracle.sql.ARRAY and a custom TYPE in the Oracle DB but was not successful for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM