19

我有一个使用 hibernate 和 jpa 的应用程序。我需要找出它连接到哪个数据库,以便我基于数据库执行一些本机 sql 查询,例如。甲骨文和postgres。如果我使用的是纯 jdbc,那么直接获取数据库元数据但不知道如何从实体管理器获取

谢谢

4

7 回答 7

9

在 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();

干杯

伊曼纽尔

于 2013-10-03T02:01:04.263 回答
9

作为一种解决方法,您可以获取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();

现在,您可以从中获取数据库产品名称、驱动程序等。

于 2012-05-15T20:56:38.467 回答
4

基于 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();
        }
    }
}
于 2016-03-04T00:07:22.683 回答
1

使用以下行获取与您的连接和数据源相关的信息

entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");
于 2019-11-01T07:36:47.307 回答
1

基于这个答案,这里是 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
于 2021-07-13T17:14:30.393 回答
0

如果其他人(比如我)想尝试获取一些 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

希望有帮助!我疯狂地寻找这些信息......

于 2012-05-29T09:46:45.637 回答
0

如果您想从 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;   
}
于 2020-03-17T20:58:48.677 回答