26

...without actually reading and parsing the persistence.xml

I can retrieve the name of the persistence unit of an EntityManager using the properties of it's factory. I can retrieve the available datasources using the jboss-as-controller-client. But I have found no API that would give me the datasource of a particular EntityManager.

A String with a name would be enough.

Thank you

I am working with Hibernate 4.0.1.Final over JPA 2 on a JBoss 7.1.1.Final.

EDIT: and I would like to avoid straying from JPA to Hibernate APIs if possible.

EDIT : Augusto's solution worked, I have some notes on details: The casting of the EM didn't work because of a ClassCastException:(org.jboss.as.jpa.container.TransactionScopedEntityManager cannot be cast to org.hibernate.ejb.EntityManagerImpl), but it worked for the retrieved factory. So I omitted step 1.

I also could not find a way to retrieve the name of the datasource from the instance. So I had to content myself with the catalog name: connectionProvider.getConnection().getCatalog();

kostja
  • 60,521
  • 48
  • 179
  • 224

10 Answers10

23

You need to:

  1. cast the EntityManager to EntityManagerImpl (the Hibernate implementation)
  2. call getFactory()
  3. cast the EntityManagerFactory to HibernateEntityManagerFactory
  4. call getSessionFactory() and cast it to SessionFactoryImpl
  5. call getConnectionProvider() and cast it to the correct implementation. You can see the implementations here. I'll assume that it's a DatasourceConnectionProvider
  6. call getDataSource() and you're done.

Unfortunately, you must use the Hibernate API, as there's no way to retrieve the DataSource using the JPA API.

Brad Cupit
  • 6,530
  • 8
  • 55
  • 60
Augusto
  • 28,839
  • 5
  • 58
  • 88
  • Thanks, Augusto, looks promising. I'm still fighting my way out of the dependency hell, trying to find out which hibernate artifacts jboss actually provides to reference them in my pom. I'll accept once I see the light – kostja Sep 14 '12 at 13:18
  • 3
    Another strike against JPA in my opinion... This process, while correct is way more complicated than necessary. – rtcarlson Jun 17 '13 at 21:28
  • 10
    getConnectionProvider() now seems to have been removed from the API. – Richard Whitehead Nov 15 '16 at 10:34
  • In my case there is `C3P0ConnectionProvider` (not `DatasourceConnectionProvider`), that dont have `getDataSource()` method. By reading source code, find this solution: `sessionFactory.getConnectionProvider().unwrap(DataSource.class)` – bobzer Jul 18 '18 at 11:09
19

In a Spring environment you can use this:

import org.springframework.orm.jpa.EntityManagerFactoryInfo;
...

@PersistenceContext
EntityManager entityManager;

public DataSource getDataSourceFromHibernateEntityManager() {
   EntityManagerFactoryInfo info = (EntityManagerFactoryInfo) entityManager.getEntityManagerFactory();
   return info.getDataSource();
}
Markus Umefjord
  • 191
  • 1
  • 2
10

I am using hibernate 5.2.10.Final and the following worked for me:

    import org.hibernate.SessionFactory;
    import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
    import javax.persistence.EntityManagerFactory;
    import javax.sql.DataSource;
    //...
    public static DataSource getDataSource(EntityManagerFactory entityManagerFactory) {
    ConnectionProvider cp = ((SessionFactory) entityManagerFactory).getSessionFactoryOptions()
            .getServiceRegistry()
            .getService(ConnectionProvider.class);
    return cp.unwrap(DataSource.class);
    }

What you need is just to pass entityManager.getEntityManagerFactory() to this method (For my case, I have multiple factories. Then I can use this method to get the datasource for any of them when needed).

Youness
  • 1,920
  • 22
  • 28
9

If you just want the name of the datasource and that datasource name was supplied per JPA means, you should be able to get that information via:

entityManager.getEntityManagerFactory().getProperties().get( "javax.persistence.jtaDataSource" );

or

entityManager.getEntityManagerFactory().getProperties().get( "javax.persistence.nonJtaDataSource" );

depending on how you defined the datasource.

Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46
  • 1
    Does not work for me :( both queries return `null`. Do you happen to have a source for those property names? – kostja Mar 28 '13 at 14:04
  • A source? Not sure what you mean. The JPA spec defines them. Is that what you mean? – Steve Ebersole Mar 28 '13 at 14:09
  • Yeah, I thought I can try some other properties. Have found the spot - §9.4.3 Persistence Unit Properties. As I have understood it, since setting these properties is not required, they may remain unset by the provider. Could that be the case for Hibernate? – kostja Mar 28 '13 at 14:20
  • Have iterated over all the properties of the EMF. The *DataSource properties are not set, `hibernate.session_factory_name` is the closest thing to what I want, but it's only the PU name. – kostja Mar 28 '13 at 14:30
  • 1
    This is something I think *should* work. If it does not I'd like to make sure it does. https://hibernate.atlassian.net/browse/HHH-8121 Sorry, I thought it was that way already. – Steve Ebersole Mar 29 '13 at 13:47
  • 4
    This property works in Hibernate (I've tried it with Hibernate 4.1): `entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");` – Brad Cupit Apr 16 '13 at 15:57
4

I needed to do this in order to run Flyway migrations. I wasn't able to retrieve the DataSource using Augusto's method, but I was able to recreate the data source by retrieving the url, username & password from the SessionFactory properties:

SessionFactory sessionFactory = ((HibernateEntityManagerFactory) entityManagerFactory).getSessionFactory();
Properties properties = ((SessionFactoryImpl) sessionFactory).getProperties();
String url = (String) properties.get("hibernate.connection.url");
String username = (String) properties.get("hibernate.connection.username");
String password = (String) properties.get("hibernate.connection.password");
shangxiao
  • 1,206
  • 11
  • 10
  • No, no. Usage of a different `DataSource` (even pointing to the same database) can lead to very subtle problems. – G. Demecki Jun 11 '15 at 10:48
3

Try this :

Session s = (Session) getEntityManager().getDelegate();
org.hibernate.SessionFactory sessionFactory=s.getSessionFactory();
ConnectionProvider cp=((SessionFactoryImpl)sessionFactory).getConnectionProvider();Connection connection=cp.getConnection();
DatabaseMetaData dbmetadata= connection.getMetaData();
String dtsource=dbmetadata.getUserName();
JDGuide
  • 6,239
  • 12
  • 46
  • 64
2

I'm using Hibernate 5.0.x

This is how I'm getting a connection from the persistence pool:

import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.jpa.internal.EntityManagerFactoryImpl;

public Connection getConnection(EntityManagerFactory emf) throws SQLException
{
    final EntityManagerFactoryImpl hibernateEmf = (EntityManagerFactoryImpl) emf;
    return hibernateEmf.getSessionFactory().getServiceRegistry().getService(ConnectionProvider.class).getConnection();
}

The emf parameter is JPA's standard javax.persistence.EntityManagerFactory, typically acquired globally using:

emf = Persistence.createEntityManagerFactory("persistence-unit-name");

or by injection:

@PersistenceUnit(unitName="persistence-unit-name")
EntityManagerFactory emf;
Zach-M
  • 2,127
  • 18
  • 12
1
DataSource dataSource = (DataSource) 
em.getEntityManagerFactory().getProperties()
            .get(org.hibernate.cfg.AvailableSettings.JPA_JTA_DATASOURCE);

You can retrieve datasoure with hibernate. Tested with Hibernate 5.3

László Tóth
  • 483
  • 5
  • 15
1

in SpringBoot environment you can use the following:

@PersistenceContext
EntityManager entityManager;

private HikariDataSource getDataSourceFromHibernateEntityManager() {
    EntityManagerFactoryInfo info = (EntityManagerFactoryInfo) entityManager.getEntityManagerFactory();
    return (HikariDataSource) info.getDataSource();
}

public String getDataSourceProperties() {
    HikariDataSource dataSource = getDataSourceFromHibernateEntityManager();
    return "DataSource properties:" +
            "URL: " + dataSource.getJdbcUrl() + "\n" +
            "Default Schema: " + dataSource.getPoolName() + "\n" +
            "Driver Class Name: " + dataSource.getDriverClassName() + "\n" +
            "Username: " + dataSource.getUsername() + "\n";
}
Lukasz Ochmanski
  • 970
  • 1
  • 11
  • 15
-1

Here's what helped me. I use HikariCP but I don't think it matters.

Basically what needs to be done is

  1. find a service registry
  2. get service by org.hibernate.engine.jdbc.connections.spi.ConnectionProvider class
  3. unwrap it to javax.sql.DataSource.

Service registry can be retrieved from EntityManager

((SessionImpl) em).getFactory().getServiceRegistry()

or from EntityManagerFactory directly

((SessionFactoryImpl) entityManagerFactory).getServiceRegistry()
Mikie Mike
  • 29
  • 2