java - Hibernate 3 到 Hibernate 4 迁移 - 自定义 ConnectionProvider 实现 configure() 方法不再存在

标签 java hibernate jboss wildfly

我正在尝试将旧版 Web 应用程序从 JBoss 4.2.3GA 迁移到 Wildfly 9。

此迁移的一部分需要从 Hibernate 3.2 升级到 Hibernate 4.3.10

我对 Hibernate 的了解很少,所以如果我遗漏了一些明显的东西,请原谅我,但是我在网上找不到任何东西来解释我遇到的问题。

Web 应用程序本身具有 ConnectionProvider 接口(interface)的自定义实现。

重构前的MyConnectionProvider

package com.my_package.data;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;

import javax.naming.NamingException;
import javax.sql.DataSource;

import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.connection.ConnectionProviderFactory;
import org.hibernate.util.NamingHelper;


/**
 * An implementation of the {@link ConnectionProvider} interface. This class
 * requires the <code>hibernate.connection.datasource</code> property to be
 * set to the JNDI name of the DataSource to provide connections from, or a
 * DataSource to be injected before the {@link #configure(Properties)} method
 * is called. This supports use by the {@link Database} class and the Hibernate
 * implementation of the {@link javax.persistence.EntityManager}.
 * 
 */
public class MyConnectionProvider implements ConnectionProvider
{
   private static final Logger LOGGER = Logger.getLogger(MyConnectionProvider.class.getName());
   private static final String JNDI_NAME_KEY = "hibernate.connection.datasource";
   private static final String JNDI_NAME_KEY_STANDBY = "StandbyDatasource";

   private static final ThreadLocal<String>  USER_NAME = new ThreadLocal<String>();
   private static final ThreadLocal<String>  PASSWORD = new ThreadLocal<String>();

   private DataSource     dataSource;
   private DataSource[]   dataSources;
   private DataSource     lastMaster;
   private DataSource     standby;

   /**
    * Instantiate an instance of this class. This is called by the {@link 
    * ConnectionProviderFactory} class so must be public.
    */
   public MyConnectionProvider()
   {
      super();
   }

   /**
    * Configure the connection provider. If a DataSource has not been
    * explicitly injected by  {@link #setDataSource(DataSource)} then the JNDI
    * name for the DataSource must be provided.
    * 
    * @param props  The configuration properties. The property
    *               <code>hibernate.connection.datasource</code> should be set
    *               and point to the JNDI name of the DataSource.
    * 
    * @see org.hibernate.connection.ConnectionProvider#configure(java.util.Properties)
    */
   public void configure(Properties props) throws HibernateException
   {  
      String jndiName = props.getProperty(JNDI_NAME_KEY);
      String jndiStandbyName = props.getProperty(JNDI_NAME_KEY_STANDBY);

      /* 
       * either a JNDI name should be present or a DataSource should have been
       * injected
       */
      if (jndiName == null && this.dataSource == null)
      {
         throw new HibernateException("Datasource not set explicitly and JNDI name not specified");
      }

      /* if no DataSource was injected then use JNDI to look one up */
      if (this.dataSource == null)
      {                  
         try
         {  
            // Assume the first database is master - at least one needs to be configured             
            this.dataSource = (DataSource) NamingHelper.getInitialContext(props).lookup(
                  props.getProperty(JNDI_NAME_KEY));
            // Copy the datasource to another to keep the references throughout the
            // code the same if only using a single datasource or floating IP.
            this.lastMaster = this.dataSource;
         }
         catch (NamingException e)
         {
            throw new HibernateException("There was a problem retrieving the primary data source", e);
         }

         try
         {
            // Is there a standby datasource configured
            if (jndiStandbyName != null)
            {
               // Set up the standby data source            
               this.standby = null;            
               // Read the standby datasource which has been added to props. 
               this.standby =  (DataSource) NamingHelper.getInitialContext(props).lookup(
                     props.getProperty(JNDI_NAME_KEY_STANDBY));

               // Add both data sources
               dataSources = new DataSource[]{lastMaster,standby};
            }
         }
         catch (NamingException e)
         {
            // The standby datasource was not found - log out.
            System.err.println("Standby data source not configured");
         }

         if (this.dataSource == null)
         {
            throw new HibernateException("The primary data source wasn't found");
         }
      }
   }

