我有一个使用 hibernate 和 jpa 的应用程序。我需要找出它连接到哪个数据库,以便我基于数据库执行一些本机 sql 查询,例如。甲骨文和postgres。如果我使用的是纯 jdbc,那么直接获取数据库元数据但不知道如何从实体管理器获取
谢谢
在 Hibernate 4 中,您可以使用该代码从实体管理器获取数据库信息:
org.hibernate.engine.spi.SessionImplementor sessionImp =
(org.hibernate.engine.spi.SessionImplementor) eManager.getDelegate();
DatabaseMetaData metadata = sessionImp.connection().getMetaData();
//do whatever you need with the metadata object...
metadata.getDatabaseProductName();
干杯
伊曼纽尔
作为一种解决方法,您可以获取EntityManagerFactory
属性以获取特定于实现的底层数据库配置。
休眠:hibernate.connection.driver_class
EclipseLink : eclipselink.target-database
此属性指定目标数据库。Oracle
在您的情况下,它可能具有PostgreSQL
相应数据库的价值。
一般的 : javax.persistence.jdbc.driver
从这些信息中,您可以获取当前连接的数据库。
EntityManagerFactory emf = entityManager.getEntityManagerFactory();
Map<String, Object> emfProperties = emf.getProperties();
String driverClass = (String)emfProperties.get(PROPERTY);
//-- For PostgreSQL, it will have value "org.postgresql.Driver"
if(driverClass.lastIndexOf("postgresql") != -1)
postGreSQL_DB = true;
注意: 您的应用程序设计不太清楚,但您的应用程序可能会连接到两个数据库。如果可能,您可以尝试EntityManager
为每个数据库单独设置,指向persistence.xml
& 中的不同持久性单元,可以相应地使用它。
如果不是这种情况并且一次只连接其中一个,那么您可以简单地通过entityManager.isOpen()
或进行验证emf.isOpen()
。
编辑 :
Connection connection = entityManager.unwrap(Connection.class);
DatabaseMetaData metaData = connection.getMetaData();
现在,您可以从中获取数据库产品名称、驱动程序等。
基于 Emmanuel 回答如何使用 Spring 和休眠:
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public void getMeta() {
org.hibernate.engine.spi.SessionImplementor sessionImp = (org.hibernate.engine.spi.SessionImplementor) em.getDelegate();
DatabaseMetaData metadata = null;
try {
metadata = sessionImp.connection().getMetaData();
ResultSet res = metadata.getColumns(null, null, "USERS", "USERNAME");
System.out.println("List of columns: ");
while (res.next()) {
System.out.println(
" " + res.getString("TABLE_SCHEM")
+ ", " + res.getString("TABLE_NAME")
+ ", " + res.getString("COLUMN_NAME")
+ ", " + res.getString("TYPE_NAME")
+ ", " + res.getInt("COLUMN_SIZE")
+ ", " + res.getInt("NULLABLE"));
}
res.close();
System.out.println();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
使用以下行获取与您的连接和数据源相关的信息
entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");
基于这个答案,这里是 Kotlin/Spring 代码,用于根据一些已知值获取枚举值databaseProductName
:
@Component
class DbDetector {
@Autowired
private lateinit var dataSource: DataSource
val dbType by lazy { detectDatabase() }
private fun detectDatabase(): DbType {
val rawName = dataSource.connection.use {
it.metaData.databaseProductName
}
return when (rawName) {
"MySQL" -> DbType.MY_SQL
"Oracle" -> DbType.ORACLE
"PostgreSQL" -> DbType.POSTGRES
"Microsoft SQL Server" -> DbType.SQL_SERVER
"HSQL Database Engine" -> DbType.HSQLDB
"H2" -> DbType.H2
else -> DbType.UNKNOWN
}
}
}
enum class DbType {
MY_SQL, ORACLE, POSTGRES, SQL_SERVER, HSQLDB, H2, UNKNOWN
}
一件让我痛苦的关键事情,我想明确指出的是使用后关闭数据库连接。Kotlin 的use
构造优雅地允许这样做:
val rawName = dataSource.connection.use {
it.metaData.databaseProductName
}
下面是我之前的错误代码,之后没有关闭连接。我已经把它划掉了,以表明你不应该使用它,因为它在第一次使用时可能不会明显失败,你可能认为没关系:
val rawName = dataSource.connection.metaData.databaseProductName
对我来说,这段代码暂时没有明显的错误。但它导致了一个微妙的连接泄漏,幸运的是,当我的连接池在测试期间达到最大值并且连接开始超时时,它才被幸运地检测到。然后,此答案指向我使用以下中的泄漏检测设置来调试超时application.properties
:
spring.datasource.hikari.leakDetectionThreshold=2000
如果其他人(比如我)想尝试获取一些 jdbc 信息并且您使用的是 hibernate 4.x,您可以这样尝试:
import java.sql.Connection;
import java.sql.SQLException;
import org.hibernate.jdbc.Work;
public class ConnectionInfo implements Work {
public String dataBaseUrl;
public String dataBaseProductName;
public String driverName;
@Override
public void execute(Connection connection) throws SQLException {
dataBaseUrl = connection.getMetaData().getURL();
dataBaseProductName = connection.getMetaData().getDatabaseProductName();
driverName = connection.getMetaData().getDriverName();
}
public String getDataBaseProductName() {
return dataBaseProductName;
}
public void setDataBaseProductName(String dataBaseProductName) {
this.dataBaseProductName = dataBaseProductName;
}
public String getDataBaseUrl() {
return dataBaseUrl;
}
public void setDataBaseUrl(String dataBaseUrl) {
this.dataBaseUrl = dataBaseUrl;
}
public String getDriverName() {
return driverName;
}
public void setDriverName(String driverName) {
this.driverName = driverName;
}
}
现在您可以像这样检索您的信息:
// -- snip
org.hibernate.ejb.EntityManagerImpl entityManagerImpl = (org.hibernate.ejb.EntityManagerImpl) genericDBAccess.getEntityManager().getDelegate();
Session session = entityManagerImpl.getSession();
connectionInfo = new ConnectionInfo();
session.doWork(connectionInfo);
// -- snap
希望有帮助!我疯狂地寻找这些信息......
如果您想从 url 获取 dba 名称
public String getDbName() throws SQLException {
SessionImplementor sessionImp = (SessionImplementor) em.getDelegate();
DatabaseMetaData metadata = sessionImp.connection().getMetaData();
String dbName="";
Pattern urlDbName = Pattern.compile("databaseName=([A-Za-z0-9\\-\\_]+)");
Matcher m = urlDbName.matcher(metadata.getURL());
while (m.find()) {
dbName = m.group(1);
}
return dbName;
}