Code review comment: I also like `withBounds`. Unfortunately I suppose it's too late now : ) : (

Code under review:
public PCollection<T> expand(PBegin input) {
      checkNotNull(getRowMapper(), "withRowMapper() is required");
      checkNotNull(
          getDataSourceProviderFn(),
          "withDataSourceConfiguration() or withDataSourceProviderFn() is required");
      checkNotNull(getPartitionColumn(), "withPartitionColumn() is required");
      checkNotNull(getTable(), "withTable() is required");
      if (getLowerBound() != null && getLowerBound() instanceof Comparable<?>) {
        
        
        
        checkArgument(
            ((Comparable<PartitionColumnT>) getLowerBound()).compareTo(getUpperBound()) < EQUAL,
            "The lower bound of partitioning column is larger or equal than the upper bound");
      }
      checkArgument(
          JdbcUtil.PRESET_HELPERS.containsKey(getPartitionColumnType().getRawType()),
          "readWithPartitions only supports the following types: %s",
          JdbcUtil.PRESET_HELPERS.keySet());

      PCollection<KV<Integer, KV<PartitionColumnT, PartitionColumnT>>> params;

      if (getLowerBound() == null && getUpperBound() == null) {
        String query =
            String.format(
                "SELECT min(%s), max(%s) FROM %s",
                getPartitionColumn(), getPartitionColumn(), getTable());
        if (getNumPartitions() == null) {
          query =
              String.format(
                  "SELECT min(%s), max(%s), count(*) FROM %s",
                  getPartitionColumn(), getPartitionColumn(), getTable());
        }
        params =
            input
                .apply(
                    JdbcIO.<KV<Integer, KV<PartitionColumnT, PartitionColumnT>>>read()
                        .withQuery(query)
                        .withDataSourceProviderFn(getDataSourceProviderFn())
                        .withRowMapper(
                            (JdbcReadWithPartitionsHelper<PartitionColumnT>)
                                JdbcUtil.PRESET_HELPERS.get(getPartitionColumnType().getRawType())))
                .apply(
                    MapElements.via(
                        new SimpleFunction<
                            KV<Integer, KV<PartitionColumnT, PartitionColumnT>>,
                            KV<Integer, KV<PartitionColumnT, PartitionColumnT>>>() {
                          @Override
                          public KV<Integer, KV<PartitionColumnT, PartitionColumnT>> apply(
                              KV<Integer, KV<PartitionColumnT, PartitionColumnT>> input) {
                            KV<Integer, KV<PartitionColumnT, PartitionColumnT>> result;
                            if (getNumPartitions() == null) {
                              
                              
                              
                              
                              
                              Integer numPartitions =
                                  Long.valueOf(
                                          Math.round(Math.floor(Math.sqrt(input.getKey()) / 5)))
                                      .intValue();
                              if (numPartitions == 0) {
                                numPartitions = 1;
                              }
                              result = KV.of(numPartitions, input.getValue());
                            } else {
                              result = KV.of(getNumPartitions(), input.getValue());
                            }
                            LOG.info(
                                "Inferred min: {} - max: {} - numPartitions: {}",
                                result.getValue().getKey(),
                                result.getValue().getValue(),
                                result.getKey());
                            return result;
                          }
                        }));
      } else {
        params =
            input.apply(
                Create.of(KV.of(getNumPartitions(), KV.of(getLowerBound(), getUpperBound()))));
      }

      PCollection<KV<PartitionColumnT, PartitionColumnT>> ranges =
          params
              .apply("Partitioning", ParDo.of(new PartitioningFn<>(getPartitionColumnType())))
              .apply("Reshuffle partitions", Reshuffle.viaRandomKey());

      JdbcIO.ReadAll<KV<PartitionColumnT, PartitionColumnT>, T> readAll =
          JdbcIO.<KV<PartitionColumnT, PartitionColumnT>, T>readAll()
              .withDataSourceProviderFn(getDataSourceProviderFn())
              .withQuery(
                  String.format(
                      "select * from %1$s where %2$s >= ? and %2$s < ?",
                      getTable(), getPartitionColumn()))
              .withRowMapper(getRowMapper())
              .withParameterSetter(
                  
                  
                  
                  ((JdbcReadWithPartitionsHelper<PartitionColumnT>)
                          JdbcUtil.PRESET_HELPERS.get(getPartitionColumnType().getRawType()))
                      ::setParameters)
              .withOutputParallelization(false);

      if (getCoder() != null) {
        readAll = readAll.withCoder(getCoder());
      }

      return ranges.apply("Read ranges", readAll);
    }

Line commented on: if (getLowerBound() != null && getLowerBound() instanceof Comparable<?>) {

Revised code:
public PCollection<T> expand(PBegin input) {
      checkArgument(getQuery() != null, "withQuery() is required");
      checkArgument(getRowMapper() != null, "withRowMapper() is required");
      checkArgument(
          (getDataSourceProviderFn() != null),
          "withDataSourceConfiguration() or withDataSourceProviderFn() is required");

      JdbcIO.ReadAll<Void, T> readAll =
          JdbcIO.<Void, T>readAll()
              .withDataSourceProviderFn(getDataSourceProviderFn())
              .withQuery(getQuery())
              .withRowMapper(getRowMapper())
              .withFetchSize(getFetchSize())
              .withOutputParallelization(getOutputParallelization())
              .withParameterSetter(
                  (element, preparedStatement) -> {
                    if (getStatementPreparator() != null) {
                      getStatementPreparator().setParameters(preparedStatement);
                    }
                  });

      if (getCoder() != null) {
        readAll = readAll.toBuilder().setCoder(getCoder()).build();
      }
      return input.apply(Create.of((Void) null)).apply(readAll);
    }