   /**
    * Utility method used to determine if an SQL exception is caused by invalid user
    * credentials being provided by the client.
    * 
    * @param e The SQLException raised when attempting to open a connection.
    * @return  boolean flag, true if the exception is related to invalid login, or 
    *          false if the exception is raised for some other reason.
    */
   public static boolean isLoginException(SQLException e)
   {
      /* Switch on the error code */
      switch(e.getErrorCode())
      {
      case 1004  : /* FALLTHROUGH: ORA-01004 null password given; logon denied */
      case 1017  : /* FALLTHROUGH: ORA-01017 invalid username/password; logon denied */
      case 1040  : /* FALLTHROUGH: ORA-01040 invalid character in password; logon denied (multibyte character issue) */
      case 17443 : /* ORA-17443 null username or password not supported by thin driver */
         break;
      default :
         break;
      }
      return false;
   }

   /**
    * Get a configured connection.
    * 
    * @return              the connection to the database.
    * @throws SQLException if there was a problem retrieving the connection.
    * 
    * @see org.hibernate.connection.ConnectionProvider#getConnection()
    */
   public Connection getConnection() throws SQLException
   {
      Connection conn;
      CallableStatement stmt = null; 
      ResultSet rs = null;

      String userName = USER_NAME.get();
      String password = PASSWORD.get();

      try
      {
         /* if a user name or password are specified then retrieve a connection using these criteria */
         if (userName != null || password != null)
         {
            // Set the connection. Don't return it yet as it may be a standby
            conn = lastMaster.getConnection(userName, password);
         }
         else /* use the data source parameters to retrieve the connection */
         {
            // Set the connection. Don't return it yet as it may be a standby
            conn = lastMaster.getConnection();
         }

         // See if the connection is to the master and is ok
         // This will throw a SQL exception if the application is not running or the
         // connection is being made to the standby
         stmt = conn.prepareCall("{call oracle_package.stored_procedure_check_sys_state}");
         stmt.executeUpdate();

         // If we get here then no exceptions have been thrown so its ok return connection
         return conn;
      }
      catch(SQLException e)
      {         
         /* 
          * Determine that the exception isn't simply a user credentials issue, if it
          * is then we rethrow the exception, otherwise we see  if there is an alternate
          * connection to try.
          */
         if (!isLoginException(e) && dataSources != null && dataSources.length > 1)
         {
            /* Determine the alternate data source */
            DataSource alternate = dataSources[0] == lastMaster ? dataSources[1] : dataSources[0];

            /* Attempt to open a conenction to the alternate datasource */                        
            conn = (Connection)alternate.getConnection(userName, password);

            /* 
             * If we opened a connection then update the lastMaster instance variable,
             * If we didn't then an SQLException will have been thrown and we won't have
             * reached this next instruction.
             */
            lastMaster = alternate;
            System.err.println("Problem with datasource - switching to next datasource");
            return conn;                      
         }
         else
         {
            /* The caught exception cannot be handled here so propogate it. */
            throw e;
         }
      }
   }

   /**
    * Close a connection.
    * 
    * @param conn the connection to close.
    * 
    * @see org.hibernate.connection.ConnectionProvider#closeConnection(java.sql.Connection)
    */
   public void closeConnection(Connection conn) throws SQLException
   {
      conn.close();
   }

   /**
    * Close the connection provider.
    * 
    * @see org.hibernate.connection.ConnectionProvider#close()
    */
   public void close()
   {
      this.dataSource = null;
      this.lastMaster = null;
      this.standby = null;
   }

   /**
    * Query whether this instance supports aggressive release of database
    * connections.
    * 
    * @return  true as this implementation does support aggressive release of
    *          connections.
    * 
    * @see ConnectionProvider#supportsAggressiveRelease()
    */
   public boolean supportsAggressiveRelease()
   {
      return true;
   }

   /**
    * Inject a DataSource into this connection provider. Must be provided
    * before {@link MyConnectionProvider#configure(Properties)} is invoked
    * if the DataSource is not being provided via JNDI.
    * 
    * @param dataSource The dataSource to inject.
    */
   public void setDataSource(DataSource dataSource)
   {
      if (dataSource != null)
      {
         LOGGER.info("Using injected DataSource");
      }
      this.dataSource = dataSource;
   }

   /**
    * Set the user name to be used for connections retrieved by the current
    * thread, when {@link #getConnection()} is called.
    * 
    * @param userName   the user name to use.
    */
   public static final void setUserName(String userName)
   {
      MyConnectionProvider.USER_NAME.set(userName);
   }

   /**
    * Set the password to be used for connections retrieved by the current
    * thread, when {@link #getConnection()} is called.
    * 
    * @param password   the password to use.
    */
   public static final void setPassword(String password)
   {
      MyConnectionProvider.PASSWORD.set(password);
   }
}

我已将 jar 更新到 Hibernate 4.3.10 并重构代码以使用新的 ConnectionProvider 路径并停止使用不再存在的 NamingHelper Hibernate util 类(我不确定此重构是否正确/有效 - 任何)指针将不胜感激)。

重构后的MyConnectionProvider

package com.my_package.data;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.hibernate.HibernateException;
/*Refactored*/    import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;

/**
 * An implementation of the {@link ConnectionProvider} interface. This class
 * requires the <code>hibernate.connection.datasource</code> property to be
 * set to the JNDI name of the DataSource to provide connections from, or a
 * DataSource to be injected before the {@link #configure(Properties)} method
 * is called. This supports use by the {@link Database} class and the Hibernate
 * implementation of the {@link javax.persistence.EntityManager}.
 * 
 */
public class MyConnectionProvider implements ConnectionProvider
{
/*Refactored*/   private static final long serialVersionUID = -7542368426769408563L;

   private static final Logger LOGGER = Logger.getLogger(MyConnectionProvider.class.getName());
   private static final String JNDI_NAME_KEY = "hibernate.connection.datasource";
   private static final String JNDI_NAME_KEY_STANDBY = "StandbyDatasource";

   private static final ThreadLocal<String>  USER_NAME = new ThreadLocal<String>();
   private static final ThreadLocal<String>  PASSWORD = new ThreadLocal<String>();

   private DataSource     dataSource;
   private DataSource[]   dataSources;
   private DataSource     lastMaster;
   private DataSource     standby;

   /**
    * Instantiate an instance of this class. This is called by the {@link 
    * ConnectionProviderFactory} class so must be public.
    */
   public MyConnectionProvider()
   {
      super();
   }

   /**
    * Configure the connection provider. If a DataSource has not been
    * explicitly injected by  {@link #setDataSource(DataSource)} then the JNDI
    * name for the DataSource must be provided.
    * 
    * @param props  The configuration properties. The property
    *               <code>hibernate.connection.datasource</code> should be set
    *               and point to the JNDI name of the DataSource.
    * 
    * @see org.hibernate.connection.ConnectionProvider#configure(java.util.Properties)
    */
   public void configure(Properties props) throws HibernateException
   {  
      String jndiName = props.getProperty(JNDI_NAME_KEY);
      String jndiStandbyName = props.getProperty(JNDI_NAME_KEY_STANDBY);

      /* 
       * either a JNDI name should be present or a DataSource should have been
       * injected
       */
      if (jndiName == null && this.dataSource == null)
      {
         throw new HibernateException("Datasource not set explicitly and JNDI name not specified");
      }

      /* if no DataSource was injected then use JNDI to look one up */
      if (this.dataSource == null)
      {                  
         try
         {  
            // Assume the first database is master - at least one needs to be configured             
/*Refactored*/            Context initialContext = new InitialContext(props);

/*Refactored*/            this.dataSource = (DataSource) initialContext.lookup(props.getProperty(JNDI_NAME_KEY));
            // Copy the datasource to another to keep the references throughout the
            // code the same if only using a single datasource or floating IP.
            this.lastMaster = this.dataSource;
         }
         catch (NamingException e)
         {
            throw new HibernateException("There was a problem retrieving the primary data source", e);
         }

         try
         {
            // Is there a standby datasource configured
            if (jndiStandbyName != null)
            {
               // Set up the standby data source            
               this.standby = null;            
               // Read the standby datasource which has been added to props. 
/*Refactored*/               Context initialContext = new InitialContext(props);

/*Refactored*/               this.standby =  (DataSource) initialContext.lookup(props.getProperty(JNDI_NAME_KEY_STANDBY));

               // Add both data sources
               dataSources = new DataSource[]{lastMaster,standby};
            }
         }
         catch (NamingException e)
         {
            // The standby datasource was not found - log out.
            System.err.println("Standby data source not configured");
         }

         if (this.dataSource == null)
         {
            throw new HibernateException("The primary data source wasn't found");
         }
      }
   }

   /**
    * Utility method used to determine if an SQL exception is caused by invalid user
    * credentials being provided by the client.
    * 
    * @param e The SQLException raised when attempting to open a connection.
    * @return  boolean flag, true if the exception is related to invalid login, or 
    *          false if the exception is raised for some other reason.
    */
   public static boolean isLoginException(SQLException e)
   {
      /* Switch on the error code */
      switch(e.getErrorCode())
      {
      case 1004  : /* FALLTHROUGH: ORA-01004 null password given; logon denied */
      case 1017  : /* FALLTHROUGH: ORA-01017 invalid username/password; logon denied */
      case 1040  : /* FALLTHROUGH: ORA-01040 invalid character in password; logon denied (multibyte character issue) */
      case 17443 : /* ORA-17443 null username or password not supported by thin driver */
         break;
      default :
         break;
      }
      return false;
   }

   /**
    * Get a configured connection.
    * 
    * @return              the connection to the database.
    * @throws SQLException if there was a problem retrieving the connection.
    * 
    * @see org.hibernate.connection.ConnectionProvider#getConnection()
    */
   public Connection getConnection() throws SQLException
   {
      Connection conn;
      CallableStatement stmt = null; 
      ResultSet rs = null;

      String userName = USER_NAME.get();
      String password = PASSWORD.get();

      try
      {
         /* if a user name or password are specified then retrieve a connection using these criteria */
         if (userName != null || password != null)
         {
            // Set the connection. Don't return it yet as it may be a standby
            conn = lastMaster.getConnection(userName, password);
         }
         else /* use the data source parameters to retrieve the connection */
         {
            // Set the connection. Don't return it yet as it may be a standby
            conn = lastMaster.getConnection();
         }

         // See if the connection is to the master and is ok
         // This will throw a SQL exception if the application is not running or the
         // connection is being made to the standby
         stmt = conn.prepareCall("{call oracle_package.stored_procedure_check_sys_state}");
         stmt.executeUpdate();

         // If we get here then no exceptions have been thrown so its ok return connection
         return conn;
      }
      catch(SQLException e)
      {         
         /* 
          * Determine that the exception isn't simply a user credentials issue, if it
          * is then we rethrow the exception, otherwise we see  if there is an alternate
          * connection to try.
          */
         if (!isLoginException(e) && dataSources != null && dataSources.length > 1)
         {
            /* Determine the alternate data source */
            DataSource alternate = dataSources[0] == lastMaster ? dataSources[1] : dataSources[0];

            /* Attempt to open a conenction to the alternate datasource */                        
            conn = (Connection)alternate.getConnection(userName, password);

            /* 
             * If we opened a connection then update the lastMaster instance variable,
             * If we didn't then an SQLException will have been thrown and we won't have
             * reached this next instruction.
             */
            lastMaster = alternate;
            System.err.println("Problem with datasource - switching to next datasource");
            return conn;                      
         }
         else
         {
            /* The caught exception cannot be handled here so propogate it. */
            throw e;
         }
      }
   }

   /**
    * Close a connection.
    * 
    * @param conn the connection to close.
    * 
    * @see org.hibernate.connection.ConnectionProvider#closeConnection(java.sql.Connection)
    */
   public void closeConnection(Connection conn) throws SQLException
   {
      conn.close();
   }

   /**
    * Close the connection provider.
    * 
    * @see org.hibernate.connection.ConnectionProvider#close()
    */
   public void close()
   {
      this.dataSource = null;
      this.lastMaster = null;
      this.standby = null;
   }

