如何从实体管理器获取数据库元数据

时间:2012-05-15 19:05:21

标签: hibernate jpa

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

由于

7 个答案:

答案 0 :(得分:6)

作为一种变通方法,您可以获取EntityManagerFactory属性以获取底层数据库配置,这是特定于实现的。

  • Hibernate hibernate.connection.driver_class

  • EclipseLink: eclipselink.target-database此属性指定目标数据库。在您的情况下,对于相应的数据库,它可能具有OraclePostgreSQL的值。

  • 一般: 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&amp;中的不同持久性单元。可以相应地使用它。

如果情况并非如此&amp;其中只有一个连接在一起,您可以通过entityManager.isOpen()emf.isOpen()进行验证。

修改:

Connection connection = entityManager.unwrap(Connection.class);  
DatabaseMetaData metaData = connection.getMetaData();

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

答案 1 :(得分:5)

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

干杯

灵光

答案 2 :(得分:2)

基于Emmanuel回答如何使用Spring和hibernate做到这一点:

var q = Stores.Where(s => s.Features.Intersect<Feature>(DesiredFeatures));

答案 3 :(得分: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

希望有所帮助!我疯狂地发现了这些信息......

答案 4 :(得分:0)

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

entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");

答案 5 :(得分:0)

如果您想从网址中获取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;   
}

答案 6 :(得分:0)

基于 this answer,这里是 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

对我来说,这段代码有一段时间没有明显的错误。但它导致了一个微妙的连接泄漏,只有在我的连接池在测试期间达到最大值并且连接开始超时时才幸运地检测到。 This answer 然后指出我使用 application.properties 中的泄漏检测设置调试超时:

spring.datasource.hikari.leakDetectionThreshold=2000