【问题标题】:How to get database metadata from entity manager如何从实体管理器获取数据库元数据
【发布时间】:2012-05-23 08:18:27
【问题描述】:

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

谢谢

【问题讨论】:

    标签: hibernate jpa


    【解决方案1】:

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

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

    【讨论】:

    • 感谢 Nayan 提供的有用提示。当我尝试从 em 中获取 emf 时,我没有看到它们,所以我想出了这个代码 DatabaseMetaData dbmd = ((Session)entityManager.getDelegate()).connection().getMetaData(); dbName = dbmd.getDatabaseProductName();
    • 我不知道这段代码是否可以移植到 diff 服务器和正确的做法,因为 hibernate 将从 4.x 版本中删除 connection() 方法。但现在这对我有用。
    • @user509755 很高兴您发现它有帮助。正如您在问题中提到的那样,我与实体经理一起尝试过。此外,当您尝试使用实体管理器时发生了什么。您可以将其发布为答案,可能会对某人有所帮助。
    • 我没有在我的 entitymanager 上看到名为 getEntityManagerFactory() 的方法。这就是我必须找到之前提到的替代选项的原因。
    • @user509755 在Java EE 6中,可以参考docs.oracle.com/javaee/6/api/javax/persistence/…
    【解决方案2】:

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

    干杯

    伊曼纽尔

    【讨论】:

      【解决方案3】:

      基于 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();
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          基于this answer,这里是根据databaseProductName 的一些已知值获取枚举值的 Kotlin/Spring 代码:

          @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
          

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              【解决方案7】:

              如果您想从 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;   
              }
              

              【讨论】:

                猜你喜欢
                • 2014-01-09
                • 1970-01-01
                • 2013-12-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-07-08
                • 1970-01-01
                相关资源
                最近更新 更多