Skip to content
Advertisement

A way to bind Java Map to sql varchar in JDBI INSERT statement

Is there a way to bind a java Map<String, Object> to a varchar in the the JDBI @BindBean annotation.

So for example I have a class Something.class and I create a

@SqlBatch("INSERT INTO Something (name, payload) VALUES(:name, :payload)").

Now in my java class the name is of type String and payload is of type Map<String, Object> and I want in the DB table the types are varchar(...). Now I want the Map object to be inserted in the column as a JSON object, is that somehow achievable wihtout creating my own complex Binder as defined in http://jdbi.org/sql_object_api_argument_binding/ ? and other than making my payload be of type String in java.

Advertisement

Answer

Fixed my problem with creating an ArgumentFactory binder suggested in this post.

So what I needed was to create a class that just contained one field of type Map<String, Object> implemented the Arugment interface from org.skife.jdbi.v2.tweak so I ended up with the following

public class NotificationPayloadArgument implements Argument {
  private NotificationPayload payload;

  NotificationPayloadArgument(NotificationPayload payload) {
      this.payload = payload;
  }
    
  @Override
  public void apply(int i, PreparedStatement preparedStatement, StatementContext statementContext)
    throws SQLException {
      preparedStatement.setString(i, toString());
  }

  @Override
  public String toString() {
      return new JSONObject(payload).toString();
  } 

}

To make this work I of course needed to implement a Factory class which implements the org.skife.jdbi.v2.tweak.ArgumentFactory<T> interface with my newly created type, so the factory ended up as such:

public class NotificationPayloadFactory implements ArgumentFactory<NotificationPayload> {

    @Override
    public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
        return value instanceof NotificationPayload;
    }

    @Override
    public Argument build(Class<?> expectedType, NotificationPayload value, StatementContext ctx) {
        return value;
    }

}

and of course lastly also as mentioned in Does JDBI accept UUID parameters? I had to register my factory:

jdbi.registerArgumentFactory(new NotificationPayloadFactory());

I tried to do this with just a factory for a Map<String, Object> but could not make it work for that, and there was a risk for NPE.

EDIT

The reason I am Overriding the toString() in NotificationPayload is because I need the payload in json format some places where I need a String object. But else it can be removed and then just use the new JSONObject(payload).toString() in the preparedStatement.setString() where toString() is called.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement