javasqljdbi

A way to bind Java Map<String, Object> 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.


Solution

  • 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.