PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Thursday, May 19, 2022

[FIXED] How to make Hibernate use setFixedCHAR instead of setString

 May 19, 2022     hibernate, parameterbinding, spring-data-jpa, string     No comments   

Issue

Can I somehow modify the way Hibernate binds parameters to the query?

For example, I want hibernate to use OracleResultSet.setFixedChar() when executing on an string column, instead of rs.setString() when executing a JPA query via Spring data.

This is how I would do it without Hibernate:

try(PreparedStatement ps = con.executeQuery("...")) {
   if(ps.isWrapped(OraclePreparedStatement.class) {
      ps.unwrap(OraclePreparedStatement.class).setFixedCHAR(0, myStringField);
   } else {
      ps.setString(0, myStringField);
   }

   try(ResultSet rs = ps.getResultSet()) {
      while(rs.next()) {
         ... do stuff ...
      }
   }
}

Repository method (Spring data JPA):

List<Object> findByMyStringField(String myStringField);

How can I influence how Hibernate binds my variable. With the above example setString is used always.

As background: the problem is that all our Legacy DB's use CHAR columns and not VARCHAR2, so we have to deal with whitespace and setFixedCHAR should do exactly what we would want.


Solution

Found an solution by implementing a SqlTypeDescriptor & Custom Dialect:

  @Autowired
  private DataSource source;

  @Bean
  public HibernateJpaVendorAdapter getHibernateJPAVendorAdapter() {
    return new CustomHibernateJpaVendorAdaptor();
  }

  private static class CustomHibernateJpaVendorAdaptor extends HibernateJpaVendorAdapter {

    @Override
    protected Class<?> determineDatabaseDialectClass(Database database) {
      // if HSQL is copied from Spring Sourcecode to keep everything the same
      if (Database.HSQL.equals(database)) {
        return CustomHsqlDialect.class;
      }
      try {
        if (source.isWrapperFor(OracleDataSource.class)) {
          return CustomOracleDialect.class;
        }
      } catch (SQLException e) {
      }
      return super.determineDatabaseDialectClass(database);
    }

    private class CustomHsqlDialect extends HSQLDialect {

      public CustomHsqlDialect() {
        registerColumnType(Types.BOOLEAN, "boolean");
        registerHibernateType(Types.BOOLEAN, "boolean");
      }
    }
  }

  @NoArgsConstructor
  public static class CustomOracleDialect extends Oracle12cDialect {

    private static final OracleCharFix INSTANCE = new OracleCharFix();


    @Override
    protected SqlTypeDescriptor getSqlTypeDescriptorOverride(final int sqlCode) {
      switch (sqlCode) {
        case Types.VARCHAR:
          return INSTANCE;
        default:
          return super.getSqlTypeDescriptorOverride(sqlCode);
      }
    }
  }

  @Slf4j
  private static class OracleCharFix extends CharTypeDescriptor {

    @Override
    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
      return new BasicBinder<>(javaTypeDescriptor, this) {
        @Override
        protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
          throws SQLException {
          if (st.isWrapperFor(OraclePreparedStatement.class)) {
            OraclePreparedStatement ops = st.unwrap(OraclePreparedStatement.class);
            if (ops.getParameterMetaData().getParameterType(index) == Types.CHAR) {
              ops.setFixedCHAR(index, javaTypeDescriptor.unwrap(value, String.class, options));
            } else {
              st.setString(index, javaTypeDescriptor.unwrap(value, String.class, options));
            }
          } else {
            st.setString(index, javaTypeDescriptor.unwrap(value, String.class, options));
          }
        }

        @Override
        protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
          throws SQLException {
            //Is nolonger used by Hibernate in the current Version
            st.setString(name, javaTypeDescriptor.unwrap(value, String.class, options));
        }

        private boolean checkIfCHARByName(ResultSetMetaData metadata, String name)
          throws SQLException {
          for (int i = 1; i <= metadata.getColumnCount(); i++) {
            if (metadata.getColumnType(i) == Types.CHAR && Objects.equals(metadata.getColumnName(i), name)) {
              return true;
            }
          }
          return false;
        }
      };
    }


Answered By - Chris
Answer Checked By - Mildred Charles (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing