gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 07:58:57 24 4
gpt4 key购买 nike

我正在尝试将旧版 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com