简体   繁体   English

如何让 Hibernate 使用 setFixedCHAR 而不是 setString

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

Can I somehow modify the way Hibernate binds parameters to the query?我可以以某种方式修改 Hibernate 将参数绑定到查询的方式吗?

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.例如,我想冬眠使用OracleResultSet.setFixedChar()而不是在一个字符串列执行时, rs.setString()执行通过Spring数据的JPA查询时。

This is how I would do it without Hibernate:如果没有 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):存储库方法(Spring 数据 JPA):

List<Object> findByMyStringField(String myStringField);

How can I influence how Hibernate binds my variable.我如何影响 Hibernate 如何绑定我的变量。 With the above example setString is used always.在上面的示例中,始终使用setString

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.作为背景:问题是我们所有的旧数据库都使用CHAR列而不是VARCHAR2 ,所以我们必须处理空格,而setFixedCHAR应该完全符合我们的要求。

Found an solution by implementing a SqlTypeDescriptor & Custom Dialect:通过实现 SqlTypeDescriptor 和自定义方言找到了一个解决方案:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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