   /**
    * Query whether this instance supports aggressive release of database
    * connections.
    * 
    * @return  true as this implementation does support aggressive release of
    *          connections.
    * 
    * @see ConnectionProvider#supportsAggressiveRelease()
    */
   public boolean supportsAggressiveRelease()
   {
      return true;
   }

   /**
    * Inject a DataSource into this connection provider. Must be provided
    * before {@link MyConnectionProvider#configure(Properties)} is invoked
    * if the DataSource is not being provided via JNDI.
    * 
    * @param dataSource The dataSource to inject.
    */
   public void setDataSource(DataSource dataSource)
   {
      if (dataSource != null)
      {
         LOGGER.info("Using injected DataSource");
      }
      this.dataSource = dataSource;
   }

   /**
    * Set the user name to be used for connections retrieved by the current
    * thread, when {@link #getConnection()} is called.
    * 
    * @param userName   the user name to use.
    */
   public static final void setUserName(String userName)
   {
      MyConnectionProvider.USER_NAME.set(userName);
   }

   /**
    * Set the password to be used for connections retrieved by the current
    * thread, when {@link #getConnection()} is called.
    * 
    * @param password   the password to use.
    */
   public static final void setPassword(String password)
   {
      MyConnectionProvider.PASSWORD.set(password);
   }


/*Refactored - stub methods added for isWrappableAs and unwrap*/
   @Override
    public boolean isUnwrappableAs(Class arg0) 
    {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> arg0) 
    {
        // TODO Auto-generated method stub
        return null;
    }
}

我可以编译 ConnectionProvider,但是我在运行时在 getConnection() 方法中读取的行处抛出了一个空指针异常

conn = lastMaster.getConnection(userName, password);

初始化lastMaster变量的逻辑在configure(props)方法中。然而这个方法没有被调用 - 我认为这就是问题所在。

我注意到 ConnectionProvider 接口(interface)类在 Hibernate 3 和 Hibernate 4 之间发生了显着变化 - 值得注意的是 .configure(props) 方法不再需要,所以我认为无论用于调用 .configure(props) 方法都不再需要这样做的时间更长。

我当前在配置方法中有很多逻辑,因此我假设我的自定义 ConnectionProvider 类无法正常工作是因为它不再像以前那样使用。

有人可以强调一下将自定义 ConnectionProvider 从 Hibernate 3 迁移到 Hibernate 4.3.10 所需的步骤,特别是用于在 configure() 方法中处理的逻辑,或者指出任何可以解释这一点的文档吗?

最佳答案

可能已经晚了,但我遇到了同样的问题,但在查看 <a href="https://forums.hibernate.org/viewtopic.php?p=2452561" rel="noreferrer noopener nofollow">TomcatJDBCConnectionProvider</a> 之后实现,然后我意识到你需要实现 <a href="https://docs.jboss.org/hibernate/orm/4.1/javadocs/org/hibernate/service/spi/Configurable.html" rel="noreferrer noopener nofollow">Configurable</a>除了 <a href="https://docs.jboss.org/hibernate/orm/4.1/javadocs/org/hibernate/service/jdbc/connections/spi/ConnectionProvider.html" rel="noreferrer noopener nofollow">ConnectionProvider</a> 之外的接口(interface)界面

关于java - Hibernate 3 到 Hibernate 4 迁移 - 自定义 ConnectionProvider 实现 configure() 方法不再存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32227744/

相关文章:

java - 用于比较两个 ListNode 值的比较器

java - 是否可以动态选择要在 hibernate 搜索中使用的字段

java - 如何指定 war 文件中的目录?

java - j2ee : I have a problem regarding jboss server

java - 将 Byte[] append 到二进制文件的末尾

java - 如何确定一个字符串部分包含另一个字符串? (最好是Java)

java - Hibernate native 查询 : Invalid Column Name Error SQL-17006

java - Hibernate Session的存储过程中的数据

java - 如何在 jboss7.1.1 服务器中将 jboss 时区 IST(Asia/Calcutta) 更改为 EST(America/New_York)

java - 让runnable进入 hibernate 状态